wip
diff --git a/Android.mk b/Android.mk
index 282862f..dfe4d16 100644
--- a/Android.mk
+++ b/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 commands_recovery_local_path := $(LOCAL_PATH)
+# LOCAL_CPP_EXTENSION := .c
 
 LOCAL_SRC_FILES := \
     recovery.c \
@@ -9,22 +10,49 @@
     install.c \
     roots.c \
     ui.c \
+    mounts.c \
+    extendedcommands.c \
+    nandroid.c \
+    ../../system/core/toolbox/reboot.c \
+    firmware.c \
+    edifyscripting.c \
+    setprop.c \
+    default_recovery_ui.c \
     verifier.c
 
+ADDITIONAL_RECOVERY_FILES := $(shell echo $$ADDITIONAL_RECOVERY_FILES)
+LOCAL_SRC_FILES += $(ADDITIONAL_RECOVERY_FILES)
+
 LOCAL_MODULE := recovery
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
-RECOVERY_API_VERSION := 3
+ifdef I_AM_KOUSH
+RECOVERY_NAME := ClockworkMod Recovery
+LOCAL_CFLAGS += -DI_AM_KOUSH
+else
+RECOVERY_NAME := CWM-based Recovery
+endif
+
+RECOVERY_VERSION := $(RECOVERY_NAME) v5.0.2.7
+
+LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)"
+RECOVERY_API_VERSION := 2
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
 
+BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_HAS_SMALL_RECOVERY BOARD_LDPI_RECOVERY BOARD_UMS_LUNFILE BOARD_RECOVERY_ALWAYS_WIPES BOARD_RECOVERY_HANDLES_MOUNT
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
 LOCAL_STATIC_LIBRARIES :=
 
-ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
 LOCAL_CFLAGS += -DUSE_EXT4
 LOCAL_C_INCLUDES += system/extras/ext4_utils
 LOCAL_STATIC_LIBRARIES += libext4_utils libz
-endif
 
 # This binary is in the recovery ramdisk, which is otherwise a copy of root.
 # It gets copied there in config/Makefile.  LOCAL_MODULE_TAGS suppresses
@@ -33,13 +61,23 @@
 
 LOCAL_MODULE_TAGS := eng
 
-ifeq ($(TARGET_RECOVERY_UI_LIB),)
-  LOCAL_SRC_FILES += default_recovery_ui.c
+ifeq ($(BOARD_CUSTOM_RECOVERY_KEYMAPPING),)
+  LOCAL_SRC_FILES += default_recovery_keys.c
 else
-  LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
+  LOCAL_SRC_FILES += $(BOARD_CUSTOM_RECOVERY_KEYMAPPING)
 endif
+
 LOCAL_STATIC_LIBRARIES += libext4_utils libz
-LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt
+LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt
+
+LOCAL_STATIC_LIBRARIES += libedify libbusybox libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
+
+LOCAL_STATIC_LIBRARIES += libcrecovery libflashutils libmtdutils libmmcutils libbmlutils 
+
+ifeq ($(BOARD_USES_BML_OVER_MTD),true)
+LOCAL_STATIC_LIBRARIES += libbml_over_mtd
+endif
+
 LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
 LOCAL_STATIC_LIBRARIES += libstdc++ libc
 
@@ -47,6 +85,52 @@
 
 include $(BUILD_EXECUTABLE)
 
+RECOVERY_LINKS := edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot volume setprop
+
+# nc is provided by external/netcat
+RECOVERY_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS))
+$(RECOVERY_SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE)
+$(RECOVERY_SYMLINKS): $(LOCAL_INSTALLED_MODULE)
+	@echo "Symlink: $@ -> $(RECOVERY_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(RECOVERY_BINARY) $@
+
+ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_SYMLINKS)
+
+# Now let's do recovery symlinks
+BUSYBOX_LINKS := $(shell cat external/busybox/busybox-minimal.links)
+ifndef BOARD_HAS_SMALL_RECOVERY
+exclude := tune2fs
+ifeq ($(BOARD_HAS_LARGE_FILESYSTEM),true)
+exclude += mke2fs
+endif
+endif
+RECOVERY_BUSYBOX_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(filter-out $(exclude),$(notdir $(BUSYBOX_LINKS))))
+$(RECOVERY_BUSYBOX_SYMLINKS): BUSYBOX_BINARY := busybox
+$(RECOVERY_BUSYBOX_SYMLINKS): $(LOCAL_INSTALLED_MODULE)
+	@echo "Symlink: $@ -> $(BUSYBOX_BINARY)"
+	@mkdir -p $(dir $@)
+	@rm -rf $@
+	$(hide) ln -sf $(BUSYBOX_BINARY) $@
+
+ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_BUSYBOX_SYMLINKS)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := nandroid-md5.sh
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := nandroid-md5.sh
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := killrecovery.sh
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := killrecovery.sh
+include $(BUILD_PREBUILT)
 
 include $(CLEAR_VARS)
 
@@ -62,13 +146,19 @@
 
 include $(BUILD_EXECUTABLE)
 
+include $(commands_recovery_local_path)/dedupe/Android.mk
 
+include $(commands_recovery_local_path)/bmlutils/Android.mk
+include $(commands_recovery_local_path)/flashutils/Android.mk
+include $(commands_recovery_local_path)/libcrecovery/Android.mk
 include $(commands_recovery_local_path)/minui/Android.mk
 include $(commands_recovery_local_path)/minelf/Android.mk
 include $(commands_recovery_local_path)/minzip/Android.mk
 include $(commands_recovery_local_path)/mtdutils/Android.mk
+include $(commands_recovery_local_path)/mmcutils/Android.mk
 include $(commands_recovery_local_path)/tools/Android.mk
 include $(commands_recovery_local_path)/edify/Android.mk
 include $(commands_recovery_local_path)/updater/Android.mk
 include $(commands_recovery_local_path)/applypatch/Android.mk
+include $(commands_recovery_local_path)/utilities/Android.mk
 commands_recovery_local_path :=
diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk
new file mode 100644
index 0000000..6d9ab83
--- /dev/null
+++ b/bmlutils/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_STATIC_LIBRARY)
diff --git a/bmlutils/bmlutils.c b/bmlutils/bmlutils.c
new file mode 100644
index 0000000..a7b09b4
--- /dev/null
+++ b/bmlutils/bmlutils.c
@@ -0,0 +1,205 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+extern int __system(const char *command);
+#define BML_UNLOCK_ALL				0x8A29		///< unlock all partition RO -> RW
+
+#ifndef BOARD_BML_BOOT
+#define BOARD_BML_BOOT              "/dev/block/bml7"
+#endif
+
+#ifndef BOARD_BML_RECOVERY
+#define BOARD_BML_RECOVERY          "/dev/block/bml8"
+#endif
+
+static int restore_internal(const char* bml, const char* filename)
+{
+    char buf[4096];
+    int dstfd, srcfd, bytes_read, bytes_written, total_read = 0;
+    if (filename == NULL)
+        srcfd = 0;
+    else {
+        srcfd = open(filename, O_RDONLY | O_LARGEFILE);
+        if (srcfd < 0)
+            return 2;
+    }
+    dstfd = open(bml, O_RDWR | O_LARGEFILE);
+    if (dstfd < 0)
+        return 3;
+    if (ioctl(dstfd, BML_UNLOCK_ALL, 0))
+        return 4;
+    do {
+        total_read += bytes_read = read(srcfd, buf, 4096);
+        if (!bytes_read)
+            break;
+        if (bytes_read < 4096)
+            memset(&buf[bytes_read], 0, 4096 - bytes_read);
+        if (write(dstfd, buf, 4096) < 4096)
+            return 5;
+    } while(bytes_read == 4096);
+    
+    close(dstfd);
+    close(srcfd);
+    
+    return 0;
+}
+
+int cmd_bml_restore_raw_partition(const char *partition, const char *filename)
+{
+    if (strcmp(partition, "boot") != 0 && strcmp(partition, "recovery") != 0 && strcmp(partition, "recoveryonly") != 0 && partition[0] != '/')
+        return 6;
+
+    int ret = -1;
+    if (strcmp(partition, "recoveryonly") != 0) {
+        // always restore boot, regardless of whether recovery or boot is flashed.
+        // this is because boot and recovery are the same on some samsung phones.
+        // unless of course, recoveryonly is explictly chosen (bml8)
+        ret = restore_internal(BOARD_BML_BOOT, filename);
+        if (ret != 0)
+            return ret;
+    }
+
+    if (strcmp(partition, "recovery") == 0 || strcmp(partition, "recoveryonly") == 0)
+        ret = restore_internal(BOARD_BML_RECOVERY, filename);
+
+    // support explicitly provided device paths
+    if (partition[0] == '/')
+        ret = restore_internal(partition, filename);
+    return ret;
+}
+
+int cmd_bml_backup_raw_partition(const char *partition, const char *out_file)
+{
+    char* bml;
+    if (strcmp("boot", partition) == 0)
+        bml = BOARD_BML_BOOT;
+    else if (strcmp("recovery", partition) == 0)
+        bml = BOARD_BML_RECOVERY;
+    else if (partition[0] == '/') {
+        // support explicitly provided device paths
+        bml = partition;
+    }
+    else {
+        printf("Invalid partition.\n");
+        return -1;
+    }
+
+    int ch;
+    FILE *in;
+    FILE *out;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    char *in_file = bml;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    out = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR2;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    if (sz % 512)
+    {
+        while ( ( ch = fgetc ( in ) ) != EOF )
+            fputc ( ch, out );
+    }
+    else
+    {
+        for (i=0; i< (sz/512); i++)
+        {
+            if ((fread(buf, 512, 1, in)) != 1)
+                goto ERROR1;
+            if ((fwrite(buf, 512, 1, out)) != 1)
+                goto ERROR1;
+        }
+    }
+
+    fsync(out);
+    ret = 0;
+ERROR1:
+    fclose ( out );
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+}
+
+int cmd_bml_erase_raw_partition(const char *partition)
+{
+    // TODO: implement raw wipe
+    return 0;
+}
+
+int cmd_bml_erase_partition(const char *partition, const char *filesystem)
+{
+    return -1;
+}
+
+int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
+{
+    return -1;
+}
+
+int cmd_bml_get_partition_device(const char *partition, char *device)
+{
+    return -1;
+}
+
+int format_rfs_device (const char *device, const char *path) {
+    const char *fatsize = "32";
+    const char *sectorsize = "1";
+
+    if (strcmp(path, "/datadata") == 0 || strcmp(path, "/cache") == 0) {
+        fatsize = "16";
+    }
+
+    // Just in case /data sector size needs to be altered
+    else if (strcmp(path, "/data") == 0 ) {
+        sectorsize = "1";
+    } 
+
+    // dump 10KB of zeros to partition before format due to fat.format bug
+    char cmd[PATH_MAX];
+
+    sprintf(cmd, "/sbin/dd if=/dev/zero of=%s bs=4096 count=10", device);
+    if(__system(cmd)) {
+        printf("failure while zeroing rfs partition.\n");
+        return -1;
+    }
+
+    // Run fat.format
+    sprintf(cmd, "/sbin/fat.format -F %s -S 4096 -s %s %s", fatsize, sectorsize, device);
+    if(__system(cmd)) {
+        printf("failure while running fat.format\n");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/bmlutils/bmlutils.h b/bmlutils/bmlutils.h
new file mode 100644
index 0000000..e6ffeee
--- /dev/null
+++ b/bmlutils/bmlutils.h
@@ -0,0 +1,6 @@
+#ifndef BMLUTILS_H_
+#define BMLUTILS_H_
+
+int format_rfs_device (const char *device, const char *path);
+
+#endif // BMLUTILS_H_
diff --git a/bootloader.c b/bootloader.c
index baaddc5..e88160d 100644
--- a/bootloader.c
+++ b/bootloader.c
@@ -32,31 +32,31 @@
 
 int get_bootloader_message(struct bootloader_message *out) {
     Volume* v = volume_for_path("/misc");
-    if (v == NULL) {
-      LOGE("Cannot load volume /misc!\n");
-      return -1;
+    if(v)
+    {
+        if (strcmp(v->fs_type, "mtd") == 0) {
+                return get_bootloader_message_mtd(out, v);
+        } else if (strcmp(v->fs_type, "emmc") == 0) {
+                return get_bootloader_message_block(out, v);
+        }
+        LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
+        return -1;
     }
-    if (strcmp(v->fs_type, "mtd") == 0) {
-        return get_bootloader_message_mtd(out, v);
-    } else if (strcmp(v->fs_type, "emmc") == 0) {
-        return get_bootloader_message_block(out, v);
-    }
-    LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
     return -1;
 }
 
 int set_bootloader_message(const struct bootloader_message *in) {
     Volume* v = volume_for_path("/misc");
-    if (v == NULL) {
-      LOGE("Cannot load volume /misc!\n");
-      return -1;
+    if(v)
+    {
+        if (strcmp(v->fs_type, "mtd") == 0) {
+            return set_bootloader_message_mtd(in, v);
+        } else if (strcmp(v->fs_type, "emmc") == 0) {
+            return set_bootloader_message_block(in, v);
+        }
+        LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
+        return -1;
     }
-    if (strcmp(v->fs_type, "mtd") == 0) {
-        return set_bootloader_message_mtd(in, v);
-    } else if (strcmp(v->fs_type, "emmc") == 0) {
-        return set_bootloader_message_block(in, v);
-    }
-    LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
     return -1;
 }
 
@@ -200,3 +200,153 @@
     }
     return 0;
 }
+
+/* Update Image
+ *
+ * - will be stored in the "cache" partition
+ * - bad blocks will be ignored, like boot.img and recovery.img
+ * - the first block will be the image header (described below)
+ * - the size is in BYTES, inclusive of the header
+ * - offsets are in BYTES from the start of the update header
+ * - two raw bitmaps will be included, the "busy" and "fail" bitmaps
+ * - for dream, the bitmaps will be 320x480x16bpp RGB565
+ */
+ 
+#define UPDATE_MAGIC       "MSM-RADIO-UPDATE"
+#define UPDATE_MAGIC_SIZE  16
+#define UPDATE_VERSION     0x00010000
+ 
+struct update_header {
+    unsigned char MAGIC[UPDATE_MAGIC_SIZE];
+ 
+    unsigned version;
+    unsigned size;
+ 
+    unsigned image_offset;
+    unsigned image_length;
+ 
+    unsigned bitmap_width;
+    unsigned bitmap_height;
+    unsigned bitmap_bpp;
+ 
+    unsigned busy_bitmap_offset;
+    unsigned busy_bitmap_length;
+ 
+    unsigned fail_bitmap_offset;
+    unsigned fail_bitmap_length;
+};
+
+int write_update_for_bootloader(
+        const char *update, int update_length,
+        int bitmap_width, int bitmap_height, int bitmap_bpp,
+        const char *busy_bitmap, const char *fail_bitmap) {
+    if (ensure_path_unmounted("/cache")) {
+        LOGE("Can't unmount /cache\n");
+        return -1;
+    }
+ 
+    const MtdPartition *part = mtd_find_partition_by_name("cache");
+    if (part == NULL) {
+        LOGE("Can't find cache\n");
+        return -1;
+    }
+ 
+    MtdWriteContext *write = mtd_write_partition(part);
+    if (write == NULL) {
+        LOGE("Can't open cache\n(%s)\n", strerror(errno));
+        return -1;
+    }
+ 
+    /* Write an invalid (zero) header first, to disable any previous
+     * update and any other structured contents (like a filesystem),
+     * and as a placeholder for the amount of space required.
+     */
+ 
+    struct update_header header;
+    memset(&header, 0, sizeof(header));
+    const ssize_t header_size = sizeof(header);
+    if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
+        LOGE("Can't write header to cache\n(%s)\n", strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+ 
+    /* Write each section individually block-aligned, so we can write
+     * each block independently without complicated buffering.
+     */
+ 
+    memcpy(&header.MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE);
+    header.version = UPDATE_VERSION;
+    header.size = header_size;
+ 
+    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) {
+        LOGE("Can't write update to cache\n(%s)\n", strerror(errno));
+        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;
+    header.bitmap_bpp = bitmap_bpp;
+ 
+    int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;
+ 
+    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) {
+        LOGE("Can't write bitmap to cache\n(%s)\n", strerror(errno));
+        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_length = fail_bitmap != NULL ? bitmap_length : 0;
+    if ((int) header.fail_bitmap_offset == -1 ||
+        mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
+        LOGE("Can't write bitmap to cache\n(%s)\n", strerror(errno));
+        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.
+     */
+ 
+    if (mtd_write_close(write)) {
+        LOGE("Can't finish writing cache\n(%s)\n", strerror(errno));
+        return -1;
+    }
+ 
+    write = mtd_write_partition(part);
+    if (write == NULL) {
+        LOGE("Can't reopen cache\n(%s)\n", strerror(errno));
+        return -1;
+    }
+ 
+    if (mtd_write_data(write, (char*) &header, header_size) != header_size) {
+        LOGE("Can't rewrite header to cache\n(%s)\n", strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+ 
+    if (mtd_erase_blocks(write, 0) != image_start_pos) {
+        LOGE("Misalignment rewriting cache\n(%s)\n", strerror(errno));
+        mtd_write_close(write);
+        return -1;
+    }
+ 
+    if (mtd_write_close(write)) {
+        LOGE("Can't finish header of cache\n(%s)\n", strerror(errno));
+        return -1;
+    }
+ 
+    return 0;
+}
diff --git a/bootloader.h b/bootloader.h
index 2e749aa..3d4302f 100644
--- a/bootloader.h
+++ b/bootloader.h
@@ -47,4 +47,13 @@
 int get_bootloader_message(struct bootloader_message *out);
 int set_bootloader_message(const struct bootloader_message *in);
 
+/* Write an update to the cache partition for update-radio or update-hboot.
+ * Note, this destroys any filesystem on the cache partition!
+ * The expected bitmap format is 240x320, 16bpp (2Bpp), RGB 5:6:5.
+ */
+int write_update_for_bootloader(
+        const char *update, int update_len,
+        int bitmap_width, int bitmap_height, int bitmap_bpp,
+        const char *busy_bitmap, const char *error_bitmap);
+
 #endif
diff --git a/common.h b/common.h
index ef2fe9f..4c55c06 100644
--- a/common.h
+++ b/common.h
@@ -34,11 +34,15 @@
 // The screen is small, and users may need to report these messages to support,
 // so keep the output short and not too cryptic.
 void ui_print(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void ui_printlogtail(int nb_lines);
+
+void ui_reset_text_col();
+void ui_set_show_text(int value);
 
 // Display some header text followed by a menu of items, which appears
 // at the top of the screen (in place of any scrolling ui_print()
 // output, if necessary).
-void ui_start_menu(char** headers, char** items, int initial_selection);
+int ui_start_menu(char** headers, char** items, int initial_selection);
 // Set the menu highlight to the given index, and return it (capped to
 // the range [0..numitems).
 int ui_menu_select(int sel);
@@ -46,15 +50,26 @@
 // statements will be displayed.
 void ui_end_menu();
 
+int ui_get_showing_back_button();
+void ui_set_showing_back_button(int showBackButton);
+
 // Set the icon (normally the only thing visible besides the progress bar).
 enum {
   BACKGROUND_ICON_NONE,
   BACKGROUND_ICON_INSTALLING,
   BACKGROUND_ICON_ERROR,
+  BACKGROUND_ICON_CLOCKWORK,
+  BACKGROUND_ICON_FIRMWARE_INSTALLING,
+  BACKGROUND_ICON_FIRMWARE_ERROR,
   NUM_BACKGROUND_ICONS
 };
 void ui_set_background(int icon);
 
+// Get a malloc'd copy of the screen image showing (only) the specified icon.
+// Also returns the width, height, and bits per pixel of the returned image.
+// TODO: Use some sort of "struct Bitmap" here instead of all these variables?
+char *ui_copy_image(int icon, int *width, int *height, int *bpp);
+
 // Show a progress bar and define the scope of the next operation:
 //   portion - fraction of the progress bar the next operation will use
 //   seconds - expected time interval (progress bar moves at this minimum rate)
@@ -105,6 +120,12 @@
                               // partition.  0 or negative number
                               // means to format all but the last
                               // (that much).
+
+    const char* fs_type2;
+
+    const char* fs_options;
+
+    const char* fs_options2;
 } Volume;
 
 typedef struct {
diff --git a/dedupe/Android.mk b/dedupe/Android.mk
new file mode 100644
index 0000000..6696c49
--- /dev/null
+++ b/dedupe/Android.mk
@@ -0,0 +1,24 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := dedupe.c
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := dedupe
+LOCAL_STATIC_LIBRARIES := libcrypto_static
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../external/openssl/include
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := dedupe.c
+LOCAL_STATIC_LIBRARIES := libcrypto libcutils libc
+LOCAL_MODULE := utility_dedupe
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_STEM := dedupe
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_C_INCLUDES := external/openssl/include
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
diff --git a/dedupe/dedupe.c b/dedupe/dedupe.c
new file mode 100644
index 0000000..b8ac0bf
--- /dev/null
+++ b/dedupe/dedupe.c
@@ -0,0 +1,330 @@
+#include <stdio.h>
+#include <sys/stat.h>
+#include <openssl/md5.h>
+#include <openssl/sha.h>
+#include <openssl/ripemd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <limits.h>
+
+typedef struct DEDUPE_STORE_CONTEXT {
+    char blob_dir[PATH_MAX];
+    FILE *output_manifest;
+};
+
+static void usage(char** argv) {
+    fprintf(stderr, "usage: %s c input_directory blob_dir output_manifest\n", argv[0]);
+    fprintf(stderr, "usage: %s x input_manifest blob_dir output_directory\n", argv[0]);
+}
+
+static int copy_file(const char *dst, const char *src) {
+    char buf[4096];
+    int dstfd, srcfd, bytes_read, bytes_written, total_read = 0;
+    if (src == NULL)
+        return 1;
+    if (dst == NULL)
+        return 2;
+    
+    srcfd = open(src, O_RDONLY);
+    if (srcfd < 0)
+        return 3;
+
+    dstfd = open(dst, O_RDWR | O_CREAT | O_TRUNC, 0600);
+    if (dstfd < 0) {
+        close(srcfd);
+        return 4;
+    }
+
+    while (bytes_read = read(srcfd, buf, 4096)) {
+        total_read += bytes_read;
+        if (write(dstfd, buf, bytes_read) != bytes_read)
+            return 5;
+    }
+
+    close(dstfd);
+    close(srcfd);
+    
+    return 0;
+}
+
+static void do_sha256sum(FILE *mfile, unsigned char *rptr) {
+    char rdata[BUFSIZ];
+    int rsize;
+    SHA256_CTX c;
+    
+    SHA256_Init(&c);
+    while(!feof(mfile)) {
+        rsize = fread(rdata, sizeof(char), BUFSIZ, mfile);
+        if(rsize > 0) {
+            SHA256_Update(&c, rdata, rsize);
+        }
+    }
+
+    SHA256_Final(rptr, &c);
+}
+
+static int do_sha256sum_file(const char* filename, unsigned char *rptr) {
+    FILE *f = fopen(filename, "rb");
+    if (f == NULL) {
+        fprintf(stderr, "Unable to open file: %s\n", filename);
+        return 1;
+    }
+    do_sha256sum(f, rptr);
+    fclose(f);
+    return 0;
+}
+
+static int store_st(struct DEDUPE_STORE_CONTEXT *context, struct stat st, const char* s);
+
+void print_stat(struct DEDUPE_STORE_CONTEXT *context, char type, struct stat st, const char *f) {
+    fprintf(context->output_manifest, "%c\t%o\t%d\t%d\t%s\t", type, st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID), st.st_uid, st.st_gid, f);
+}
+
+static int store_file(struct DEDUPE_STORE_CONTEXT *context, struct stat st, const char* f) {
+    printf("%s\n", f);
+    unsigned char sumdata[SHA256_DIGEST_LENGTH];
+    int ret;
+    if (ret = do_sha256sum_file(f, sumdata)) {
+        fprintf(stderr, "Error calculating sha256sum of %s\n", f);
+        return ret; 
+    }
+    char psum[128];
+    int j;
+    for (j = 0; j < SHA256_DIGEST_LENGTH; j++)
+        sprintf(&psum[(j*2)], "%02x", (int)sumdata[j]);
+    psum[(SHA256_DIGEST_LENGTH * 2)] = '\0';
+
+    char out_blob[PATH_MAX];
+    sprintf(out_blob, "%s/%s", context->blob_dir, psum);
+    if (ret = copy_file(out_blob, f)) {
+        fprintf(stderr, "Error copying blob %s\n", f);
+        return ret;
+    }
+    
+    fprintf(context->output_manifest, "%s\t%d\t\n", psum, st.st_size);
+    return 0;
+}
+
+static int store_dir(struct DEDUPE_STORE_CONTEXT *context, struct stat st, const char* d) {
+    printf("%s\n", d);
+    DIR *dp = opendir(d);
+    if (d == NULL) {
+        fprintf(stderr, "Error opening directory: %s\n", d);
+        return 1;
+    }
+    struct dirent *ep;
+    char full_path[PATH_MAX];
+    while (ep = readdir(dp)) {
+        if (strcmp(ep->d_name, ".") == 0)
+            continue;
+        if (strcmp(ep->d_name, "..") == 0)
+            continue;
+        struct stat cst;
+        int ret;
+        sprintf(full_path, "%s/%s", d, ep->d_name);
+        if (0 != (ret = lstat(full_path, &cst))) {
+            fprintf(stderr, "Error opening: %s\n", ep->d_name);
+            closedir(dp);
+            return ret;
+        }
+        
+        if (ret = store_st(context, cst, full_path))
+            return ret;
+    }
+    closedir(dp);
+    return 0;
+}
+
+static int store_link(struct DEDUPE_STORE_CONTEXT *context, struct stat st, const char* l) {
+    printf("%s\n", l);
+    char link[PATH_MAX];
+    int ret = readlink(l, link, PATH_MAX);
+    if (ret < 0) {
+        fprintf(stderr, "Error reading symlink\n");
+        return errno;
+    }
+    link[ret] = '\0';
+    fprintf(context->output_manifest, "%s\t\n", link);
+    return 0;
+}
+
+static int store_st(struct DEDUPE_STORE_CONTEXT *context, struct stat st, const char* s) {
+    if (S_ISREG(st.st_mode)) {
+        print_stat(context, 'f', st, s);
+        return store_file(context, st, s);
+    }
+    else if (S_ISDIR(st.st_mode)) {
+        print_stat(context, 'd', st, s);
+        fprintf(context->output_manifest, "\n");
+        return store_dir(context, st, s);
+    }
+    else if (S_ISLNK(st.st_mode)) {
+        print_stat(context, 'l', st, s);
+        return store_link(context, st, s);
+    }
+    else {
+        fprintf(stderr, "Skipping special: %s\n", s);
+        return 0;
+    }
+}
+
+void get_full_path(char *out_path, char *rel_path) {
+    char tmp[PATH_MAX];
+    getcwd(tmp, PATH_MAX);
+    chdir(rel_path);
+    getcwd(out_path, PATH_MAX);
+    chdir(tmp);
+}
+
+static char* tokenize(char *out, const char* line, const char sep) {
+    while (*line != sep) {
+        if (*line == '\0') {
+            return NULL;
+        }
+        
+        *out = *line;
+        out++;
+        line++;
+    }
+    
+    *out = '\0';
+    // resume at the next char
+    return ++line;
+}
+
+static int dec_to_oct(int dec) {
+    int ret = 0;
+    int mult = 1;
+    while (dec != 0) {
+        int rem = dec % 10;
+        ret += (rem * mult);
+        dec /= 10;
+        mult *= 8;
+    }
+    
+    return ret;
+}
+
+int main(int argc, char** argv) {
+    if (argc != 5) {
+        usage(argv);
+        return 1;
+    }
+
+    if (strcmp(argv[1], "c") == 0) {
+        struct stat st;
+        int ret;
+        if (0 != (ret = lstat(argv[2], &st))) {
+            fprintf(stderr, "Error opening input_file/input_directory.\n");
+            return ret;
+        }
+        
+        if (!S_ISDIR(st.st_mode)) {
+            fprintf(stderr, "%s must be a directory.\n", argv[2]);
+            return 1;
+        }
+        
+        char blob_dir[PATH_MAX];
+        struct DEDUPE_STORE_CONTEXT context;
+        context.output_manifest = fopen(argv[4], "wb");
+        if (context.output_manifest == NULL) {
+            fprintf(stderr, "Unable to open output file %s\n", argv[4]);
+            return 1;
+        }
+        get_full_path(context.blob_dir, argv[3]);
+        chdir(argv[2]);
+        
+        return store_dir(&context, st, ".");
+    }
+    else if (strcmp(argv[1], "x") == 0) {
+        FILE *input_manifest = fopen(argv[2], "rb");
+        if (input_manifest == NULL) {
+            fprintf(stderr, "Unable to open input manifest %s\n", argv[2]);
+            return 1;
+        }
+
+        char blob_dir[PATH_MAX];
+        char *output_dir = argv[4];
+        get_full_path(blob_dir, argv[3]);
+    
+        printf("%s\n" , output_dir);
+        chdir(output_dir);
+        
+        char line[PATH_MAX];
+        while (fgets(line, PATH_MAX, input_manifest)) {
+            //printf("%s", line);
+            
+            char type[4];
+            char mode[8];
+            char uid[32];
+            char gid[32];
+            char filename[PATH_MAX];
+            
+            char *token = line;
+            token = tokenize(type, token, '\t');
+            token = tokenize(mode, token, '\t');
+            token = tokenize(uid, token, '\t');
+            token = tokenize(gid, token, '\t');
+            token = tokenize(filename, token, '\t');
+            
+            int mode_oct = dec_to_oct(atoi(mode));
+            int uid_int = atoi(uid);
+            int gid_int = atoi(gid);
+            int ret;
+            printf("%s\t%s\t%s\t%s\t%s\t", type, mode, uid, gid, filename);
+            if (strcmp(type, "f") == 0) {
+                char sha256[128];
+                token = tokenize(sha256, token, '\t');
+                char sizeStr[32];
+                token = tokenize(sizeStr, token, '\t');
+                int size = atoi(sizeStr);
+                printf("%s\t%d\n", sha256, size);
+                
+                char blob_file[PATH_MAX];
+                sprintf(blob_file, "%s/%s", blob_dir, sha256);
+                if (ret = copy_file(filename, blob_file)) {
+                    fprintf(stderr, "Unable to copy file %s\n", filename);
+                    fclose(input_manifest);
+                    return ret;
+                }
+                
+                chmod(filename, mode_oct);
+                chown(filename, uid_int, gid_int);
+            }
+            else if (strcmp(type, "l") == 0) {
+                char link[41];
+                token = tokenize(link, token, '\t');
+                printf("%s\n", link);
+                
+                symlink(link, filename);
+
+                // Android has no lchmod, and chmod follows symlinks
+                //chmod(filename, mode_oct);
+                lchown(filename, uid_int, gid_int);
+            }
+            else if (strcmp(type, "d") == 0) {
+                printf("\n");
+
+                mkdir(filename, mode_oct);
+
+                chmod(filename, mode_oct);
+                chown(filename, uid_int, gid_int);
+            }
+            else {
+                fprintf(stderr, "Unknown type %s\n", type);
+                fclose(input_manifest);
+                return 1;
+            }
+        }
+        
+        fclose(input_manifest);
+        return 0;
+    }
+    else {
+        usage(argv);
+        return 1;
+    }
+}
diff --git a/default_recovery_keys.c b/default_recovery_keys.c
new file mode 100644
index 0000000..4ba8701
--- /dev/null
+++ b/default_recovery_keys.c
@@ -0,0 +1,64 @@
+#include <linux/input.h>
+
+#include "recovery_ui.h"
+#include "common.h"
+#include "extendedcommands.h"
+
+
+int device_toggle_display(volatile char* key_pressed, int key_code) {
+    int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT];
+    if (alt && key_code == KEY_L)
+        return 1;
+    // allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off
+    if (ui_get_showing_back_button()) {
+        return 0;
+        //return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_END);
+    }
+    return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END);
+}
+
+int device_handle_key(int key_code, int visible) {
+    if (visible) {
+        switch (key_code) {
+            case KEY_CAPSLOCK:
+            case KEY_DOWN:
+            case KEY_VOLUMEDOWN:
+            case KEY_MENU:
+                return HIGHLIGHT_DOWN;
+
+            case KEY_LEFTSHIFT:
+            case KEY_UP:
+            case KEY_VOLUMEUP:
+            case KEY_HOME:
+                return HIGHLIGHT_UP;
+
+            case KEY_POWER:
+                if (ui_get_showing_back_button()) {
+                    return SELECT_ITEM;
+                }
+                if (!get_allow_toggle_display())
+                    return GO_BACK;
+                break;
+            case KEY_LEFTBRACE:
+            case KEY_ENTER:
+            case BTN_MOUSE:
+            case KEY_CAMERA:
+            case KEY_F21:
+            case KEY_SEND:
+                return SELECT_ITEM;
+            
+            case KEY_END:
+            case KEY_BACKSPACE:
+            case KEY_SEARCH:
+                if (ui_get_showing_back_button()) {
+                    return SELECT_ITEM;
+                }
+                if (!get_allow_toggle_display())
+                    return GO_BACK;
+            case KEY_BACK:
+                return GO_BACK;
+        }
+    }
+
+    return NO_ACTION;
+}
diff --git a/default_recovery_ui.c b/default_recovery_ui.c
index d56164e..be8b7e8 100644
--- a/default_recovery_ui.c
+++ b/default_recovery_ui.c
@@ -18,16 +18,17 @@
 
 #include "recovery_ui.h"
 #include "common.h"
+#include "extendedcommands.h"
 
-char* MENU_HEADERS[] = { "Android system recovery utility",
-                         "",
-                         NULL };
+char* MENU_HEADERS[] = { NULL };
 
 char* MENU_ITEMS[] = { "reboot system now",
-                       "apply update from external storage",
+                       "install zip from sdcard",
                        "wipe data/factory reset",
                        "wipe cache partition",
-                       "apply update from cache",
+                       "backup and restore",
+                       "mounts and storage",
+                       "advanced",
                        NULL };
 
 void device_ui_init(UIParameters* ui_parameters) {
@@ -37,33 +38,10 @@
     return 0;
 }
 
-int device_toggle_display(volatile char* key_pressed, int key_code) {
-    return key_code == KEY_HOME;
-}
-
 int device_reboot_now(volatile char* key_pressed, int key_code) {
     return 0;
 }
 
-int device_handle_key(int key_code, int visible) {
-    if (visible) {
-        switch (key_code) {
-            case KEY_DOWN:
-            case KEY_VOLUMEDOWN:
-                return HIGHLIGHT_DOWN;
-
-            case KEY_UP:
-            case KEY_VOLUMEUP:
-                return HIGHLIGHT_UP;
-
-            case KEY_ENTER:
-                return SELECT_ITEM;
-        }
-    }
-
-    return NO_ACTION;
-}
-
 int device_perform_action(int which) {
     return which;
 }
diff --git a/edifyscripting.c b/edifyscripting.c
new file mode 100644
index 0000000..e66df2b
--- /dev/null
+++ b/edifyscripting.c
@@ -0,0 +1,429 @@
+// Copyright ClockworkMod, LLC. Reference and porting purposes only. Usage of the extendedcommand API
+// is restricted to those granted explicit permission, or by use of the ROM Manager Recovery API.
+// https://github.com/koush/TestRomManager
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "bootloader.h"
+#include "common.h"
+#include "cutils/properties.h"
+#include "firmware.h"
+#include "install.h"
+#include "minui/minui.h"
+#include "minzip/DirUtil.h"
+#include "roots.h"
+#include "recovery_ui.h"
+
+#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
+#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
+
+#include "extendedcommands.h"
+#include "nandroid.h"
+#include "mounts.h"
+#include "flashutils/flashutils.h"
+#include "edify/expr.h"
+#include "mtdutils/mtdutils.h"
+#include "mmcutils/mmcutils.h"
+//#include "edify/parser.h"
+
+Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char** args = ReadVarArgs(state, argc, argv);
+    if (args == NULL) {
+        return NULL;
+    }
+
+    int size = 0;
+    int i;
+    for (i = 0; i < argc; ++i) {
+        size += strlen(args[i]);
+    }
+    char* buffer = malloc(size+1);
+    size = 0;
+    for (i = 0; i < argc; ++i) {
+        strcpy(buffer+size, args[i]);
+        size += strlen(args[i]);
+        free(args[i]);
+    }
+    free(args);
+    buffer[size] = '\0';
+
+    char* line = strtok(buffer, "\n");
+    while (line) {
+        ui_print("%s\n", line);
+        line = strtok(NULL, "\n");
+    }
+
+    return StringValue(buffer);
+}
+
+Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
+    if (argc < 1) {
+        return ErrorAbort(state, "%s() expects at least 1 arg", name);
+    }
+    char** args = ReadVarArgs(state, argc, argv);
+    if (args == NULL) {
+        return NULL;
+    }
+
+    char** args2 = malloc(sizeof(char*) * (argc+1));
+    memcpy(args2, args, sizeof(char*) * argc);
+    args2[argc] = NULL;
+
+    fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
+
+    pid_t child = fork();
+    if (child == 0) {
+        execv(args2[0], args2);
+        fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
+        _exit(1);
+    }
+    int status;
+    waitpid(child, &status, 0);
+    if (WIFEXITED(status)) {
+        if (WEXITSTATUS(status) != 0) {
+            fprintf(stderr, "run_program: child exited with status %d\n",
+                    WEXITSTATUS(status));
+        }
+    } else if (WIFSIGNALED(status)) {
+        fprintf(stderr, "run_program: child terminated by signal %d\n",
+                WTERMSIG(status));
+    }
+
+    int i;
+    for (i = 0; i < argc; ++i) {
+        free(args[i]);
+    }
+    free(args);
+    free(args2);
+
+    char buffer[20];
+    sprintf(buffer, "%d", status);
+
+    return StringValue(strdup(buffer));
+}
+
+Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char* result = NULL;
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
+    }
+    
+    char *path;
+    if (ReadArgs(state, argv, 1, &path) < 0) {
+        return NULL;
+    }
+    
+    ui_print("Formatting %s...\n", path);
+    if (0 != format_volume(path)) {
+        free(path);
+        return StringValue(strdup(""));
+    }
+    
+    if (strcmp(path, "/data") == 0 && has_datadata()) {
+        ui_print("Formatting /datadata...\n", path);
+        if (0 != format_volume("/datadata")) {
+            free(path);
+            return StringValue(strdup(""));
+        }
+        if (0 != format_volume("/sdcard/.android_secure")) {
+            free(path);
+            return StringValue(strdup(""));
+        }
+    }
+
+done:
+    return StringValue(strdup(path));
+}
+
+Value* BackupFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char* result = NULL;
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc);
+    }
+    char* path;
+    if (ReadArgs(state, argv, 1, &path) < 0) {
+        return NULL;
+    }
+    
+    if (0 != nandroid_backup(path))
+        return StringValue(strdup(""));
+    
+    return StringValue(strdup(path));
+}
+
+Value* RestoreFn(const char* name, State* state, int argc, Expr* argv[]) {
+    if (argc < 1) {
+        return ErrorAbort(state, "%s() expects at least 1 arg", name);
+    }
+    char** args = ReadVarArgs(state, argc, argv);
+    if (args == NULL) {
+        return NULL;
+    }
+
+    char** args2 = malloc(sizeof(char*) * (argc+1));
+    memcpy(args2, args, sizeof(char*) * argc);
+    args2[argc] = NULL;
+    
+    char* path = strdup(args2[0]);
+    int restoreboot = 1;
+    int restoresystem = 1;
+    int restoredata = 1;
+    int restorecache = 1;
+    int restoresdext = 1;
+    int i;
+    for (i = 1; i < argc; i++)
+    {
+        if (args2[i] == NULL)
+            continue;
+        if (strcmp(args2[i], "noboot") == 0)
+            restoreboot = 0;
+        else if (strcmp(args2[i], "nosystem") == 0)
+            restoresystem = 0;
+        else if (strcmp(args2[i], "nodata") == 0)
+            restoredata = 0;
+        else if (strcmp(args2[i], "nocache") == 0)
+            restorecache = 0;
+        else if (strcmp(args2[i], "nosd-ext") == 0)
+            restoresdext = 0;
+    }
+    
+    for (i = 0; i < argc; ++i) {
+        free(args[i]);
+    }
+    free(args);
+    free(args2);
+
+    if (0 != nandroid_restore(path, restoreboot, restoresystem, restoredata, restorecache, restoresdext, 0)) {
+        free(path);
+        return StringValue(strdup(""));
+    }
+    
+    return StringValue(path);
+}
+
+Value* InstallZipFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char* result = NULL;
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc);
+    }
+    char* path;
+    if (ReadArgs(state, argv, 1, &path) < 0) {
+        return NULL;
+    }
+    
+    if (0 != install_zip(path))
+        return StringValue(strdup(""));
+    
+    return StringValue(strdup(path));
+}
+
+Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
+    char* result = NULL;
+    if (argc != 1) {
+        return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc);
+    }
+    char* path;
+    if (ReadArgs(state, argv, 1, &path) < 0) {
+        return NULL;
+    }
+    
+    if (0 != ensure_path_mounted(path))
+        return StringValue(strdup(""));
+
+    return StringValue(strdup(path));
+}
+
+void RegisterRecoveryHooks() {
+    RegisterFunction("mount", MountFn);
+    RegisterFunction("format", FormatFn);
+    RegisterFunction("ui_print", UIPrintFn);
+    RegisterFunction("run_program", RunProgramFn);
+    RegisterFunction("backup_rom", BackupFn);
+    RegisterFunction("restore_rom", RestoreFn);
+    RegisterFunction("install_zip", InstallZipFn);
+}
+
+static int hasInitializedEdify = 0;
+int run_script_from_buffer(char* script_data, int script_len, char* filename)
+{
+    if (!hasInitializedEdify) {
+        RegisterBuiltins();
+        RegisterRecoveryHooks();
+        FinishRegistration();
+        hasInitializedEdify = 1;
+    }
+
+    Expr* root;
+    int error_count = 0;
+    yy_scan_bytes(script_data, script_len);
+    int error = yyparse(&root, &error_count);
+    printf("parse returned %d; %d errors encountered\n", error, error_count);
+    if (error == 0 || error_count > 0) {
+        //ExprDump(0, root, buffer);
+
+        State state;
+        state.cookie = NULL;
+        state.script = script_data;
+        state.errmsg = NULL;
+
+        char* result = Evaluate(&state, root);
+        if (result == NULL) {
+            printf("result was NULL, message is: %s\n",
+                   (state.errmsg == NULL ? "(NULL)" : state.errmsg));
+            free(state.errmsg);
+            return -1;
+        } else {
+            printf("result is [%s]\n", result);
+        }
+    }
+    return 0;
+}
+
+
+
+#define EXTENDEDCOMMAND_SCRIPT "/cache/recovery/extendedcommand"
+
+int run_and_remove_extendedcommand()
+{
+    char tmp[PATH_MAX];
+    sprintf(tmp, "cp %s /tmp/%s", EXTENDEDCOMMAND_SCRIPT, basename(EXTENDEDCOMMAND_SCRIPT));
+    __system(tmp);
+    remove(EXTENDEDCOMMAND_SCRIPT);
+    int i = 0;
+    for (i = 20; i > 0; i--) {
+        ui_print("Waiting for SD Card to mount (%ds)\n", i);
+        if (ensure_path_mounted("/sdcard") == 0) {
+            ui_print("SD Card mounted...\n");
+            break;
+        }
+        sleep(1);
+    }
+    remove("/sdcard/clockworkmod/.recoverycheckpoint");
+    if (i == 0) {
+        ui_print("Timed out waiting for SD card... continuing anyways.");
+    }
+
+    ui_print("Verifying SD Card marker...\n");
+    struct stat st;
+    if (stat("/sdcard/clockworkmod/.salted_hash", &st) != 0) {
+        ui_print("SD Card marker not found...\n");
+        if (volume_for_path("/emmc") != NULL) {
+            ui_print("Checking Internal SD Card marker...\n");
+            ensure_path_unmounted("/sdcard");
+            if (ensure_path_mounted_at_mount_point("/emmc", "/sdcard") != 0) {
+                ui_print("Internal SD Card marker not found... continuing anyways.\n");
+                // unmount everything, and remount as normal
+                ensure_path_unmounted("/emmc");
+                ensure_path_unmounted("/sdcard");
+
+                ensure_path_mounted("/sdcard");
+            }
+        }
+    }
+
+    sprintf(tmp, "/tmp/%s", basename(EXTENDEDCOMMAND_SCRIPT));
+    int ret;
+#ifdef I_AM_KOUSH
+    if (0 != (ret = before_run_script(tmp))) {
+        ui_print("Error processing ROM Manager script. Please verify that you are performing the backup, restore, or ROM installation from ROM Manager v4.4.0.0 or higher.\n");
+        return ret;
+    }
+#endif
+    return run_script(tmp);
+}
+
+int extendedcommand_file_exists()
+{
+    struct stat file_info;
+    return 0 == stat(EXTENDEDCOMMAND_SCRIPT, &file_info);
+}
+
+int edify_main(int argc, char** argv) {
+    load_volume_table();
+    process_volumes();
+    RegisterBuiltins();
+    RegisterRecoveryHooks();
+    FinishRegistration();
+
+    if (argc != 2) {
+        printf("edify <filename>\n");
+        return 1;
+    }
+
+    FILE* f = fopen(argv[1], "r");
+    if (f == NULL) {
+        printf("%s: %s: No such file or directory\n", argv[0], argv[1]);
+        return 1;
+    }
+    char buffer[8192];
+    int size = fread(buffer, 1, 8191, f);
+    fclose(f);
+    buffer[size] = '\0';
+
+    Expr* root;
+    int error_count = 0;
+    yy_scan_bytes(buffer, size);
+    int error = yyparse(&root, &error_count);
+    printf("parse returned %d; %d errors encountered\n", error, error_count);
+    if (error == 0 || error_count > 0) {
+
+        //ExprDump(0, root, buffer);
+
+        State state;
+        state.cookie = NULL;
+        state.script = buffer;
+        state.errmsg = NULL;
+
+        char* result = Evaluate(&state, root);
+        if (result == NULL) {
+            printf("result was NULL, message is: %s\n",
+                   (state.errmsg == NULL ? "(NULL)" : state.errmsg));
+            free(state.errmsg);
+        } else {
+            printf("result is [%s]\n", result);
+        }
+    }
+    return 0;
+}
+
+int run_script(char* filename)
+{
+    struct stat file_info;
+    if (0 != stat(filename, &file_info)) {
+        printf("Error executing stat on file: %s\n", filename);
+        return 1;
+    }
+
+    int script_len = file_info.st_size;
+    char* script_data = (char*)malloc(script_len + 1);
+    FILE *file = fopen(filename, "rb");
+    fread(script_data, script_len, 1, file);
+    // supposedly not necessary, but let's be safe.
+    script_data[script_len] = '\0';
+    fclose(file);
+    LOGI("Running script:\n");
+    LOGI("\n%s\n", script_data);
+
+    int ret = run_script_from_buffer(script_data, script_len, filename);
+    free(script_data);
+    return ret;
+}
diff --git a/etc/init.rc b/etc/init.rc
index 554d4e6..0c76fb7 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -9,7 +9,11 @@
 
     symlink /system/etc /etc
 
+    mkdir /boot
     mkdir /sdcard
+    mkdir /sd-ext
+    mkdir /datadata
+    mkdir /emmc
     mkdir /system
     mkdir /data
     mkdir /cache
diff --git a/extendedcommands.c b/extendedcommands.c
new file mode 100644
index 0000000..bbe6018
--- /dev/null
+++ b/extendedcommands.c
@@ -0,0 +1,1219 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "bootloader.h"
+#include "common.h"
+#include "cutils/properties.h"
+#include "firmware.h"
+#include "install.h"
+#include "minui/minui.h"
+#include "minzip/DirUtil.h"
+#include "roots.h"
+#include "recovery_ui.h"
+
+#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
+#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
+
+#include "extendedcommands.h"
+#include "nandroid.h"
+#include "mounts.h"
+#include "flashutils/flashutils.h"
+#include "edify/expr.h"
+#include <libgen.h>
+#include "mtdutils/mtdutils.h"
+#include "bmlutils/bmlutils.h"
+#include "cutils/android_reboot.h"
+
+
+int signature_check_enabled = 1;
+int script_assert_enabled = 1;
+static const char *SDCARD_UPDATE_FILE = "/sdcard/update.zip";
+
+void
+toggle_signature_check()
+{
+    signature_check_enabled = !signature_check_enabled;
+    ui_print("Signature Check: %s\n", signature_check_enabled ? "Enabled" : "Disabled");
+}
+
+void toggle_script_asserts()
+{
+    script_assert_enabled = !script_assert_enabled;
+    ui_print("Script Asserts: %s\n", script_assert_enabled ? "Enabled" : "Disabled");
+}
+
+int install_zip(const char* packagefilepath)
+{
+    ui_print("\n-- Installing: %s\n", packagefilepath);
+    if (device_flash_type() == MTD) {
+        set_sdcard_update_bootloader_message();
+    }
+    int status = install_package(packagefilepath);
+    ui_reset_progress();
+    if (status != INSTALL_SUCCESS) {
+        ui_set_background(BACKGROUND_ICON_ERROR);
+        ui_print("Installation aborted.\n");
+        return 1;
+    }
+    ui_set_background(BACKGROUND_ICON_NONE);
+    ui_print("\nInstall from sdcard complete.\n");
+    return 0;
+}
+
+char* INSTALL_MENU_ITEMS[] = {  "choose zip from sdcard",
+                                "apply /sdcard/update.zip",
+                                "toggle signature verification",
+                                "toggle script asserts",
+                                "choose zip from internal sdcard",
+                                NULL };
+#define ITEM_CHOOSE_ZIP       0
+#define ITEM_APPLY_SDCARD     1
+#define ITEM_SIG_CHECK        2
+#define ITEM_ASSERTS          3
+#define ITEM_CHOOSE_ZIP_INT   4
+
+void show_install_update_menu()
+{
+    static char* headers[] = {  "Apply update from .zip file on SD card",
+                                "",
+                                NULL
+    };
+    
+    if (volume_for_path("/emmc") == NULL)
+        INSTALL_MENU_ITEMS[ITEM_CHOOSE_ZIP_INT] = NULL;
+    
+    for (;;)
+    {
+        int chosen_item = get_menu_selection(headers, INSTALL_MENU_ITEMS, 0, 0);
+        switch (chosen_item)
+        {
+            case ITEM_ASSERTS:
+                toggle_script_asserts();
+                break;
+            case ITEM_SIG_CHECK:
+                toggle_signature_check();
+                break;
+            case ITEM_APPLY_SDCARD:
+            {
+                if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
+                    install_zip(SDCARD_UPDATE_FILE);
+                break;
+            }
+            case ITEM_CHOOSE_ZIP:
+                show_choose_zip_menu("/sdcard/");
+                break;
+            case ITEM_CHOOSE_ZIP_INT:
+                show_choose_zip_menu("/emmc/");
+                break;
+            default:
+                return;
+        }
+
+    }
+}
+
+void free_string_array(char** array)
+{
+    if (array == NULL)
+        return;
+    char* cursor = array[0];
+    int i = 0;
+    while (cursor != NULL)
+    {
+        free(cursor);
+        cursor = array[++i];
+    }
+    free(array);
+}
+
+char** gather_files(const char* directory, const char* fileExtensionOrDirectory, int* numFiles)
+{
+    char path[PATH_MAX] = "";
+    DIR *dir;
+    struct dirent *de;
+    int total = 0;
+    int i;
+    char** files = NULL;
+    int pass;
+    *numFiles = 0;
+    int dirLen = strlen(directory);
+
+    dir = opendir(directory);
+    if (dir == NULL) {
+        ui_print("Couldn't open directory.\n");
+        return NULL;
+    }
+
+    int extension_length = 0;
+    if (fileExtensionOrDirectory != NULL)
+        extension_length = strlen(fileExtensionOrDirectory);
+
+    int isCounting = 1;
+    i = 0;
+    for (pass = 0; pass < 2; pass++) {
+        while ((de=readdir(dir)) != NULL) {
+            // skip hidden files
+            if (de->d_name[0] == '.')
+                continue;
+
+            // NULL means that we are gathering directories, so skip this
+            if (fileExtensionOrDirectory != NULL)
+            {
+                // make sure that we can have the desired extension (prevent seg fault)
+                if (strlen(de->d_name) < extension_length)
+                    continue;
+                // compare the extension
+                if (strcmp(de->d_name + strlen(de->d_name) - extension_length, fileExtensionOrDirectory) != 0)
+                    continue;
+            }
+            else
+            {
+                struct stat info;
+                char fullFileName[PATH_MAX];
+                strcpy(fullFileName, directory);
+                strcat(fullFileName, de->d_name);
+                stat(fullFileName, &info);
+                // make sure it is a directory
+                if (!(S_ISDIR(info.st_mode)))
+                    continue;
+            }
+
+            if (pass == 0)
+            {
+                total++;
+                continue;
+            }
+
+            files[i] = (char*) malloc(dirLen + strlen(de->d_name) + 2);
+            strcpy(files[i], directory);
+            strcat(files[i], de->d_name);
+            if (fileExtensionOrDirectory == NULL)
+                strcat(files[i], "/");
+            i++;
+        }
+        if (pass == 1)
+            break;
+        if (total == 0)
+            break;
+        rewinddir(dir);
+        *numFiles = total;
+        files = (char**) malloc((total+1)*sizeof(char*));
+        files[total]=NULL;
+    }
+
+    if(closedir(dir) < 0) {
+        LOGE("Failed to close directory.");
+    }
+
+    if (total==0) {
+        return NULL;
+    }
+
+    // sort the result
+    if (files != NULL) {
+        for (i = 0; i < total; i++) {
+            int curMax = -1;
+            int j;
+            for (j = 0; j < total - i; j++) {
+                if (curMax == -1 || strcmp(files[curMax], files[j]) < 0)
+                    curMax = j;
+            }
+            char* temp = files[curMax];
+            files[curMax] = files[total - i - 1];
+            files[total - i - 1] = temp;
+        }
+    }
+
+    return files;
+}
+
+// pass in NULL for fileExtensionOrDirectory and you will get a directory chooser
+char* choose_file_menu(const char* directory, const char* fileExtensionOrDirectory, const char* headers[])
+{
+    char path[PATH_MAX] = "";
+    DIR *dir;
+    struct dirent *de;
+    int numFiles = 0;
+    int numDirs = 0;
+    int i;
+    char* return_value = NULL;
+    int dir_len = strlen(directory);
+
+    char** files = gather_files(directory, fileExtensionOrDirectory, &numFiles);
+    char** dirs = NULL;
+    if (fileExtensionOrDirectory != NULL)
+        dirs = gather_files(directory, NULL, &numDirs);
+    int total = numDirs + numFiles;
+    if (total == 0)
+    {
+        ui_print("No files found.\n");
+    }
+    else
+    {
+        char** list = (char**) malloc((total + 1) * sizeof(char*));
+        list[total] = NULL;
+
+
+        for (i = 0 ; i < numDirs; i++)
+        {
+            list[i] = strdup(dirs[i] + dir_len);
+        }
+
+        for (i = 0 ; i < numFiles; i++)
+        {
+            list[numDirs + i] = strdup(files[i] + dir_len);
+        }
+
+        for (;;)
+        {
+            int chosen_item = get_menu_selection(headers, list, 0, 0);
+            if (chosen_item == GO_BACK)
+                break;
+            static char ret[PATH_MAX];
+            if (chosen_item < numDirs)
+            {
+                char* subret = choose_file_menu(dirs[chosen_item], fileExtensionOrDirectory, headers);
+                if (subret != NULL)
+                {
+                    strcpy(ret, subret);
+                    return_value = ret;
+                    break;
+                }
+                continue;
+            }
+            strcpy(ret, files[chosen_item - numDirs]);
+            return_value = ret;
+            break;
+        }
+        free_string_array(list);
+    }
+
+    free_string_array(files);
+    free_string_array(dirs);
+    return return_value;
+}
+
+void show_choose_zip_menu(const char *mount_point)
+{
+    if (ensure_path_mounted(mount_point) != 0) {
+        LOGE ("Can't mount %s\n", mount_point);
+        return;
+    }
+
+    static char* headers[] = {  "Choose a zip to apply",
+                                "",
+                                NULL
+    };
+
+    char* file = choose_file_menu(mount_point, ".zip", headers);
+    if (file == NULL)
+        return;
+    static char* confirm_install  = "Confirm install?";
+    static char confirm[PATH_MAX];
+    sprintf(confirm, "Yes - Install %s", basename(file));
+    if (confirm_selection(confirm_install, confirm))
+        install_zip(file);
+}
+
+void show_nandroid_restore_menu(const char* path)
+{
+    if (ensure_path_mounted(path) != 0) {
+        LOGE("Can't mount %s\n", path);
+        return;
+    }
+
+    static char* headers[] = {  "Choose an image to restore",
+                                "",
+                                NULL
+    };
+
+    char tmp[PATH_MAX];
+    sprintf(tmp, "%s/clockworkmod/backup/", path);
+    char* file = choose_file_menu(tmp, NULL, headers);
+    if (file == NULL)
+        return;
+
+    if (confirm_selection("Confirm restore?", "Yes - Restore"))
+        nandroid_restore(file, 1, 1, 1, 1, 1, 0);
+}
+
+#ifndef BOARD_UMS_LUNFILE
+#define BOARD_UMS_LUNFILE	"/sys/devices/platform/usb_mass_storage/lun0/file"
+#endif
+
+void show_mount_usb_storage_menu()
+{
+    int fd;
+    Volume *vol = volume_for_path("/sdcard");
+    if ((fd = open(BOARD_UMS_LUNFILE, O_WRONLY)) < 0) {
+        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
+        return -1;
+    }
+
+    if ((write(fd, vol->device, strlen(vol->device)) < 0) &&
+        (!vol->device2 || (write(fd, vol->device, strlen(vol->device2)) < 0))) {
+        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
+        close(fd);
+        return -1;
+    }
+    static char* headers[] = {  "USB Mass Storage device",
+                                "Leaving this menu unmount",
+                                "your SD card from your PC.",
+                                "",
+                                NULL
+    };
+
+    static char* list[] = { "Unmount", NULL };
+
+    for (;;)
+    {
+        int chosen_item = get_menu_selection(headers, list, 0, 0);
+        if (chosen_item == GO_BACK || chosen_item == 0)
+            break;
+    }
+
+    if ((fd = open(BOARD_UMS_LUNFILE, O_WRONLY)) < 0) {
+        LOGE("Unable to open ums lunfile (%s)", strerror(errno));
+        return -1;
+    }
+
+    char ch = 0;
+    if (write(fd, &ch, 1) < 0) {
+        LOGE("Unable to write to ums lunfile (%s)", strerror(errno));
+        close(fd);
+        return -1;
+    }
+}
+
+int confirm_selection(const char* title, const char* confirm)
+{
+    struct stat info;
+    if (0 == stat("/sdcard/clockworkmod/.no_confirm", &info))
+        return 1;
+
+    char* confirm_headers[]  = {  title, "  THIS CAN NOT BE UNDONE.", "", NULL };
+    char* items[] = { "No",
+                      "No",
+                      "No",
+                      "No",
+                      "No",
+                      "No",
+                      "No",
+                      confirm, //" Yes -- wipe partition",   // [7
+                      "No",
+                      "No",
+                      "No",
+                      NULL };
+
+    int chosen_item = get_menu_selection(confirm_headers, items, 0, 0);
+    return chosen_item == 7;
+}
+
+#define MKE2FS_BIN      "/sbin/mke2fs"
+#define TUNE2FS_BIN     "/sbin/tune2fs"
+#define E2FSCK_BIN      "/sbin/e2fsck"
+
+int format_device(const char *device, const char *path, const char *fs_type) {
+    Volume* v = volume_for_path(path);
+    if (v == NULL) {
+        // no /sdcard? let's assume /data/media
+        if (strstr(path, "/sdcard") == path && is_data_media()) {
+            return format_unknown_device(NULL, path, NULL);
+        }
+        // silent failure for sd-ext
+        if (strcmp(path, "/sd-ext") == 0)
+            return -1;
+        LOGE("unknown volume \"%s\"\n", path);
+        return -1;
+    }
+    if (strcmp(fs_type, "ramdisk") == 0) {
+        // you can't format the ramdisk.
+        LOGE("can't format_volume \"%s\"", path);
+        return -1;
+    }
+
+    if (strcmp(fs_type, "rfs") == 0) {
+        if (ensure_path_unmounted(path) != 0) {
+            LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
+            return -1;
+        }
+        if (0 != format_rfs_device(device, path)) {
+            LOGE("format_volume: format_rfs_device failed on %s\n", device);
+            return -1;
+        }
+        return 0;
+    }
+ 
+    if (strcmp(v->mount_point, path) != 0) {
+        return format_unknown_device(v->device, path, NULL);
+    }
+
+    if (ensure_path_unmounted(path) != 0) {
+        LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point);
+        return -1;
+    }
+
+    if (strcmp(fs_type, "yaffs2") == 0 || strcmp(fs_type, "mtd") == 0) {
+        mtd_scan_partitions();
+        const MtdPartition* partition = mtd_find_partition_by_name(device);
+        if (partition == NULL) {
+            LOGE("format_volume: no MTD partition \"%s\"\n", device);
+            return -1;
+        }
+
+        MtdWriteContext *write = mtd_write_partition(partition);
+        if (write == NULL) {
+            LOGW("format_volume: can't open MTD \"%s\"\n", device);
+            return -1;
+        } else if (mtd_erase_blocks(write, -1) == (off_t) -1) {
+            LOGW("format_volume: can't erase MTD \"%s\"\n", device);
+            mtd_write_close(write);
+            return -1;
+        } else if (mtd_write_close(write)) {
+            LOGW("format_volume: can't close MTD \"%s\"\n",device);
+            return -1;
+        }
+        return 0;
+    }
+
+    if (strcmp(fs_type, "ext4") == 0) {
+        reset_ext4fs_info();
+        int result = make_ext4fs(device, NULL, NULL, 0, 0, 0);
+        if (result != 0) {
+            LOGE("format_volume: make_extf4fs failed on %s\n", device);
+            return -1;
+        }
+        return 0;
+    }
+
+    return format_unknown_device(device, path, fs_type);
+}
+
+int format_unknown_device(const char *device, const char* path, const char *fs_type)
+{
+    LOGI("Formatting unknown device.\n");
+
+    if (fs_type != NULL && get_flash_type(fs_type) != UNSUPPORTED)
+        return erase_raw_partition(fs_type, device);
+
+    // if this is SDEXT:, don't worry about it if it does not exist.
+    if (0 == strcmp(path, "/sd-ext"))
+    {
+        struct stat st;
+        Volume *vol = volume_for_path("/sd-ext");
+        if (vol == NULL || 0 != stat(vol->device, &st))
+        {
+            ui_print("No app2sd partition found. Skipping format of /sd-ext.\n");
+            return 0;
+        }
+    }
+
+    if (NULL != fs_type) {
+        if (strcmp("ext3", fs_type) == 0) {
+            LOGI("Formatting ext3 device.\n");
+            if (0 != ensure_path_unmounted(path)) {
+                LOGE("Error while unmounting %s.\n", path);
+                return -12;
+            }
+            return format_ext3_device(device);
+        }
+
+        if (strcmp("ext2", fs_type) == 0) {
+            LOGI("Formatting ext2 device.\n");
+            if (0 != ensure_path_unmounted(path)) {
+                LOGE("Error while unmounting %s.\n", path);
+                return -12;
+            }
+            return format_ext2_device(device);
+        }
+    }
+
+    if (0 != ensure_path_mounted(path))
+    {
+        ui_print("Error mounting %s!\n", path);
+        ui_print("Skipping format...\n");
+        return 0;
+    }
+
+    static char tmp[PATH_MAX];
+    if (strcmp(path, "/data") == 0) {
+        sprintf(tmp, "cd /data ; for f in $(ls -a | grep -v ^media$); do rm -rf $f; done");
+        __system(tmp);
+    }
+    else {
+        sprintf(tmp, "rm -rf %s/*", path);
+        __system(tmp);
+        sprintf(tmp, "rm -rf %s/.*", path);
+        __system(tmp);
+    }
+
+    ensure_path_unmounted(path);
+    return 0;
+}
+
+//#define MOUNTABLE_COUNT 5
+//#define DEVICE_COUNT 4
+//#define MMC_COUNT 2
+
+typedef struct {
+    char mount[255];
+    char unmount[255];
+    Volume* v;
+} MountMenuEntry;
+
+typedef struct {
+    char txt[255];
+    Volume* v;
+} FormatMenuEntry;
+
+int is_safe_to_format(char* name)
+{
+    char str[255];
+    char* partition;
+    property_get("ro.cwm.forbid_format", str, "/misc,/radio,/bootloader,/recovery,/efs");
+
+    partition = strtok(str, ", ");
+    while (partition != NULL) {
+        if (strcmp(name, partition) == 0) {
+            return 0;
+        }
+        partition = strtok(NULL, ", ");
+    }
+
+    return 1;
+}
+
+void show_partition_menu()
+{
+    static char* headers[] = {  "Mounts and Storage Menu",
+                                "",
+                                NULL
+    };
+
+    static MountMenuEntry* mount_menue = NULL;
+    static FormatMenuEntry* format_menue = NULL;
+
+    typedef char* string;
+
+    int i, mountable_volumes, formatable_volumes;
+    int num_volumes;
+    Volume* device_volumes;
+
+    num_volumes = get_num_volumes();
+    device_volumes = get_device_volumes();
+
+    string options[255];
+
+    if(!device_volumes)
+		return;
+
+		mountable_volumes = 0;
+		formatable_volumes = 0;
+
+		mount_menue = malloc(num_volumes * sizeof(MountMenuEntry));
+		format_menue = malloc(num_volumes * sizeof(FormatMenuEntry));
+
+		for (i = 0; i < num_volumes; ++i) {
+			Volume* v = &device_volumes[i];
+			if(strcmp("ramdisk", v->fs_type) != 0 && strcmp("mtd", v->fs_type) != 0 && strcmp("emmc", v->fs_type) != 0 && strcmp("bml", v->fs_type) != 0)
+			{
+				sprintf(&mount_menue[mountable_volumes].mount, "mount %s", v->mount_point);
+				sprintf(&mount_menue[mountable_volumes].unmount, "unmount %s", v->mount_point);
+				mount_menue[mountable_volumes].v = &device_volumes[i];
+				++mountable_volumes;
+				if (is_safe_to_format(v->mount_point)) {
+					sprintf(&format_menue[formatable_volumes].txt, "format %s", v->mount_point);
+					format_menue[formatable_volumes].v = &device_volumes[i];
+					++formatable_volumes;
+				}
+		    }
+		    else if (strcmp("ramdisk", v->fs_type) != 0 && strcmp("mtd", v->fs_type) == 0 && is_safe_to_format(v->mount_point))
+		    {
+				sprintf(&format_menue[formatable_volumes].txt, "format %s", v->mount_point);
+				format_menue[formatable_volumes].v = &device_volumes[i];
+				++formatable_volumes;
+			}
+		}
+
+
+    static char* confirm_format  = "Confirm format?";
+    static char* confirm = "Yes - Format";
+    char confirm_string[255];
+
+    for (;;)
+    {
+
+		for (i = 0; i < mountable_volumes; i++)
+		{
+			MountMenuEntry* e = &mount_menue[i];
+			Volume* v = e->v;
+			if(is_path_mounted(v->mount_point))
+				options[i] = e->unmount;
+			else
+				options[i] = e->mount;
+		}
+
+		for (i = 0; i < formatable_volumes; i++)
+		{
+			FormatMenuEntry* e = &format_menue[i];
+
+			options[mountable_volumes+i] = e->txt;
+		}
+
+        options[mountable_volumes+formatable_volumes] = "mount USB storage";
+        options[mountable_volumes+formatable_volumes + 1] = NULL;
+
+        int chosen_item = get_menu_selection(headers, &options, 0, 0);
+        if (chosen_item == GO_BACK)
+            break;
+        if (chosen_item == (mountable_volumes+formatable_volumes))
+        {
+            show_mount_usb_storage_menu();
+        }
+        else if (chosen_item < mountable_volumes)
+        {
+			MountMenuEntry* e = &mount_menue[chosen_item];
+            Volume* v = e->v;
+
+            if (is_path_mounted(v->mount_point))
+            {
+                if (0 != ensure_path_unmounted(v->mount_point))
+                    ui_print("Error unmounting %s!\n", v->mount_point);
+            }
+            else
+            {
+                if (0 != ensure_path_mounted(v->mount_point))
+                    ui_print("Error mounting %s!\n",  v->mount_point);
+            }
+        }
+        else if (chosen_item < (mountable_volumes + formatable_volumes))
+        {
+            chosen_item = chosen_item - mountable_volumes;
+            FormatMenuEntry* e = &format_menue[chosen_item];
+            Volume* v = e->v;
+
+            sprintf(confirm_string, "%s - %s", v->mount_point, confirm_format);
+
+            if (!confirm_selection(confirm_string, confirm))
+                continue;
+            ui_print("Formatting %s...\n", v->mount_point);
+            if (0 != format_volume(v->mount_point))
+                ui_print("Error formatting %s!\n", v->mount_point);
+            else
+                ui_print("Done.\n");
+        }
+    }
+
+    free(mount_menue);
+    free(format_menue);
+
+}
+
+void show_nandroid_advanced_restore_menu(const char* path)
+{
+    if (ensure_path_mounted(path) != 0) {
+        LOGE ("Can't mount sdcard\n");
+        return;
+    }
+
+    static char* advancedheaders[] = {  "Choose an image to restore",
+                                "",
+                                "Choose an image to restore",
+                                "first. The next menu will",
+                                "you more options.",
+                                "",
+                                NULL
+    };
+
+    char tmp[PATH_MAX];
+    sprintf(tmp, "%s/clockworkmod/backup/", path);
+    char* file = choose_file_menu(tmp, NULL, advancedheaders);
+    if (file == NULL)
+        return;
+
+    static char* headers[] = {  "Nandroid Advanced Restore",
+                                "",
+                                NULL
+    };
+
+    static char* list[] = { "Restore boot",
+                            "Restore system",
+                            "Restore data",
+                            "Restore cache",
+                            "Restore sd-ext",
+                            "Restore wimax",
+                            NULL
+    };
+    
+    if (0 != get_partition_device("wimax", tmp)) {
+        // disable wimax restore option
+        list[5] = NULL;
+    }
+
+    static char* confirm_restore  = "Confirm restore?";
+
+    int chosen_item = get_menu_selection(headers, list, 0, 0);
+    switch (chosen_item)
+    {
+        case 0:
+            if (confirm_selection(confirm_restore, "Yes - Restore boot"))
+                nandroid_restore(file, 1, 0, 0, 0, 0, 0);
+            break;
+        case 1:
+            if (confirm_selection(confirm_restore, "Yes - Restore system"))
+                nandroid_restore(file, 0, 1, 0, 0, 0, 0);
+            break;
+        case 2:
+            if (confirm_selection(confirm_restore, "Yes - Restore data"))
+                nandroid_restore(file, 0, 0, 1, 0, 0, 0);
+            break;
+        case 3:
+            if (confirm_selection(confirm_restore, "Yes - Restore cache"))
+                nandroid_restore(file, 0, 0, 0, 1, 0, 0);
+            break;
+        case 4:
+            if (confirm_selection(confirm_restore, "Yes - Restore sd-ext"))
+                nandroid_restore(file, 0, 0, 0, 0, 1, 0);
+            break;
+        case 5:
+            if (confirm_selection(confirm_restore, "Yes - Restore wimax"))
+                nandroid_restore(file, 0, 0, 0, 0, 0, 1);
+            break;
+    }
+}
+
+void show_nandroid_menu()
+{
+    static char* headers[] = {  "Nandroid",
+                                "",
+                                NULL
+    };
+
+    static char* list[] = { "backup",
+                            "restore",
+                            "advanced restore",
+                            "backup to internal sdcard",
+                            "restore from internal sdcard",
+                            "advanced restore from internal sdcard",
+                            NULL
+    };
+
+    if (volume_for_path("/emmc") == NULL)
+        list[3] = NULL;
+
+    int chosen_item = get_menu_selection(headers, list, 0, 0);
+    switch (chosen_item)
+    {
+        case 0:
+            {
+                char backup_path[PATH_MAX];
+                time_t t = time(NULL);
+                struct tm *tmp = localtime(&t);
+                if (tmp == NULL)
+                {
+                    struct timeval tp;
+                    gettimeofday(&tp, NULL);
+                    sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
+                }
+                else
+                {
+                    strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
+                }
+                nandroid_backup(backup_path);
+            }
+            break;
+        case 1:
+            show_nandroid_restore_menu("/sdcard");
+            break;
+        case 2:
+            show_nandroid_advanced_restore_menu("/sdcard");
+            break;
+        case 3:
+            {
+                char backup_path[PATH_MAX];
+                time_t t = time(NULL);
+                struct tm *tmp = localtime(&t);
+                if (tmp == NULL)
+                {
+                    struct timeval tp;
+                    gettimeofday(&tp, NULL);
+                    sprintf(backup_path, "/emmc/clockworkmod/backup/%d", tp.tv_sec);
+                }
+                else
+                {
+                    strftime(backup_path, sizeof(backup_path), "/emmc/clockworkmod/backup/%F.%H.%M.%S", tmp);
+                }
+                nandroid_backup(backup_path);
+            }
+            break;
+        case 4:
+            show_nandroid_restore_menu("/emmc");
+            break;
+        case 5:
+            show_nandroid_advanced_restore_menu("/emmc");
+            break;
+    }
+}
+
+void wipe_battery_stats()
+{
+    ensure_path_mounted("/data");
+    remove("/data/system/batterystats.bin");
+    ensure_path_unmounted("/data");
+    ui_print("Battery Stats wiped.\n");
+}
+
+void show_advanced_menu()
+{
+    static char* headers[] = {  "Advanced and Debugging Menu",
+                                "",
+                                NULL
+    };
+
+    static char* list[] = { "Reboot Recovery",
+                            "Wipe Dalvik Cache",
+                            "Wipe Battery Stats",
+                            "Report Error",
+                            "Key Test",
+                            "Show log",
+#ifndef BOARD_HAS_SMALL_RECOVERY
+                            "Partition SD Card",
+                            "Fix Permissions",
+#ifdef BOARD_HAS_SDCARD_INTERNAL
+                            "Partition Internal SD Card",
+#endif
+#endif
+                            NULL
+    };
+
+    for (;;)
+    {
+        int chosen_item = get_menu_selection(headers, list, 0, 0);
+        if (chosen_item == GO_BACK)
+            break;
+        switch (chosen_item)
+        {
+            case 0:
+            {
+                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+                break;
+            }
+            case 1:
+            {
+                if (0 != ensure_path_mounted("/data"))
+                    break;
+                ensure_path_mounted("/sd-ext");
+                ensure_path_mounted("/cache");
+                if (confirm_selection( "Confirm wipe?", "Yes - Wipe Dalvik Cache")) {
+                    __system("rm -r /data/dalvik-cache");
+                    __system("rm -r /cache/dalvik-cache");
+                    __system("rm -r /sd-ext/dalvik-cache");
+                    ui_print("Dalvik Cache wiped.\n");
+                }
+                ensure_path_unmounted("/data");
+                break;
+            }
+            case 2:
+            {
+                if (confirm_selection( "Confirm wipe?", "Yes - Wipe Battery Stats"))
+                    wipe_battery_stats();
+                break;
+            }
+            case 3:
+                handle_failure(1);
+                break;
+            case 4:
+            {
+                ui_print("Outputting key codes.\n");
+                ui_print("Go back to end debugging.\n");
+                int key;
+                int action;
+                do
+                {
+                    key = ui_wait_key();
+                    action = device_handle_key(key, 1);
+                    ui_print("Key: %d\n", key);
+                }
+                while (action != GO_BACK);
+                break;
+            }
+            case 5:
+            {
+                ui_printlogtail(12);
+                break;
+            }
+            case 6:
+            {
+                static char* ext_sizes[] = { "128M",
+                                             "256M",
+                                             "512M",
+                                             "1024M",
+                                             "2048M",
+                                             "4096M",
+                                             NULL };
+
+                static char* swap_sizes[] = { "0M",
+                                              "32M",
+                                              "64M",
+                                              "128M",
+                                              "256M",
+                                              NULL };
+
+                static char* ext_headers[] = { "Ext Size", "", NULL };
+                static char* swap_headers[] = { "Swap Size", "", NULL };
+
+                int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0);
+                if (ext_size == GO_BACK)
+                    continue;
+
+                int swap_size = get_menu_selection(swap_headers, swap_sizes, 0, 0);
+                if (swap_size == GO_BACK)
+                    continue;
+
+                char sddevice[256];
+                Volume *vol = volume_for_path("/sdcard");
+                strcpy(sddevice, vol->device);
+                // we only want the mmcblk, not the partition
+                sddevice[strlen("/dev/block/mmcblkX")] = NULL;
+                char cmd[PATH_MAX];
+                setenv("SDPATH", sddevice, 1);
+                sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]);
+                ui_print("Partitioning SD Card... please wait...\n");
+                if (0 == __system(cmd))
+                    ui_print("Done!\n");
+                else
+                    ui_print("An error occured while partitioning your SD Card. Please see /tmp/recovery.log for more details.\n");
+                break;
+            }
+            case 7:
+            {
+                ensure_path_mounted("/system");
+                ensure_path_mounted("/data");
+                ui_print("Fixing permissions...\n");
+                __system("fix_permissions");
+                ui_print("Done!\n");
+                break;
+            }
+            case 8:
+            {
+                static char* ext_sizes[] = { "128M",
+                                             "256M",
+                                             "512M",
+                                             "1024M",
+                                             "2048M",
+                                             "4096M",
+                                             NULL };
+
+                static char* swap_sizes[] = { "0M",
+                                              "32M",
+                                              "64M",
+                                              "128M",
+                                              "256M",
+                                              NULL };
+
+                static char* ext_headers[] = { "Data Size", "", NULL };
+                static char* swap_headers[] = { "Swap Size", "", NULL };
+
+                int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0);
+                if (ext_size == GO_BACK)
+                    continue;
+
+                int swap_size = 0;
+                if (swap_size == GO_BACK)
+                    continue;
+
+                char sddevice[256];
+                Volume *vol = volume_for_path("/emmc");
+                strcpy(sddevice, vol->device);
+                // we only want the mmcblk, not the partition
+                sddevice[strlen("/dev/block/mmcblkX")] = NULL;
+                char cmd[PATH_MAX];
+                setenv("SDPATH", sddevice, 1);
+                sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]);
+                ui_print("Partitioning Internal SD Card... please wait...\n");
+                if (0 == __system(cmd))
+                    ui_print("Done!\n");
+                else
+                    ui_print("An error occured while partitioning your Internal SD Card. Please see /tmp/recovery.log for more details.\n");
+                break;
+            }
+        }
+    }
+}
+
+void write_fstab_root(char *path, FILE *file)
+{
+    Volume *vol = volume_for_path(path);
+    if (vol == NULL) {
+        LOGW("Unable to get recovery.fstab info for %s during fstab generation!\n", path);
+        return;
+    }
+
+    char device[200];
+    if (vol->device[0] != '/')
+        get_partition_device(vol->device, device);
+    else
+        strcpy(device, vol->device);
+
+    fprintf(file, "%s ", device);
+    fprintf(file, "%s ", path);
+    // special case rfs cause auto will mount it as vfat on samsung.
+    fprintf(file, "%s rw\n", vol->fs_type2 != NULL && strcmp(vol->fs_type, "rfs") != 0 ? "auto" : vol->fs_type);
+}
+
+void create_fstab()
+{
+    struct stat info;
+    __system("touch /etc/mtab");
+    FILE *file = fopen("/etc/fstab", "w");
+    if (file == NULL) {
+        LOGW("Unable to create /etc/fstab!\n");
+        return;
+    }
+    Volume *vol = volume_for_path("/boot");
+    if (NULL != vol && strcmp(vol->fs_type, "mtd") != 0 && strcmp(vol->fs_type, "emmc") != 0 && strcmp(vol->fs_type, "bml") != 0)
+         write_fstab_root("/boot", file);
+    write_fstab_root("/cache", file);
+    write_fstab_root("/data", file);
+    write_fstab_root("/datadata", file);
+    write_fstab_root("/emmc", file);
+    write_fstab_root("/system", file);
+    write_fstab_root("/sdcard", file);
+    write_fstab_root("/sd-ext", file);
+    fclose(file);
+    LOGI("Completed outputting fstab.\n");
+}
+
+int bml_check_volume(const char *path) {
+    ui_print("Checking %s...\n", path);
+    ensure_path_unmounted(path);
+    if (0 == ensure_path_mounted(path)) {
+        ensure_path_unmounted(path);
+        return 0;
+    }
+    
+    Volume *vol = volume_for_path(path);
+    if (vol == NULL) {
+        LOGE("Unable process volume! Skipping...\n");
+        return 0;
+    }
+    
+    ui_print("%s may be rfs. Checking...\n", path);
+    char tmp[PATH_MAX];
+    sprintf(tmp, "mount -t rfs %s %s", vol->device, path);
+    int ret = __system(tmp);
+    printf("%d\n", ret);
+    return ret == 0 ? 1 : 0;
+}
+
+void process_volumes() {
+    create_fstab();
+
+    if (is_data_media()) {
+        setup_data_media();
+    }
+
+    return;
+
+    // dead code.
+    if (device_flash_type() != BML)
+        return;
+
+    ui_print("Checking for ext4 partitions...\n");
+    int ret = 0;
+    ret = bml_check_volume("/system");
+    ret |= bml_check_volume("/data");
+    if (has_datadata())
+        ret |= bml_check_volume("/datadata");
+    ret |= bml_check_volume("/cache");
+    
+    if (ret == 0) {
+        ui_print("Done!\n");
+        return;
+    }
+    
+    char backup_path[PATH_MAX];
+    time_t t = time(NULL);
+    char backup_name[PATH_MAX];
+    struct timeval tp;
+    gettimeofday(&tp, NULL);
+    sprintf(backup_name, "before-ext4-convert-%d", tp.tv_sec);
+    sprintf(backup_path, "/sdcard/clockworkmod/backup/%s", backup_name);
+
+    ui_set_show_text(1);
+    ui_print("Filesystems need to be converted to ext4.\n");
+    ui_print("A backup and restore will now take place.\n");
+    ui_print("If anything goes wrong, your backup will be\n");
+    ui_print("named %s. Try restoring it\n", backup_name);
+    ui_print("in case of error.\n");
+
+    nandroid_backup(backup_path);
+    nandroid_restore(backup_path, 1, 1, 1, 1, 1, 0);
+    ui_set_show_text(0);
+}
+
+void handle_failure(int ret)
+{
+    if (ret == 0)
+        return;
+    if (0 != ensure_path_mounted("/sdcard"))
+        return;
+    mkdir("/sdcard/clockworkmod", S_IRWXU);
+    __system("cp /tmp/recovery.log /sdcard/clockworkmod/recovery.log");
+    ui_print("/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.log. Please open ROM Manager to report the issue.\n");
+}
+
+int is_path_mounted(const char* path) {
+    Volume* v = volume_for_path(path);
+    if (v == NULL) {
+        return 0;
+    }
+    if (strcmp(v->fs_type, "ramdisk") == 0) {
+        // the ramdisk is always mounted.
+        return 1;
+    }
+
+    int result;
+    result = scan_mounted_volumes();
+    if (result < 0) {
+        LOGE("failed to scan mounted volumes\n");
+        return 0;
+    }
+
+    const MountedVolume* mv =
+        find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv) {
+        // volume is already mounted
+        return 1;
+    }
+    return 0;
+}
+
+int has_datadata() {
+    Volume *vol = volume_for_path("/datadata");
+    return vol != NULL;
+}
+
+int volume_main(int argc, char **argv) {
+    load_volume_table();
+    return 0;
+}
diff --git a/extendedcommands.h b/extendedcommands.h
new file mode 100644
index 0000000..53a0201
--- /dev/null
+++ b/extendedcommands.h
@@ -0,0 +1,62 @@
+extern int signature_check_enabled;
+extern int script_assert_enabled;
+
+void
+toggle_signature_check();
+
+void
+toggle_script_asserts();
+
+void
+show_choose_zip_menu();
+
+int
+do_nandroid_backup(const char* backup_name);
+
+int
+do_nandroid_restore();
+
+void
+show_nandroid_restore_menu(const char* path);
+
+void
+show_nandroid_advanced_restore_menu(const char* path);
+
+void
+show_nandroid_menu();
+
+void
+show_partition_menu();
+
+void
+show_choose_zip_menu();
+
+int
+install_zip(const char* packagefilepath);
+
+int
+__system(const char *command);
+
+void
+show_advanced_menu();
+
+int format_unknown_device(const char *device, const char* path, const char *fs_type);
+
+void
+wipe_battery_stats();
+
+void create_fstab();
+
+int has_datadata();
+
+void handle_failure(int ret);
+
+void process_volumes();
+
+int extendedcommand_file_exists();
+
+void show_install_update_menu();
+
+int confirm_selection(const char* title, const char* confirm);
+
+int run_and_remove_extendedcommand();
diff --git a/firmware.c b/firmware.c
new file mode 100644
index 0000000..4e5c24f
--- /dev/null
+++ b/firmware.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bootloader.h"
+#include "common.h"
+#include "firmware.h"
+#include "roots.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/reboot.h>
+
+static const char *update_type = NULL;
+static const char *update_data = NULL;
+static int update_length = 0;
+
+int remember_firmware_update(const char *type, const char *data, int length) {
+    if (update_type != NULL || update_data != NULL) {
+        LOGE("Multiple firmware images\n");
+        return -1;
+    }
+
+    update_type = type;
+    update_data = data;
+    update_length = length;
+    return 0;
+}
+
+// Return true if there is a firmware update pending.
+int firmware_update_pending() {
+  return update_data != NULL && update_length > 0;
+}
+
+/* Bootloader / Recovery Flow
+ *
+ * On every boot, the bootloader will read the bootloader_message
+ * from flash and check the command field.  The bootloader should
+ * deal with the command field not having a 0 terminator correctly
+ * (so as to not crash if the block is invalid or corrupt).
+ *
+ * The bootloader will have to publish the partition that contains
+ * the bootloader_message to the linux kernel so it can update it.
+ *
+ * if command == "boot-recovery" -> boot recovery.img
+ * else if command == "update-radio" -> update radio image (below)
+ * else if command == "update-hboot" -> update hboot image (below)
+ * else -> boot boot.img (normal boot)
+ *
+ * Radio/Hboot Update Flow
+ * 1. the bootloader will attempt to load and validate the header
+ * 2. if the header is invalid, status="invalid-update", goto #8
+ * 3. display the busy image on-screen
+ * 4. if the update image is invalid, status="invalid-radio-image", goto #8
+ * 5. attempt to update the firmware (depending on the command)
+ * 6. if successful, status="okay", goto #8
+ * 7. if failed, and the old image can still boot, status="failed-update"
+ * 8. write the bootloader_message, leaving the recovery field
+ *    unchanged, updating status, and setting command to
+ *    "boot-recovery"
+ * 9. reboot
+ *
+ * The bootloader will not modify or erase the cache partition.
+ * It is recovery's responsibility to clean up the mess afterwards.
+ */
+
+int maybe_install_firmware_update(const char *send_intent) {
+    if (update_data == NULL || update_length == 0) return 0;
+
+    /* We destroy the cache partition to pass the update image to the
+     * bootloader, so all we can really do afterwards is wipe cache and reboot.
+     * Set up this instruction now, in case we're interrupted while writing.
+     */
+
+    struct bootloader_message boot;
+    memset(&boot, 0, sizeof(boot));
+    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
+    strlcpy(boot.recovery, "recovery\n--wipe_cache\n", sizeof(boot.command));
+    if (send_intent != NULL) {
+        strlcat(boot.recovery, "--send_intent=", sizeof(boot.recovery));
+        strlcat(boot.recovery, send_intent, sizeof(boot.recovery));
+        strlcat(boot.recovery, "\n", sizeof(boot.recovery));
+    }
+    if (set_bootloader_message(&boot)) return -1;
+
+    int width = 0, height = 0, bpp = 0;
+    char *busy_image = ui_copy_image(
+        BACKGROUND_ICON_FIRMWARE_INSTALLING, &width, &height, &bpp);
+    char *fail_image = ui_copy_image(
+        BACKGROUND_ICON_FIRMWARE_ERROR, &width, &height, &bpp);
+
+    ui_print("Writing %s image...\n", update_type);
+    if (write_update_for_bootloader(
+            update_data, update_length,
+            width, height, bpp, busy_image, fail_image)) {
+        LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno));
+        format_volume("/cache");  // Attempt to clean cache up, at least.
+        return -1;
+    }
+
+    free(busy_image);
+    free(fail_image);
+
+    /* The update image is fully written, so now we can instruct the bootloader
+     * to install it.  (After doing so, it will come back here, and we will
+     * wipe the cache and reboot into the system.)
+     */
+    snprintf(boot.command, sizeof(boot.command), "update-%s", update_type);
+    if (set_bootloader_message(&boot)) {
+        format_volume("/cache");
+        return -1;
+    }
+
+    reboot(RB_AUTOBOOT);
+
+    // Can't reboot?  WTF?
+    LOGE("Can't reboot\n");
+    return -1;
+}
diff --git a/firmware.h b/firmware.h
new file mode 100644
index 0000000..aeb8f97
--- /dev/null
+++ b/firmware.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RECOVERY_FIRMWARE_H
+#define _RECOVERY_FIRMWARE_H
+
+/* Save a radio or bootloader update image for later installation.
+ * The type should be one of "hboot" or "radio".
+ * Takes ownership of type and data.  Returns nonzero on error.
+ */
+int remember_firmware_update(const char *type, const char *data, int length);
+
+/* Returns true if a firmware update has been saved. */
+int firmware_update_pending();
+
+/* If an update was saved, reboot into the bootloader now to install it.
+ * Returns 0 if no radio image was defined, nonzero on error,
+ * doesn't return at all on success...
+ */
+int maybe_install_firmware_update(const char *send_intent);
+
+#endif
diff --git a/flashutils/Android.mk b/flashutils/Android.mk
new file mode 100644
index 0000000..aa4c7d3
--- /dev/null
+++ b/flashutils/Android.mk
@@ -0,0 +1,106 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_ARCH),arm)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := flashutils.c
+LOCAL_MODULE := libflashutils
+LOCAL_MODULE_TAGS := eng
+LOCAL_C_INCLUDES += bootable/recovery
+LOCAL_STATIC_LIBRARIES := libmmcutils libmtdutils libbmlutils libcrecovery
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := flash_image.c
+LOCAL_MODULE := flash_image
+LOCAL_MODULE_TAGS := eng
+#LOCAL_STATIC_LIBRARIES += $(BOARD_FLASH_LIBRARY)
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils
+LOCAL_SHARED_LIBRARIES := libcutils libc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := dump_image.c
+LOCAL_MODULE := dump_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils
+LOCAL_SHARED_LIBRARIES := libcutils libc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := erase_image.c
+LOCAL_MODULE := erase_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils
+LOCAL_SHARED_LIBRARIES := libcutils libc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := flash_image.c
+LOCAL_MODULE := libflash_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_CFLAGS += -Dmain=flash_image_main
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := dump_image.c
+LOCAL_MODULE := libdump_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_CFLAGS += -Dmain=dump_image_main
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := erase_image.c
+LOCAL_MODULE := liberase_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_CFLAGS += -Dmain=erase_image_main
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := dump_image.c
+LOCAL_MODULE := utility_dump_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_STEM := dump_image
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := flash_image.c
+LOCAL_MODULE := utility_flash_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_STEM := flash_image
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := erase_image.c
+LOCAL_MODULE := utility_erase_image
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_STEM := erase_image
+LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+include $(BUILD_EXECUTABLE)
+
+endif	# TARGET_ARCH == arm
+endif	# !TARGET_SIMULATOR
diff --git a/flashutils/dump_image.c b/flashutils/dump_image.c
new file mode 100644
index 0000000..64c4e1c
--- /dev/null
+++ b/flashutils/dump_image.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "cutils/log.h"
+#include "flashutils.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#if 0
+
+#define LOG_TAG "dump_image"
+
+#define BLOCK_SIZE    2048
+#define SPARE_SIZE    (BLOCK_SIZE >> 5)
+
+static int die(const char *msg, ...) {
+    int err = errno;
+    va_list args;
+    va_start(args, msg);
+    char buf[1024];
+    vsnprintf(buf, sizeof(buf), msg, args);
+    va_end(args);
+
+    if (err != 0) {
+        strlcat(buf, ": ", sizeof(buf));
+        strlcat(buf, strerror(err), sizeof(buf));
+    }
+
+    fprintf(stderr, "%s\n", buf);
+    return 1;
+}
+
+/* Read a flash partition and write it to an image file. */
+
+int dump_image(char* partition_name, char* filename, dump_image_callback callback) {
+    MtdReadContext *in;
+    const MtdPartition *partition;
+    char buf[BLOCK_SIZE + SPARE_SIZE];
+    size_t partition_size;
+    size_t read_size;
+    size_t total;
+    int fd;
+    int wrote;
+    int len;
+    
+    if (mtd_scan_partitions() <= 0)
+        return die("error scanning partitions");
+
+    partition = mtd_find_partition_by_name(partition_name);
+    if (partition == NULL)
+        return die("can't find %s partition", partition_name);
+
+    if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
+        return die("can't get info of partition %s", partition_name);
+    }
+
+    if (!strcmp(filename, "-")) {
+        fd = fileno(stdout);
+    } 
+    else {
+        fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+    }
+
+    if (fd < 0)
+        return die("error opening %s", filename);
+
+    in = mtd_read_partition(partition);
+    if (in == NULL) {
+        close(fd);
+        unlink(filename);
+        return die("error opening %s: %s\n", partition_name, strerror(errno));
+    }
+
+    total = 0;
+    while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
+        wrote = write(fd, buf, len);
+        if (wrote != len) {
+            close(fd);
+            unlink(filename);
+            return die("error writing %s", filename);
+        }
+        total += BLOCK_SIZE;
+        if (callback != NULL)
+            callback(total, partition_size);
+    }
+
+    mtd_read_close(in);
+
+    if (close(fd)) {
+        unlink(filename);
+        return die("error closing %s", filename);
+    }
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    ssize_t (*read_func) (MtdReadContext *, char *, size_t);
+    MtdReadContext *in;
+    const MtdPartition *partition;
+    char buf[BLOCK_SIZE + SPARE_SIZE];
+    size_t partition_size;
+    size_t read_size;
+    size_t total;
+    int fd;
+    int wrote;
+    int len;
+
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
+        return 2;
+    }
+
+    return dump_image(argv[1], argv[2], NULL);
+}
+
+#endif
+
+int main(int argc, char **argv)
+{
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
+        return 2;
+    }
+
+    return backup_raw_partition(NULL, argv[1], argv[2]);
+}
diff --git a/flashutils/erase_image.c b/flashutils/erase_image.c
new file mode 100644
index 0000000..b09a424
--- /dev/null
+++ b/flashutils/erase_image.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * Portions Copyright (C) 2010 Magnus Eriksson <packetlss@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+
+#include "cutils/log.h"
+#include "flashutils.h"
+
+#if 0
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+
+#define LOG_TAG "erase_image"
+
+static int die(const char *msg, ...) {
+    int err = errno;
+    va_list args;
+    va_start(args, msg);
+    char buf[1024];
+    vsnprintf(buf, sizeof(buf), msg, args);
+    va_end(args);
+
+    if (err != 0) {
+        strlcat(buf, ": ", sizeof(buf));
+        strlcat(buf, strerror(err), sizeof(buf));
+    }
+
+    fprintf(stderr, "%s\n", buf);
+    LOGE("%s\n", buf);
+    return 3;
+}
+
+
+int erase_image(char* partition_name) {
+    MtdWriteContext *out;
+    size_t erased;
+    size_t total_size;
+    size_t erase_size;
+
+    if (mtd_scan_partitions() <= 0) die("error scanning partitions");
+    const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
+    if (partition == NULL) return die("can't find %s partition", partition_name);
+
+    out = mtd_write_partition(partition);
+    if (out == NULL) return die("could not estabilish write context for %s", partition_name);
+
+    // do the actual erase, -1 = full partition erase
+    erased = mtd_erase_blocks(out, -1);
+
+    // erased = bytes erased, if zero, something borked
+    if (!erased) return die("error erasing %s", partition_name);
+
+    return 0;
+}
+
+
+/* Erase a mtd partition */
+
+int main(int argc, char **argv) {
+    if (argc != 2) {
+        fprintf(stderr, "usage: %s <partition>\n", argv[0]);
+        return 2;
+    }
+    
+    return erase_image(argv[1]);
+}
+
+#endif
+
+
+int main(int argc, char **argv)
+{
+    if (argc != 2) {
+        fprintf(stderr, "usage: %s partition\n", argv[0]);
+        return 2;
+    }
+
+    return erase_raw_partition(NULL, argv[1]);
+}
diff --git a/mtdutils/flash_image.c b/flashutils/flash_image.c
similarity index 92%
rename from mtdutils/flash_image.c
rename to flashutils/flash_image.c
index c776876..c9f3683 100644
--- a/mtdutils/flash_image.c
+++ b/flashutils/flash_image.c
@@ -22,8 +22,9 @@
 #include <unistd.h>
 
 #include "cutils/log.h"
-#include "mtdutils.h"
+#include "flashutils.h"
 
+#if 0
 #define LOG_TAG "flash_image"
 
 #define HEADER_SIZE 2048  // size of header to compare for equality
@@ -138,3 +139,17 @@
     if (mtd_write_close(out)) die("error closing %s", argv[1]);
     return 0;
 }
+#endif
+
+int main(int argc, char **argv)
+{
+    if (argc != 3) {
+        fprintf(stderr, "usage: %s partition file.img\n", argv[0]);
+        return 2;
+    }
+
+    int ret = restore_raw_partition(NULL, argv[1], argv[2]);
+    if (ret != 0)
+        fprintf(stderr, "failed with error: %d\n", ret);
+    return ret;
+}
diff --git a/flashutils/flashutils.c b/flashutils/flashutils.c
new file mode 100644
index 0000000..de45e87
--- /dev/null
+++ b/flashutils/flashutils.c
@@ -0,0 +1,156 @@
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+
+#include "flashutils/flashutils.h"
+
+#ifndef BOARD_BML_BOOT
+#define BOARD_BML_BOOT              "/dev/block/bml7"
+#endif
+
+#ifndef BOARD_BML_RECOVERY
+#define BOARD_BML_RECOVERY          "/dev/block/bml8"
+#endif
+
+int the_flash_type = UNKNOWN;
+
+int device_flash_type()
+{
+    if (the_flash_type == UNKNOWN) {
+        if (access(BOARD_BML_BOOT, F_OK) == 0) {
+            the_flash_type = BML;
+        } else if (access("/proc/emmc", F_OK) == 0) {
+            the_flash_type = MMC;
+        } else if (access("/proc/mtd", F_OK) == 0) {
+            the_flash_type = MTD;
+        } else {
+            the_flash_type = UNSUPPORTED;
+        }
+    }
+    return the_flash_type;
+}
+
+char* get_default_filesystem()
+{
+    return device_flash_type() == MMC ? "ext3" : "yaffs2";
+}
+
+int get_flash_type(const char* partitionType) {
+    int type = UNSUPPORTED;
+    if (strcmp(partitionType, "mtd") == 0)
+        type = MTD;
+    else if (strcmp(partitionType, "emmc") == 0)
+        type = MMC;
+    else if (strcmp(partitionType, "bml") == 0)
+        type = BML;
+    return type;
+}
+
+static int detect_partition(const char *partitionType, const char *partition)
+{
+    int type = device_flash_type();
+    if (strstr(partition, "/dev/block/mtd") != NULL)
+        type = MTD;
+    else if (strstr(partition, "/dev/block/mmc") != NULL)
+        type = MMC;
+    else if (strstr(partition, "/dev/block/bml") != NULL)
+        type = BML;
+
+    if (partitionType != NULL) {
+        type = get_flash_type(partitionType);
+    }
+
+    return type;
+}
+int restore_raw_partition(const char* partitionType, const char *partition, const char *filename)
+{
+    int type = detect_partition(partitionType, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_restore_raw_partition(partition, filename);
+        case MMC:
+            return cmd_mmc_restore_raw_partition(partition, filename);
+        case BML:
+            return cmd_bml_restore_raw_partition(partition, filename);
+        default:
+            return -1;
+    }
+}
+
+int backup_raw_partition(const char* partitionType, const char *partition, const char *filename)
+{
+    int type = detect_partition(partitionType, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_backup_raw_partition(partition, filename);
+        case MMC:
+            return cmd_mmc_backup_raw_partition(partition, filename);
+        case BML:
+            return cmd_bml_backup_raw_partition(partition, filename);
+        default:
+            printf("unable to detect device type");
+            return -1;
+    }
+}
+
+int erase_raw_partition(const char* partitionType, const char *partition)
+{
+    int type = detect_partition(partitionType, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_erase_raw_partition(partition);
+        case MMC:
+            return cmd_mmc_erase_raw_partition(partition);
+        case BML:
+            return cmd_bml_erase_raw_partition(partition);
+        default:
+            return -1;
+    }
+}
+
+int erase_partition(const char *partition, const char *filesystem)
+{
+    int type = detect_partition(NULL, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_erase_partition(partition, filesystem);
+        case MMC:
+            return cmd_mmc_erase_partition(partition, filesystem);
+        case BML:
+            return cmd_bml_erase_partition(partition, filesystem);
+        default:
+            return -1;
+    }
+}
+
+int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
+{
+    int type = detect_partition(NULL, partition);
+    switch (type) {
+        case MTD:
+            return cmd_mtd_mount_partition(partition, mount_point, filesystem, read_only);
+        case MMC:
+            return cmd_mmc_mount_partition(partition, mount_point, filesystem, read_only);
+        case BML:
+            return cmd_bml_mount_partition(partition, mount_point, filesystem, read_only);
+        default:
+            return -1;
+    }
+}
+
+int get_partition_device(const char *partition, char *device)
+{
+    int type = device_flash_type();
+    switch (type) {
+        case MTD:
+            return cmd_mtd_get_partition_device(partition, device);
+        case MMC:
+            return cmd_mmc_get_partition_device(partition, device);
+        case BML:
+            return cmd_bml_get_partition_device(partition, device);
+        default:
+            return -1;
+    }
+}
diff --git a/flashutils/flashutils.h b/flashutils/flashutils.h
new file mode 100644
index 0000000..d112a31
--- /dev/null
+++ b/flashutils/flashutils.h
@@ -0,0 +1,50 @@
+#ifndef FLASHUTILS_H
+#define FLASHUTILS_H
+
+int restore_raw_partition(const char* partitionType, const char *partition, const char *filename);
+int backup_raw_partition(const char* partitionType, const char *partition, const char *filename);
+int erase_raw_partition(const char* partitionType, const char *partition);
+int erase_partition(const char *partition, const char *filesystem);
+int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
+int get_partition_device(const char *partition, char *device);
+
+#define FLASH_MTD 0
+#define FLASH_MMC 1
+#define FLASH_BML 2
+
+int is_mtd_device();
+char* get_default_filesystem();
+
+extern int cmd_mtd_restore_raw_partition(const char *partition, const char *filename);
+extern int cmd_mtd_backup_raw_partition(const char *partition, const char *filename);
+extern int cmd_mtd_erase_raw_partition(const char *partition);
+extern int cmd_mtd_erase_partition(const char *partition, const char *filesystem);
+extern int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
+extern int cmd_mtd_get_partition_device(const char *partition, char *device);
+
+extern int cmd_mmc_restore_raw_partition(const char *partition, const char *filename);
+extern int cmd_mmc_backup_raw_partition(const char *partition, const char *filename);
+extern int cmd_mmc_erase_raw_partition(const char *partition);
+extern int cmd_mmc_erase_partition(const char *partition, const char *filesystem);
+extern int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
+extern int cmd_mmc_get_partition_device(const char *partition, char *device);
+
+extern int cmd_bml_restore_raw_partition(const char *partition, const char *filename);
+extern int cmd_bml_backup_raw_partition(const char *partition, const char *filename);
+extern int cmd_bml_erase_raw_partition(const char *partition);
+extern int cmd_bml_erase_partition(const char *partition, const char *filesystem);
+extern int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only);
+extern int cmd_bml_get_partition_device(const char *partition, char *device);
+
+extern int device_flash_type();
+extern int get_flash_type(const char* fs_type);
+
+enum flash_type {
+    UNSUPPORTED = -1,
+    UNKNOWN = 0,
+    MTD = 1,
+    MMC = 2,
+    BML = 3
+};
+
+#endif
\ No newline at end of file
diff --git a/install.c b/install.c
index 707cda4..711f849 100644
--- a/install.c
+++ b/install.c
@@ -28,14 +28,81 @@
 #include "minui/minui.h"
 #include "minzip/SysUtil.h"
 #include "minzip/Zip.h"
-#include "mtdutils/mounts.h"
+#include "mounts.h"
 #include "mtdutils/mtdutils.h"
 #include "roots.h"
 #include "verifier.h"
 
+#include "firmware.h"
+
+#include "extendedcommands.h"
+
+
 #define ASSUMED_UPDATE_BINARY_NAME  "META-INF/com/google/android/update-binary"
+#define ASSUMED_UPDATE_SCRIPT_NAME  "META-INF/com/google/android/update-script"
 #define PUBLIC_KEYS_FILE "/res/keys"
 
+// The update binary ask us to install a firmware file on reboot.  Set
+// that up.  Takes ownership of type and filename.
+static int
+handle_firmware_update(char* type, char* filename, ZipArchive* zip) {
+    unsigned int data_size;
+    const ZipEntry* entry = NULL;
+
+    if (strncmp(filename, "PACKAGE:", 8) == 0) {
+        entry = mzFindZipEntry(zip, filename+8);
+        if (entry == NULL) {
+            LOGE("Failed to find \"%s\" in package", filename+8);
+            return INSTALL_ERROR;
+        }
+        data_size = entry->uncompLen;
+    } else {
+        struct stat st_data;
+        if (stat(filename, &st_data) < 0) {
+            LOGE("Error stat'ing %s: %s\n", filename, strerror(errno));
+            return INSTALL_ERROR;
+        }
+        data_size = st_data.st_size;
+    }
+
+    LOGI("type is %s; size is %d; file is %s\n",
+         type, data_size, filename);
+
+    char* data = malloc(data_size);
+    if (data == NULL) {
+        LOGI("Can't allocate %d bytes for firmware data\n", data_size);
+        return INSTALL_ERROR;
+    }
+
+    if (entry) {
+        if (mzReadZipEntry(zip, entry, data, data_size) == false) {
+            LOGE("Failed to read \"%s\" from package", filename+8);
+            return INSTALL_ERROR;
+        }
+    } else {
+        FILE* f = fopen(filename, "rb");
+        if (f == NULL) {
+            LOGE("Failed to open %s: %s\n", filename, strerror(errno));
+            return INSTALL_ERROR;
+        }
+        if (fread(data, 1, data_size, f) != data_size) {
+            LOGE("Failed to read firmware data: %s\n", strerror(errno));
+            return INSTALL_ERROR;
+        }
+        fclose(f);
+    }
+
+    if (remember_firmware_update(type, data, data_size)) {
+        LOGE("Can't store %s image\n", type);
+        free(data);
+        return INSTALL_ERROR;
+    }
+
+    free(filename);
+
+    return INSTALL_SUCCESS;
+}
+
 static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
 
 // If the package contains an update binary, extract it and run it.
@@ -44,8 +111,18 @@
     const ZipEntry* binary_entry =
             mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
     if (binary_entry == NULL) {
+        const ZipEntry* update_script_entry =
+                mzFindZipEntry(zip, ASSUMED_UPDATE_SCRIPT_NAME);
+        if (update_script_entry != NULL) {
+            ui_print("Amend scripting (update-script) is no longer supported.\n");
+            ui_print("Amend scripting was deprecated by Google in Android 1.5.\n");
+            ui_print("It was necessary to remove it when upgrading to the ClockworkMod 3.0 Gingerbread based recovery.\n");
+            ui_print("Please switch to Edify scripting (updater-script and update-binary) to create working update zip packages.\n");
+            return INSTALL_UPDATE_BINARY_MISSING;
+        }
+
         mzCloseZipArchive(zip);
-        return INSTALL_CORRUPT;
+        return INSTALL_UPDATE_BINARY_MISSING;
     }
 
     char* binary = "/tmp/update_binary";
@@ -58,10 +135,10 @@
     }
     bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);
     close(fd);
-    mzCloseZipArchive(zip);
 
     if (!ok) {
         LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);
+        mzCloseZipArchive(zip);
         return 1;
     }
 
@@ -112,6 +189,7 @@
 
     pid_t pid = fork();
     if (pid == 0) {
+        setenv("UPDATE_PACKAGE", path, 1);
         close(pipefd[0]);
         execv(binary, args);
         fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
@@ -119,6 +197,9 @@
     }
     close(pipefd[1]);
 
+    char* firmware_type = NULL;
+    char* firmware_filename = NULL;
+
     char buffer[1024];
     FILE* from_child = fdopen(pipefd[0], "r");
     while (fgets(buffer, sizeof(buffer), from_child) != NULL) {
@@ -138,6 +219,18 @@
             char* fraction_s = strtok(NULL, " \n");
             float fraction = strtof(fraction_s, NULL);
             ui_set_progress(fraction);
+        } else if (strcmp(command, "firmware") == 0) {
+            char* type = strtok(NULL, " \n");
+            char* filename = strtok(NULL, " \n");
+
+            if (type != NULL && filename != NULL) {
+                if (firmware_type != NULL) {
+                    LOGE("ignoring attempt to do multiple firmware updates");
+                } else {
+                    firmware_type = strdup(type);
+                    firmware_filename = strdup(filename);
+                }
+            }
         } else if (strcmp(command, "ui_print") == 0) {
             char* str = strtok(NULL, "\n");
             if (str) {
@@ -155,9 +248,15 @@
     waitpid(pid, &status, 0);
     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
         LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
+        mzCloseZipArchive(zip);
         return INSTALL_ERROR;
     }
 
+    if (firmware_type != NULL) {
+        int ret = handle_firmware_update(firmware_type, firmware_filename, zip);
+        mzCloseZipArchive(zip);
+        return ret;
+    }
     return INSTALL_SUCCESS;
 }
 
@@ -250,27 +349,30 @@
 
     ui_print("Opening update package...\n");
 
-    int numKeys;
-    RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
-    if (loadedKeys == NULL) {
-        LOGE("Failed to load keys\n");
-        return INSTALL_CORRUPT;
-    }
-    LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
-
-    // Give verification half the progress bar...
-    ui_print("Verifying update package...\n");
-    ui_show_progress(
-            VERIFICATION_PROGRESS_FRACTION,
-            VERIFICATION_PROGRESS_TIME);
-
     int err;
-    err = verify_file(path, loadedKeys, numKeys);
-    free(loadedKeys);
-    LOGI("verify_file returned %d\n", err);
-    if (err != VERIFY_SUCCESS) {
-        LOGE("signature verification failed\n");
-        return INSTALL_CORRUPT;
+
+    if (signature_check_enabled) {
+        int numKeys;
+        RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
+        if (loadedKeys == NULL) {
+            LOGE("Failed to load keys\n");
+            return INSTALL_CORRUPT;
+        }
+        LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
+
+        // Give verification half the progress bar...
+        ui_print("Verifying update package...\n");
+        ui_show_progress(
+                VERIFICATION_PROGRESS_FRACTION,
+                VERIFICATION_PROGRESS_TIME);
+
+        err = verify_file(path, loadedKeys, numKeys);
+        free(loadedKeys);
+        LOGI("verify_file returned %d\n", err);
+        if (err != VERIFY_SUCCESS) {
+            LOGE("signature verification failed\n");
+            return INSTALL_CORRUPT;
+        }
     }
 
     /* Try to open the package.
diff --git a/install.h b/install.h
index a7ebc09..ec97d39 100644
--- a/install.h
+++ b/install.h
@@ -19,7 +19,7 @@
 
 #include "common.h"
 
-enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT };
+enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_UPDATE_SCRIPT_MISSING, INSTALL_UPDATE_BINARY_MISSING };
 int install_package(const char *root_path);
 
 #endif  // RECOVERY_INSTALL_H_
diff --git a/killrecovery.sh b/killrecovery.sh
new file mode 100755
index 0000000..352cb4b
--- /dev/null
+++ b/killrecovery.sh
@@ -0,0 +1,22 @@
+#!/sbin/sh
+mkdir -p /sd-ext
+rm /cache/recovery/command
+rm /cache/update.zip
+touch /tmp/.ignorebootmessage
+kill $(ps | grep /sbin/adbd)
+kill $(ps | grep /sbin/recovery)
+
+# On the Galaxy S, the recovery comes test signed, but the
+# recovery is not automatically restarted.
+if [ -f /init.smdkc110.rc ]
+then
+    /sbin/recovery &
+fi
+
+# Droid X
+if [ -f /init.mapphone_cdma.rc ]
+then
+    /sbin/recovery &
+fi
+
+exit 1
diff --git a/libcrecovery/Android.mk b/libcrecovery/Android.mk
new file mode 100644
index 0000000..3d28d97
--- /dev/null
+++ b/libcrecovery/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_ARCH),arm)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := system.c popen.c
+LOCAL_MODULE := libcrecovery
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_STATIC_LIBRARY)
+
+endif
+endif
diff --git a/libcrecovery/common.h b/libcrecovery/common.h
new file mode 100644
index 0000000..59af22e
--- /dev/null
+++ b/libcrecovery/common.h
@@ -0,0 +1,10 @@
+#ifndef LIBCRECOVERY_COMMON_H
+#define LIBCRECOVERY_COMMON_H
+
+#include <stdio.h>
+
+int __system(const char *command);
+FILE * __popen(const char *program, const char *type);
+int __pclose(FILE *iop);
+
+#endif
\ No newline at end of file
diff --git a/libcrecovery/defines.h b/libcrecovery/defines.h
new file mode 100644
index 0000000..d94ad2d
--- /dev/null
+++ b/libcrecovery/defines.h
@@ -0,0 +1,2 @@
+#undef _PATH_BSHELL
+#define _PATH_BSHELL "/sbin/sh"
diff --git a/libcrecovery/popen.c b/libcrecovery/popen.c
new file mode 100644
index 0000000..73d3c74
--- /dev/null
+++ b/libcrecovery/popen.c
@@ -0,0 +1,169 @@
+/*	$OpenBSD: popen.c,v 1.17 2005/08/08 08:05:34 espie Exp $ */
+/*
+ * Copyright (c) 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <paths.h>
+
+#include "defines.h"
+
+static struct pid {
+	struct pid *next;
+	FILE *fp;
+	pid_t pid;
+} *pidlist;
+
+FILE *
+__popen(const char *program, const char *type)
+{
+	struct pid * volatile cur;
+	FILE *iop;
+	int pdes[2];
+	pid_t pid;
+
+	if ((*type != 'r' && *type != 'w') || type[1] != '\0') {
+		errno = EINVAL;
+		return (NULL);
+	}
+
+	if ((cur = malloc(sizeof(struct pid))) == NULL)
+		return (NULL);
+
+	if (pipe(pdes) < 0) {
+		free(cur);
+		return (NULL);
+	}
+
+	switch (pid = vfork()) {
+	case -1:			/* Error. */
+		(void)close(pdes[0]);
+		(void)close(pdes[1]);
+		free(cur);
+		return (NULL);
+		/* NOTREACHED */
+	case 0:				/* Child. */
+	    {
+		struct pid *pcur;
+		/*
+		 * because vfork() instead of fork(), must leak FILE *,
+		 * but luckily we are terminally headed for an execl()
+		 */
+		for (pcur = pidlist; pcur; pcur = pcur->next)
+			close(fileno(pcur->fp));
+
+		if (*type == 'r') {
+			int tpdes1 = pdes[1];
+
+			(void) close(pdes[0]);
+			/*
+			 * We must NOT modify pdes, due to the
+			 * semantics of vfork.
+			 */
+			if (tpdes1 != STDOUT_FILENO) {
+				(void)dup2(tpdes1, STDOUT_FILENO);
+				(void)close(tpdes1);
+				tpdes1 = STDOUT_FILENO;
+			}
+		} else {
+			(void)close(pdes[1]);
+			if (pdes[0] != STDIN_FILENO) {
+				(void)dup2(pdes[0], STDIN_FILENO);
+				(void)close(pdes[0]);
+			}
+		}
+		execl(_PATH_BSHELL, "sh", "-c", program, (char *)NULL);
+		_exit(127);
+		/* NOTREACHED */
+	    }
+	}
+
+	/* Parent; assume fdopen can't fail. */
+	if (*type == 'r') {
+		iop = fdopen(pdes[0], type);
+		(void)close(pdes[1]);
+	} else {
+		iop = fdopen(pdes[1], type);
+		(void)close(pdes[0]);
+	}
+
+	/* Link into list of file descriptors. */
+	cur->fp = iop;
+	cur->pid =  pid;
+	cur->next = pidlist;
+	pidlist = cur;
+
+	return (iop);
+}
+
+/*
+ * pclose --
+ *	Pclose returns -1 if stream is not associated with a `popened' command,
+ *	if already `pclosed', or waitpid returns an error.
+ */
+int
+__pclose(FILE *iop)
+{
+	struct pid *cur, *last;
+	int pstat;
+	pid_t pid;
+
+	/* Find the appropriate file pointer. */
+	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
+		if (cur->fp == iop)
+			break;
+
+	if (cur == NULL)
+		return (-1);
+
+	(void)fclose(iop);
+
+	do {
+		pid = waitpid(cur->pid, &pstat, 0);
+	} while (pid == -1 && errno == EINTR);
+
+	/* Remove the entry from the linked list. */
+	if (last == NULL)
+		pidlist = cur->next;
+	else
+		last->next = cur->next;
+	free(cur);
+
+	return (pid == -1 ? -1 : pstat);
+}
diff --git a/libcrecovery/system.c b/libcrecovery/system.c
new file mode 100644
index 0000000..6d78ae9
--- /dev/null
+++ b/libcrecovery/system.c
@@ -0,0 +1,76 @@
+/*	$OpenBSD: system.c,v 1.8 2005/08/08 08:05:37 espie Exp $ */
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <paths.h>
+#include <sys/wait.h>
+
+#include "defines.h"
+
+extern char **environ;
+
+int
+__system(const char *command)
+{
+  pid_t pid;
+	sig_t intsave, quitsave;
+	sigset_t mask, omask;
+	int pstat;
+	char *argp[] = {"sh", "-c", NULL, NULL};
+
+	if (!command)		/* just checking... */
+		return(1);
+
+	argp[2] = (char *)command;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGCHLD);
+	sigprocmask(SIG_BLOCK, &mask, &omask);
+	switch (pid = vfork()) {
+	case -1:			/* error */
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+		return(-1);
+	case 0:				/* child */
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+		execve(_PATH_BSHELL, argp, environ);
+    _exit(127);
+  }
+
+	intsave = (sig_t)  bsd_signal(SIGINT, SIG_IGN);
+	quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN);
+	pid = waitpid(pid, (int *)&pstat, 0);
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+	(void)bsd_signal(SIGINT, intsave);
+	(void)bsd_signal(SIGQUIT, quitsave);
+	return (pid == -1 ? -1 : pstat);
+}
diff --git a/minui/font_7x16.h b/minui/font_7x16.h
new file mode 100644
index 0000000..31c94fc
--- /dev/null
+++ b/minui/font_7x16.h
@@ -0,0 +1,15 @@
+struct {
+  unsigned width;
+  unsigned height;
+  unsigned cwidth;
+  unsigned cheight;
+  unsigned char rundata[];
+} font = {
+  .width = 668,
+  .height = 16,
+  .cwidth = 7,
+  .cheight = 16,
+  .rundata = {
+0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7e,0x82,0x03,0x82,0x7f,0x7f,0x5f,0x82,0x0b,0x82,0x14,0x81,0x0b,0x81,0x11,0x81,0x0c,0x82,0x09,0x81,0x08,0x81,0x07,0x81,0x03,0x81,0x06,0x83,0x68,0x83,0x04,0x81,0x04,0x83,0x17,0x81,0x05,0x81,0x01,0x81,0x0c,0x81,0x04,0x82,0x07,0x83,0x04,0x81,0x07,0x81,0x05,0x81,0x06,0x81,0x25,0x81,0x02,0x84,0x02,0x83,0x05,0x84,0x03,0x84,0x05,0x82,0x02,0x85,0x04,0x83,0x02,0x86,0x02,0x84,0x03,0x84,0x27,0x83,0x0b,0x82,0x03,0x85,0x04,0x83,0x02,0x84,0x03,0x86,0x01,0x86,0x03,0x83,0x02,0x81,0x04,0x81,0x01,0x85,0x04,0x83,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x82,0x03,0x81,0x02,0x84,0x02,0x85,0x03,0x84,0x02,0x85,0x03,0x84,0x01,0x87,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x05,0x81,0x01,0x81,0x04,0x82,0x05,0x81,0x01,0x86,0x03,0x81,0x04,0x81,0x08,0x81,0x05,0x82,0x0e,0x81,0x0a,0x81,0x11,0x81,0x0b,0x81,0x0b,0x81,0x14,0x81,0x08,0x81,0x37,0x81,0x30,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x05,0x81,0x01,0x81,0x05,0x81,0x01,0x81,0x03,0x83,0x02,0x81,0x02,0x81,0x05,0x81,0x07,0x81,0x07,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x22,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x04,0x82,0x02,0x81,0x07,0x81,0x03,0x81,0x05,0x82,0x01,0x81,0x04,0x81,0x01,0x82,0x02,0x81,0x26,0x81,0x03,0x81,0x03,0x83,0x04,0x82,0x03,0x81,0x04,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x82,0x02,0x82,0x01,0x82,0x03,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x82,0x03,0x81,0x05,0x81,0x07,0x81,0x04,0x81,0x02,0x81,0x18,0x81,0x11,0x81,0x0b,0x81,0x0b,0x81,0x14,0x81,0x08,0x81,0x37,0x81,0x30,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x05,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x05,0x81,0x07,0x81,0x06,0x81,0x07,0x81,0x04,0x83,0x05,0x81,0x1d,0x81,0x02,0x81,0x04,0x81,0x03,0x81,0x09,0x81,0x06,0x81,0x03,0x81,0x01,0x81,0x02,0x81,0x06,0x81,0x0a,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x14,0x81,0x08,0x81,0x0b,0x81,0x02,0x81,0x02,0x82,0x03,0x82,0x03,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x82,0x02,0x82,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x08,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x07,0x81,0x04,0x81,0x05,0x81,0x07,0x81,0x03,0x81,0x04,0x81,0x11,0x83,0x03,0x84,0x04,0x83,0x04,0x84,0x03,0x83,0x03,0x85,0x03,0x84,0x02,0x81,0x01,0x82,0x03,0x83,0x05,0x83,0x03,0x81,0x03,0x81,0x04,0x81,0x04,0x85,0x02,0x81,0x01,0x82,0x04,0x83,0x03,0x84,0x04,0x84,0x03,0x84,0x03,0x83,0x03,0x85,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x05,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x85,0x04,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x86,0x01,0x81,0x01,0x81,0x04,0x82,0x02,0x81,0x03,0x82,0x0d,0x81,0x07,0x81,0x04,0x83,0x05,0x81,0x1c,0x81,0x03,0x81,0x04,0x81,0x03,0x81,0x09,0x81,0x06,0x81,0x02,0x82,0x01,0x81,0x02,0x85,0x02,0x81,0x01,0x83,0x06,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x06,0x81,0x06,0x83,0x0a,0x83,0x06,0x82,0x02,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x82,0x07,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x04,0x81,0x01,0x81,0x06,0x81,0x05,0x81,0x06,0x81,0x06,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x82,0x05,0x81,0x03,0x81,0x02,0x82,0x02,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x82,0x02,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x02,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x82,0x02,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x82,0x02,0x81,0x01,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x05,0x81,0x02,0x81,0x01,0x81,0x03,0x81,0x03,0x81,0x06,0x81,0x04,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0c,0x81,0x01,0x81,0x03,0x83,0x06,0x82,0x04,0x82,0x0d,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x1c,0x81,0x03,0x81,0x02,0x81,0x01,0x81,0x03,0x81,0x08,0x81,0x04,0x83,0x03,0x81,0x02,0x81,0x06,0x82,0x01,0x82,0x02,0x82,0x04,0x81,0x04,0x84,0x02,0x81,0x03,0x82,0x03,0x81,0x06,0x81,0x04,0x82,0x05,0x86,0x05,0x82,0x03,0x82,0x03,0x81,0x02,0x83,0x02,0x81,0x02,0x81,0x02,0x85,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x86,0x01,0x86,0x01,0x81,0x03,0x82,0x01,0x86,0x03,0x81,0x08,0x81,0x02,0x83,0x04,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x85,0x02,0x81,0x04,0x81,0x01,0x85,0x03,0x84,0x04,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x05,0x81,0x06,0x82,0x05,0x81,0x06,0x81,0x06,0x81,0x1d,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x05,0x81,0x08,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x03,0x82,0x07,0x81,0x07,0x82,0x02,0x83,0x10,0x81,0x0c,0x81,0x01,0x81,0x05,0x83,0x02,0x82,0x01,0x82,0x02,0x81,0x02,0x81,0x01,0x81,0x0a,0x81,0x07,0x81,0x05,0x81,0x03,0x87,0x09,0x83,0x0c,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x07,0x81,0x08,0x81,0x01,0x81,0x03,0x81,0x07,0x81,0x01,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x02,0x83,0x01,0x81,0x0f,0x82,0x10,0x82,0x03,0x81,0x04,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x03,0x81,0x07,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x05,0x81,0x06,0x81,0x06,0x81,0x07,0x81,0x05,0x81,0x1a,0x84,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x85,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x82,0x07,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x06,0x83,0x05,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x05,0x81,0x01,0x81,0x05,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x07,0x83,0x18,0x86,0x04,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x01,0x81,0x02,0x83,0x0a,0x81,0x07,0x81,0x0c,0x81,0x1b,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x06,0x81,0x09,0x81,0x01,0x86,0x06,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x06,0x81,0x11,0x83,0x02,0x86,0x02,0x83,0x0a,0x81,0x01,0x81,0x02,0x81,0x02,0x84,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x82,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x06,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x82,0x03,0x82,0x01,0x82,0x03,0x81,0x02,0x81,0x04,0x81,0x05,0x81,0x07,0x81,0x07,0x81,0x05,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x81,0x08,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x09,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x82,0x01,0x82,0x03,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x07,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x01,0x82,0x02,0x81,0x0c,0x81,0x05,0x81,0x0d,0x81,0x06,0x81,0x0d,0x81,0x05,0x81,0x06,0x81,0x02,0x81,0x04,0x81,0x05,0x81,0x05,0x81,0x04,0x81,0x05,0x81,0x02,0x81,0x03,0x82,0x02,0x81,0x02,0x82,0x03,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x03,0x81,0x04,0x81,0x06,0x81,0x09,0x81,0x08,0x81,0x08,0x81,0x04,0x81,0x02,0x83,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x03,0x82,0x02,0x81,0x02,0x81,0x02,0x81,0x07,0x81,0x02,0x82,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x82,0x03,0x81,0x03,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x04,0x82,0x07,0x81,0x08,0x81,0x04,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x82,0x05,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x02,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x05,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x05,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x04,0x81,0x08,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x81,0x01,0x81,0x05,0x83,0x06,0x82,0x03,0x83,0x01,0x81,0x0b,0x81,0x05,0x81,0x0d,0x81,0x06,0x81,0x0d,0x81,0x05,0x81,0x06,0x84,0x02,0x85,0x02,0x86,0x02,0x84,0x06,0x81,0x03,0x84,0x03,0x84,0x03,0x81,0x06,0x84,0x03,0x83,0x05,0x81,0x06,0x81,0x1b,0x81,0x04,0x82,0x05,0x81,0x04,0x81,0x01,0x85,0x04,0x83,0x02,0x84,0x03,0x86,0x01,0x81,0x08,0x83,0x02,0x81,0x04,0x81,0x01,0x85,0x03,0x83,0x03,0x81,0x04,0x81,0x01,0x86,0x01,0x81,0x04,0x81,0x01,0x81,0x03,0x82,0x02,0x84,0x02,0x81,0x07,0x84,0x02,0x81,0x05,0x81,0x01,0x84,0x04,0x81,0x05,0x84,0x04,0x82,0x03,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x03,0x81,0x04,0x86,0x03,0x81,0x08,0x81,0x04,0x81,0x1a,0x84,0x02,0x84,0x04,0x83,0x04,0x84,0x03,0x83,0x05,0x81,0x05,0x84,0x02,0x81,0x03,0x81,0x02,0x85,0x05,0x81,0x03,0x81,0x03,0x81,0x05,0x82,0x02,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x03,0x83,0x03,0x84,0x04,0x84,0x03,0x81,0x06,0x83,0x05,0x83,0x03,0x84,0x04,0x81,0x05,0x81,0x01,0x81,0x03,0x81,0x03,0x81,0x04,0x81,0x04,0x85,0x04,0x81,0x06,0x81,0x06,0x81,0x2c,0x81,0x1c,0x82,0x03,0x82,0x13,0x81,0x13,0x81,0x54,0x81,0x22,0x81,0x79,0x81,0x43,0x82,0x08,0x81,0x02,0x82,0x47,0x81,0x13,0x81,0x26,0x81,0x0a,0x81,0x35,0x81,0x0d,0x83,0x04,0x81,0x04,0x83,0x2c,0x81,0x7f,0x44,0x83,0x76,0x81,0x7f,0x17,0x81,0x02,0x81,0x13,0x81,0x26,0x81,0x0a,0x81,0x34,0x81,0x15,0x81,0x7f,0x7f,0x7f,0x50,0x87,0x34,0x82,0x12,0x82,0x27,0x81,0x0a,0x81,0x33,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x4b,0x00,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x00,0x49,0x44,0x41,0x54,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x50,0x4c,0x54,0x45,0x00,0x00,0x00,0x00,0x62,0x4b,0x47,0x44,0x00,0x00,0x00,0x00,0x63,0x48,0x52,0x4d,0x00,0x00,0x00,0x00,0x67,0x41,0x4d,0x41,0x00,0x00,0x00,0x00,0x68,0x49,0x53,0x54,0x00,0x00,0x00,0x00,0x69,0x43,0x43,0x50,0x00,0x00,0x00,0x00,0x69,0x54,0x58,0x74,0x00,0x00,0x00,0x00,0x6f,0x46,0x46,0x73,0x00,0x00,0x00,0x00,0x70,0x43,0x41,0x4c,0x00,0x00,0x00,0x00,0x73,0x43,0x41,0x4c,0x00,0x00,0x00,0x00,0x70,0x48,0x59,0x73,0x00,0x00,0x00,0x00,0x73,0x42,0x49,0x54,0x00,0x00,0x00,0x00,0x73,0x50,0x4c,0x54,0x00,0x00,0x00,0x00,0x73,0x52,0x47,0x42,0x00,0x00,0x00,0x00,0x74,0x45,0x58,0x74,0x00,0x00,0x00,0x00,0x74,0x49,0x4d,0x45,0x00,0x00,0x00,0x00,0x74,0x52,0x4e,0x53,0x00,0x00,0x00,0x00,0x7a,0x54,0x58,0x74,0x00, 
+  }
+};
diff --git a/mmcutils/Android.mk b/mmcutils/Android.mk
new file mode 100644
index 0000000..0046dc9
--- /dev/null
+++ b/mmcutils/Android.mk
@@ -0,0 +1,20 @@
+ifneq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_ARCH),arm)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+ifeq ($(BOARD_HAS_LARGE_FILESYSTEM),true)
+LOCAL_CFLAGS += -DBOARD_HAS_LARGE_FILESYSTEM
+endif
+
+LOCAL_SRC_FILES := \
+	mmcutils.c
+
+LOCAL_MODULE := libmmcutils
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_STATIC_LIBRARY)
+
+endif	# TARGET_ARCH == arm
+endif	# !TARGET_SIMULATOR
diff --git a/mmcutils/mmcutils.c b/mmcutils/mmcutils.c
new file mode 100644
index 0000000..492dadc
--- /dev/null
+++ b/mmcutils/mmcutils.c
@@ -0,0 +1,655 @@
+/*
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mount.h>  // for _IOW, _IOR, mount()
+
+#include "mmcutils.h"
+
+unsigned ext3_count = 0;
+char *ext3_partitions[] = {"system", "userdata", "cache", "NONE"};
+
+unsigned vfat_count = 0;
+char *vfat_partitions[] = {"modem", "NONE"};
+
+struct MmcPartition {
+    char *device_index;
+    char *filesystem;
+    char *name;
+    unsigned dstatus;
+    unsigned dtype ;
+    unsigned dfirstsec;
+    unsigned dsize;
+};
+
+typedef struct {
+    MmcPartition *partitions;
+    int partitions_allocd;
+    int partition_count;
+} MmcState;
+
+static MmcState g_mmc_state = {
+    NULL,   // partitions
+    0,      // partitions_allocd
+    -1      // partition_count
+};
+
+#define MMC_DEVICENAME "/dev/block/mmcblk0"
+
+static void
+mmc_partition_name (MmcPartition *mbr, unsigned int type) {
+    switch(type)
+    {
+        char name[64];
+        case MMC_BOOT_TYPE:
+            sprintf(name,"boot");
+            mbr->name = strdup(name);
+            break;
+        case MMC_RECOVERY_TYPE:
+            sprintf(name,"recovery");
+            mbr->name = strdup(name);
+            break;
+        case MMC_EXT3_TYPE:
+            if (strcmp("NONE", ext3_partitions[ext3_count])) {
+                strcpy((char *)name,(const char *)ext3_partitions[ext3_count]);
+                mbr->name = strdup(name);
+                ext3_count++;
+            }
+            mbr->filesystem = strdup("ext3");
+            break;
+        case MMC_VFAT_TYPE:
+            if (strcmp("NONE", vfat_partitions[vfat_count])) {
+                strcpy((char *)name,(const char *)vfat_partitions[vfat_count]);
+                mbr->name = strdup(name);
+                vfat_count++;
+            }
+            mbr->filesystem = strdup("vfat");
+            break;
+    };
+}
+
+static int
+mmc_read_mbr (const char *device, MmcPartition *mbr) {
+    FILE *fd;
+    unsigned char buffer[512];
+    int idx, i;
+    unsigned mmc_partition_count = 0;
+    unsigned int dtype;
+    unsigned int dfirstsec;
+    unsigned int EBR_first_sec;
+    unsigned int EBR_current_sec;
+    int ret = -1;
+
+    fd = fopen(device, "r");
+    if(fd == NULL)
+    {
+        printf("Can't open device: \"%s\"\n", device);
+        goto ERROR2;
+    }
+    if ((fread(buffer, 512, 1, fd)) != 1)
+    {
+        printf("Can't read device: \"%s\"\n", device);
+        goto ERROR1;
+    }
+    /* Check to see if signature exists */
+    if ((buffer[TABLE_SIGNATURE] != 0x55) || \
+        (buffer[TABLE_SIGNATURE + 1] != 0xAA))
+    {
+        printf("Incorrect mbr signatures!\n");
+        goto ERROR1;
+    }
+    idx = TABLE_ENTRY_0;
+    for (i = 0; i < 4; i++)
+    {
+        char device_index[128];
+
+        mbr[mmc_partition_count].dstatus = \
+                    buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS];
+        mbr[mmc_partition_count].dtype   = \
+                    buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE];
+        mbr[mmc_partition_count].dfirstsec = \
+                    GET_LWORD_FROM_BYTE(&buffer[idx + \
+                                        i * TABLE_ENTRY_SIZE + \
+                                        OFFSET_FIRST_SEC]);
+        mbr[mmc_partition_count].dsize  = \
+                    GET_LWORD_FROM_BYTE(&buffer[idx + \
+                                        i * TABLE_ENTRY_SIZE + \
+                                        OFFSET_SIZE]);
+        dtype  = mbr[mmc_partition_count].dtype;
+        dfirstsec = mbr[mmc_partition_count].dfirstsec;
+        mmc_partition_name(&mbr[mmc_partition_count], \
+                        mbr[mmc_partition_count].dtype);
+
+        sprintf(device_index, "%sp%d", device, (mmc_partition_count+1));
+        mbr[mmc_partition_count].device_index = strdup(device_index);
+
+        mmc_partition_count++;
+        if (mmc_partition_count == MAX_PARTITIONS)
+            goto SUCCESS;
+    }
+
+    /* See if the last partition is EBR, if not, parsing is done */
+    if (dtype != 0x05)
+    {
+        goto SUCCESS;
+    }
+
+    EBR_first_sec = dfirstsec;
+    EBR_current_sec = dfirstsec;
+
+    fseek (fd, (EBR_first_sec * 512), SEEK_SET);
+    if ((fread(buffer, 512, 1, fd)) != 1)
+        goto ERROR1;
+
+    /* Loop to parse the EBR */
+    for (i = 0;; i++)
+    {
+        char device_index[128];
+
+        if ((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE + 1] != 0xAA))
+        {
+            break;
+        }
+        mbr[mmc_partition_count].dstatus = \
+                    buffer[TABLE_ENTRY_0 + OFFSET_STATUS];
+        mbr[mmc_partition_count].dtype   = \
+                    buffer[TABLE_ENTRY_0 + OFFSET_TYPE];
+        mbr[mmc_partition_count].dfirstsec = \
+                    GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
+                                        OFFSET_FIRST_SEC])    + \
+                                        EBR_current_sec;
+        mbr[mmc_partition_count].dsize = \
+                    GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \
+                                        OFFSET_SIZE]);
+        mmc_partition_name(&mbr[mmc_partition_count], \
+                        mbr[mmc_partition_count].dtype);
+
+        sprintf(device_index, "%sp%d", device, (mmc_partition_count+1));
+        mbr[mmc_partition_count].device_index = strdup(device_index);
+
+        mmc_partition_count++;
+        if (mmc_partition_count == MAX_PARTITIONS)
+            goto SUCCESS;
+
+        dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]);
+        if(dfirstsec == 0)
+        {
+            /* Getting to the end of the EBR tables */
+            break;
+        }
+        /* More EBR to follow - read in the next EBR sector */
+        fseek (fd,  ((EBR_first_sec + dfirstsec) * 512), SEEK_SET);
+        if ((fread(buffer, 512, 1, fd)) != 1)
+            goto ERROR1;
+
+        EBR_current_sec = EBR_first_sec + dfirstsec;
+    }
+
+SUCCESS:
+    ret = mmc_partition_count;
+ERROR1:
+    fclose(fd);
+ERROR2:
+    return ret;
+}
+
+int
+mmc_scan_partitions() {
+    int i;
+    ssize_t nbytes;
+
+    if (g_mmc_state.partitions == NULL) {
+        const int nump = MAX_PARTITIONS;
+        MmcPartition *partitions = malloc(nump * sizeof(*partitions));
+        if (partitions == NULL) {
+            errno = ENOMEM;
+            return -1;
+        }
+        g_mmc_state.partitions = partitions;
+        g_mmc_state.partitions_allocd = nump;
+        memset(partitions, 0, nump * sizeof(*partitions));
+    }
+    g_mmc_state.partition_count = 0;
+    ext3_count = 0;
+    vfat_count = 0;
+
+    /* Initialize all of the entries to make things easier later.
+     * (Lets us handle sparsely-numbered partitions, which
+     * may not even be possible.)
+     */
+    for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
+        MmcPartition *p = &g_mmc_state.partitions[i];
+        if (p->device_index != NULL) {
+            free(p->device_index);
+            p->device_index = NULL;
+        }
+        if (p->name != NULL) {
+            free(p->name);
+            p->name = NULL;
+        }
+        if (p->filesystem != NULL) {
+            free(p->filesystem);
+            p->filesystem = NULL;
+        }
+    }
+
+    g_mmc_state.partition_count = mmc_read_mbr(MMC_DEVICENAME, g_mmc_state.partitions);
+    if(g_mmc_state.partition_count == -1)
+    {
+        printf("Error in reading mbr!\n");
+        // keep "partitions" around so we can free the names on a rescan.
+        g_mmc_state.partition_count = -1;
+    }
+    return g_mmc_state.partition_count;
+}
+
+static const MmcPartition *
+mmc_find_partition_by_device_index(const char *device_index)
+{
+    if (g_mmc_state.partitions != NULL) {
+        int i;
+        for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
+            MmcPartition *p = &g_mmc_state.partitions[i];
+            if (p->device_index !=NULL && p->name != NULL) {
+                if (strcmp(p->device_index, device_index) == 0) {
+                    return p;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+const MmcPartition *
+mmc_find_partition_by_name(const char *name)
+{
+    if (name[0] == '/') {
+        return mmc_find_partition_by_device_index(name);
+    }
+
+    if (g_mmc_state.partitions != NULL) {
+        int i;
+        for (i = 0; i < g_mmc_state.partitions_allocd; i++) {
+            MmcPartition *p = &g_mmc_state.partitions[i];
+            if (p->device_index !=NULL && p->name != NULL) {
+                if (strcmp(p->name, name) == 0) {
+                    return p;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+#define MKE2FS_BIN      "/sbin/mke2fs"
+#define TUNE2FS_BIN     "/sbin/tune2fs"
+#define E2FSCK_BIN      "/sbin/e2fsck"
+
+int
+run_exec_process ( char **argv) {
+    pid_t pid;
+    int status;
+    pid = fork();
+    if (pid == 0) {
+        execv(argv[0], argv);
+        fprintf(stderr, "E:Can't run (%s)\n",strerror(errno));
+        _exit(-1);
+    }
+
+    waitpid(pid, &status, 0);
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+        return 1;
+    }
+    return 0;
+}
+
+int
+format_ext3_device (const char *device) {
+#ifdef BOARD_HAS_LARGE_FILESYSTEM
+    char *const mke2fs[] = {MKE2FS_BIN, "-j", "-q", device, NULL};
+    char *const tune2fs[] = {TUNE2FS_BIN, "-C", "1", device, NULL};
+#else
+    char *const mke2fs[] = {MKE2FS_BIN, "-j", device, NULL};
+    char *const tune2fs[] = {TUNE2FS_BIN, "-j", "-C", "1", device, NULL};
+#endif
+    // Run mke2fs
+    if(run_exec_process(mke2fs)) {
+        printf("failure while running mke2fs\n");
+        return -1;
+    }
+
+    // Run tune2fs
+    if(run_exec_process(tune2fs)) {
+        printf("failure while running mke2fs\n");
+        return -1;
+    }
+
+    // Run e2fsck
+    char *const e2fsck[] = {E2FSCK_BIN, "-fy", device, NULL};
+    if(run_exec_process(e2fsck)) {
+        printf("failure while running e2fsck\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+format_ext2_device (const char *device) {
+    // Run mke2fs
+    char *const mke2fs[] = {MKE2FS_BIN, device, NULL};
+    if(run_exec_process(mke2fs))
+        return -1;
+
+    // Run tune2fs
+    char *const tune2fs[] = {TUNE2FS_BIN, "-C", "1", device, NULL};
+    if(run_exec_process(tune2fs))
+        return -1;
+
+    // Run e2fsck
+    char *const e2fsck[] = {E2FSCK_BIN, "-fy", device, NULL};
+    if(run_exec_process(e2fsck))
+        return -1;
+
+    return 0;
+}
+
+int
+mmc_format_ext3 (MmcPartition *partition) {
+    char device[128];
+    strcpy(device, partition->device_index);
+    return format_ext3_device(device);
+}
+
+int
+mmc_mount_partition(const MmcPartition *partition, const char *mount_point,
+        int read_only)
+{
+    const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME;
+    char devname[128];
+    int rv = -1;
+    strcpy(devname, partition->device_index);
+    if (partition->filesystem == NULL) {
+        printf("Null filesystem!\n");
+        return rv;
+    }
+    if (!read_only) {
+        rv = mount(devname, mount_point, partition->filesystem, flags, NULL);
+    }
+    if (read_only || rv < 0) {
+        rv = mount(devname, mount_point, partition->filesystem, flags | MS_RDONLY, 0);
+        if (rv < 0) {
+            printf("Failed to mount %s on %s: %s\n",
+                    devname, mount_point, strerror(errno));
+        } else {
+            printf("Mount %s on %s read-only\n", devname, mount_point);
+        }
+    }
+    return rv;
+}
+
+int
+mmc_raw_copy (const MmcPartition *partition, char *in_file) {
+    int ch;
+    FILE *in;
+    FILE *out;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    char *out_file = partition->device_index;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    out = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR2;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    if (sz % 512)
+    {
+        while ( ( ch = fgetc ( in ) ) != EOF )
+            fputc ( ch, out );
+    }
+    else
+    {
+        for (i=0; i< (sz/512); i++)
+        {
+            if ((fread(buf, 512, 1, in)) != 1)
+                goto ERROR1;
+            if ((fwrite(buf, 512, 1, out)) != 1)
+                goto ERROR1;
+        }
+    }
+
+    fsync(out);
+    ret = 0;
+ERROR1:
+    fclose ( out );
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+
+}
+
+
+int
+mmc_raw_dump_internal (const char* in_file, const char *out_file) {
+    int ch;
+    FILE *in;
+    FILE *out;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    out = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR2;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    if (sz % 512)
+    {
+        while ( ( ch = fgetc ( in ) ) != EOF )
+            fputc ( ch, out );
+    }
+    else
+    {
+        for (i=0; i< (sz/512); i++)
+        {
+            if ((fread(buf, 512, 1, in)) != 1)
+                goto ERROR1;
+            if ((fwrite(buf, 512, 1, out)) != 1)
+                goto ERROR1;
+        }
+    }
+
+    fsync(out);
+    ret = 0;
+ERROR1:
+    fclose ( out );
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+
+}
+
+// TODO: refactor this to not be a giant copy paste mess
+int
+mmc_raw_dump (const MmcPartition *partition, char *out_file) {
+    return mmc_raw_dump_internal(partition->device_index, out_file);
+}
+
+
+int
+mmc_raw_read (const MmcPartition *partition, char *data, int data_size) {
+    int ch;
+    FILE *in;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    char *in_file = partition->device_index;
+
+    in  = fopen ( in_file,  "r" );
+    if (in == NULL)
+        goto ERROR3;
+
+    fseek(in, 0L, SEEK_END);
+    sz = ftell(in);
+    fseek(in, 0L, SEEK_SET);
+
+    fread(data, data_size, 1, in);
+
+    ret = 0;
+ERROR1:
+ERROR2:
+    fclose ( in );
+ERROR3:
+    return ret;
+
+}
+
+int
+mmc_raw_write (const MmcPartition *partition, char *data, int data_size) {
+    int ch;
+    FILE *out;
+    int val = 0;
+    char buf[512];
+    unsigned sz = 0;
+    unsigned i;
+    int ret = -1;
+    char *out_file = partition->device_index;
+
+    out  = fopen ( out_file,  "w" );
+    if (out == NULL)
+        goto ERROR3;
+
+    fwrite(data, data_size, 1, out);
+
+    ret = 0;
+ERROR1:
+ERROR2:
+    fclose ( out );
+ERROR3:
+    return ret;
+
+}
+
+int cmd_mmc_restore_raw_partition(const char *partition, const char *filename)
+{
+    if (partition[0] != '/') {
+        mmc_scan_partitions();
+        const MmcPartition *p;
+        p = mmc_find_partition_by_name(partition);
+        if (p == NULL)
+            return -1;
+        return mmc_raw_copy(p, filename);
+    }
+    else {
+        return mmc_raw_dump_internal(filename, partition);
+    }
+}
+
+int cmd_mmc_backup_raw_partition(const char *partition, const char *filename)
+{
+    if (partition[0] != '/') {
+        mmc_scan_partitions();
+        const MmcPartition *p;
+        p = mmc_find_partition_by_name(partition);
+        if (p == NULL)
+            return -1;
+        return mmc_raw_dump(p, filename);
+    }
+    else {
+        return mmc_raw_dump_internal(partition, filename);
+    }
+}
+
+int cmd_mmc_erase_raw_partition(const char *partition)
+{
+    return 0;
+}
+
+int cmd_mmc_erase_partition(const char *partition, const char *filesystem)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    return mmc_format_ext3 (p);
+}
+
+int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    return mmc_mount_partition(p, mount_point, read_only);
+}
+
+int cmd_mmc_get_partition_device(const char *partition, char *device)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    strcpy(device, p->device_index);
+    return 0;
+}
diff --git a/mmcutils/mmcutils.h b/mmcutils/mmcutils.h
new file mode 100644
index 0000000..5b10fdc
--- /dev/null
+++ b/mmcutils/mmcutils.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *   * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MMCUTILS_H_
+#define MMCUTILS_H_
+
+/* Some useful define used to access the MBR/EBR table */
+#define BLOCK_SIZE                0x200
+#define TABLE_ENTRY_0             0x1BE
+#define TABLE_ENTRY_1             0x1CE
+#define TABLE_ENTRY_2             0x1DE
+#define TABLE_ENTRY_3             0x1EE
+#define TABLE_SIGNATURE           0x1FE
+#define TABLE_ENTRY_SIZE          0x010
+
+#define OFFSET_STATUS             0x00
+#define OFFSET_TYPE               0x04
+#define OFFSET_FIRST_SEC          0x08
+#define OFFSET_SIZE               0x0C
+#define COPYBUFF_SIZE             (1024 * 16)
+#define BINARY_IN_TABLE_SIZE      (16 * 512)
+#define MAX_FILE_ENTRIES          20
+
+#define MMC_BOOT_TYPE 0x48
+#define MMC_SYSTEM_TYPE 0x82
+#define MMC_USERDATA_TYPE 0x83
+#define MMC_RECOVERY_TYPE 0x71
+
+#define MMC_RCA 2
+
+#define MAX_PARTITIONS 64
+
+#define GET_LWORD_FROM_BYTE(x)    ((unsigned)*(x) | \
+        ((unsigned)*((x)+1) << 8) | \
+        ((unsigned)*((x)+2) << 16) | \
+        ((unsigned)*((x)+3) << 24))
+
+#define PUT_LWORD_TO_BYTE(x, y)   do{*(x) = (y) & 0xff;     \
+    *((x)+1) = ((y) >> 8) & 0xff;     \
+    *((x)+2) = ((y) >> 16) & 0xff;     \
+    *((x)+3) = ((y) >> 24) & 0xff; }while(0)
+
+#define GET_PAR_NUM_FROM_POS(x) ((((x) & 0x0000FF00) >> 8) + ((x) & 0x000000FF))
+
+#define MMC_BOOT_TYPE 0x48
+#define MMC_EXT3_TYPE 0x83
+#define MMC_VFAT_TYPE 0xC
+typedef struct MmcPartition MmcPartition;
+
+/* Functions */
+int mmc_scan_partitions();
+const MmcPartition *mmc_find_partition_by_name(const char *name);
+int mmc_format_ext3 (MmcPartition *partition);
+int mmc_mount_partition(const MmcPartition *partition, const char *mount_point, \
+                        int read_only);
+int mmc_raw_copy (const MmcPartition *partition, char *in_file);
+int mmc_raw_read (const MmcPartition *partition, char *data, int data_size);
+int mmc_raw_write (const MmcPartition *partition, char *data, int data_size);
+
+int format_ext2_device(const char *device);
+int format_ext3_device(const char *device);
+
+#endif  // MMCUTILS_H_
+
+
diff --git a/mtdutils/mounts.c b/mounts.c
similarity index 97%
rename from mtdutils/mounts.c
rename to mounts.c
index c90fc8a..e8b69e8 100644
--- a/mtdutils/mounts.c
+++ b/mounts.c
@@ -23,13 +23,6 @@
 
 #include "mounts.h"
 
-struct MountedVolume {
-    const char *device;
-    const char *mount_point;
-    const char *filesystem;
-    const char *flags;
-};
-
 typedef struct {
     MountedVolume *volumes;
     int volumes_allocd;
diff --git a/mtdutils/mounts.h b/mounts.h
similarity index 88%
rename from mtdutils/mounts.h
rename to mounts.h
index 30b2927..ed7fb5f 100644
--- a/mtdutils/mounts.h
+++ b/mounts.h
@@ -17,7 +17,12 @@
 #ifndef MTDUTILS_MOUNTS_H_
 #define MTDUTILS_MOUNTS_H_
 
-typedef struct MountedVolume MountedVolume;
+typedef struct {
+ const char *device;
+ const char *mount_point;
+ const char *filesystem;
+ const char *flags;
+} MountedVolume;
 
 int scan_mounted_volumes(void);
 
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk
index ef417fa..3314101 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -1,18 +1,29 @@
 LOCAL_PATH := $(call my-dir)
+
 include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-	mtdutils.c \
-	mounts.c
-
+LOCAL_SRC_FILES := mtdutils.c
 LOCAL_MODULE := libmtdutils
+include $(BUILD_STATIC_LIBRARY)
 
+ifeq ($(BOARD_USES_BML_OVER_MTD),true)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := bml_over_mtd.c
+LOCAL_C_INCLUDES += bootable/recovery/mtdutils
+LOCAL_MODULE := libbml_over_mtd
+LOCAL_MODULE_TAGS := eng
+LOCAL_CFLAGS += -Dmain=bml_over_mtd_main
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := flash_image.c
-LOCAL_MODULE := flash_image
+LOCAL_SRC_FILES := bml_over_mtd.c
+LOCAL_MODULE := bml_over_mtd
 LOCAL_MODULE_TAGS := eng
-LOCAL_STATIC_LIBRARIES := libmtdutils
-LOCAL_SHARED_LIBRARIES := libcutils libc
+LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities
+LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities
+LOCAL_MODULE_STEM := bml_over_mtd
+LOCAL_C_INCLUDES += bootable/recovery/mtdutils
+LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
 include $(BUILD_EXECUTABLE)
+endif
diff --git a/mtdutils/bml_over_mtd.c b/mtdutils/bml_over_mtd.c
new file mode 100644
index 0000000..c401792
--- /dev/null
+++ b/mtdutils/bml_over_mtd.c
@@ -0,0 +1,798 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+
+#include "cutils/log.h"
+
+#include <mtd/mtd-user.h>
+
+#include "mtdutils.h"
+
+typedef struct BmlOverMtdReadContext {
+	const MtdPartition *partition;
+	char *buffer;
+	size_t consumed;
+	int fd;
+} BmlOverMtdReadContext;
+
+typedef struct BmlOverMtdWriteContext {
+	const MtdPartition *partition;
+	char *buffer;
+	size_t stored;
+	int fd;
+
+	off_t* bad_block_offsets;
+	int bad_block_alloc;
+	int bad_block_count;
+} BmlOverMtdWriteContext;
+
+
+static BmlOverMtdReadContext *bml_over_mtd_read_partition(const MtdPartition *partition)
+{
+	BmlOverMtdReadContext *ctx = (BmlOverMtdReadContext*) malloc(sizeof(BmlOverMtdReadContext));
+	if (ctx == NULL) return NULL;
+
+	ctx->buffer = malloc(partition->erase_size);
+	if (ctx->buffer == NULL) {
+		free(ctx);
+		return NULL;
+	}
+
+	char mtddevname[32];
+	sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
+	ctx->fd = open(mtddevname, O_RDONLY);
+	if (ctx->fd < 0) {
+		free(ctx);
+		free(ctx->buffer);
+		return NULL;
+	}
+
+	ctx->partition = partition;
+	ctx->consumed = partition->erase_size;
+	return ctx;
+}
+
+static void bml_over_mtd_read_close(BmlOverMtdReadContext *ctx)
+{
+	close(ctx->fd);
+	free(ctx->buffer);
+	free(ctx);
+}
+
+static BmlOverMtdWriteContext *bml_over_mtd_write_partition(const MtdPartition *partition)
+{
+	BmlOverMtdWriteContext *ctx = (BmlOverMtdWriteContext*) malloc(sizeof(BmlOverMtdWriteContext));
+	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);
+		return NULL;
+	}
+
+	char mtddevname[32];
+	sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index);
+	ctx->fd = open(mtddevname, O_RDWR);
+	if (ctx->fd < 0) {
+		free(ctx->buffer);
+		free(ctx);
+		return NULL;
+	}
+
+	ctx->partition = partition;
+	ctx->stored = 0;
+	return ctx;
+}
+
+static int bml_over_mtd_write_close(BmlOverMtdWriteContext *ctx)
+{
+	int r = 0;
+	if (close(ctx->fd)) r = -1;
+	free(ctx->bad_block_offsets);
+	free(ctx->buffer);
+	free(ctx);
+	return r;
+}
+
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "bml_over_mtd"
+
+#define BLOCK_SIZE    2048
+#define SPARE_SIZE    (BLOCK_SIZE >> 5)
+
+#define EXIT_CODE_BAD_BLOCKS 15
+
+static int die(const char *msg, ...) {
+	int err = errno;
+	va_list args;
+	va_start(args, msg);
+	char buf[1024];
+	vsnprintf(buf, sizeof(buf), msg, args);
+	va_end(args);
+
+	if (err != 0) {
+		strlcat(buf, ": ", sizeof(buf));
+		strlcat(buf, strerror(err), sizeof(buf));
+	}
+
+	fprintf(stderr, "%s\n", buf);
+	return 1;
+}
+
+static unsigned short* CreateEmptyBlockMapping(const MtdPartition* pSrcPart)
+{
+	size_t srcTotal, srcErase, srcWrite;
+	if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0)
+	{
+		fprintf(stderr, "Failed to access partition.\n");
+		return NULL;
+	}
+
+	int numSrcBlocks = srcTotal/srcErase;
+
+	unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short));
+	if (pMapping == NULL)
+	{
+		fprintf(stderr, "Failed to allocate block mapping memory.\n");
+		return NULL;
+	}
+	memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short));
+	return pMapping;
+}
+
+static const unsigned short* CreateBlockMapping(const MtdPartition* pSrcPart, int srcPartStartBlock,
+		const MtdPartition *pReservoirPart, int reservoirPartStartBlock)
+{
+	size_t srcTotal, srcErase, srcWrite;
+	if (mtd_partition_info(pSrcPart, &srcTotal, &srcErase, &srcWrite) != 0)
+	{
+		fprintf(stderr, "Failed to access partition.\n");
+		return NULL;
+	}
+
+	int numSrcBlocks = srcTotal/srcErase;
+
+	unsigned short* pMapping = malloc(numSrcBlocks * sizeof(unsigned short));
+	if (pMapping == NULL)
+	{
+		fprintf(stderr, "Failed to allocate block mapping memory.\n");
+		return NULL;
+	}
+	memset(pMapping, 0xFF, numSrcBlocks * sizeof(unsigned short));
+
+	size_t total, erase, write;
+	if (mtd_partition_info(pReservoirPart, &total, &erase, &write) != 0)
+	{
+		fprintf(stderr, "Failed to access reservoir partition.\n");
+		free(pMapping);
+		return NULL;
+	}
+
+	if (erase != srcErase || write != srcWrite)
+	{
+		fprintf(stderr, "Source partition and reservoir partition differ in size properties.\n");
+		free(pMapping);
+		return NULL;
+	}
+
+	printf("Partition info: Total %d, Erase %d, write %d\n", total, erase, write);
+
+	BmlOverMtdReadContext *readctx = bml_over_mtd_read_partition(pReservoirPart);
+	if (readctx == NULL)
+	{
+		fprintf(stderr, "Failed to open reservoir partition for reading.\n");
+		free(pMapping);
+		return NULL;
+	}
+
+	if (total < erase || total > INT_MAX)
+	{
+		fprintf(stderr, "Unsuitable reservoir partition properties.\n");
+		free(pMapping);
+		bml_over_mtd_read_close(readctx);
+		return NULL;
+	}
+
+	int foundMappingTable = 0;
+
+	int currOffset = total; //Offset *behind* the last byte
+	while (currOffset > 0)
+	{
+		currOffset -= erase;
+		loff_t pos = lseek64(readctx->fd, currOffset, SEEK_SET);
+		int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos);
+		if (mgbb != 0)
+		{
+			printf("Bad block %d in reservoir area, skipping.\n", currOffset/erase);
+			continue;
+		}
+		ssize_t readBytes = read(readctx->fd, readctx->buffer, erase);
+		if (readBytes != (ssize_t)erase)
+		{
+			fprintf(stderr, "Failed to read good block in reservoir area (%s).\n",
+					strerror(errno));
+			free(pMapping);
+			bml_over_mtd_read_close(readctx);
+			return NULL;
+		}
+		if (readBytes >= 0x2000)
+		{
+			char* buf = readctx->buffer;
+			if (buf[0]=='U' && buf[1]=='P' && buf[2]=='C' && buf[3]=='H')
+			{
+				printf ("Found mapping block mark at 0x%x (block %d).\n", currOffset, currOffset/erase);
+
+				unsigned short* mappings = (unsigned short*) &buf[0x1000];
+				if (mappings[0]==0 && mappings[1]==0xffff)
+				{
+					printf("Found start of mapping table.\n");
+					foundMappingTable = 1;
+					//Skip first entry (dummy)
+					unsigned short* mappingEntry = mappings + 2;
+					while (mappingEntry - mappings < 100
+							&& mappingEntry[0] != 0xffff)
+					{
+						unsigned short rawSrcBlk = mappingEntry[0];
+						unsigned short rawDstBlk = mappingEntry[1];
+
+						printf("Found raw block mapping %d -> %d\n", rawSrcBlk,
+								rawDstBlk);
+
+						unsigned int srcAbsoluteStartAddress = srcPartStartBlock * erase;
+						unsigned int resAbsoluteStartAddress = reservoirPartStartBlock * erase;
+
+						int reservoirLastBlock = reservoirPartStartBlock + numSrcBlocks - 1;
+						if (rawDstBlk < reservoirPartStartBlock
+								|| rawDstBlk*erase >= resAbsoluteStartAddress+currOffset)
+						{
+							fprintf(stderr, "Mapped block not within reasonable reservoir area.\n");
+							foundMappingTable = 0;
+							break;
+						}
+
+						int srcLastBlock = srcPartStartBlock + numSrcBlocks - 1;
+						if (rawSrcBlk >= srcPartStartBlock && rawSrcBlk <= srcLastBlock)
+						{
+
+							unsigned short relSrcBlk = rawSrcBlk - srcPartStartBlock;
+							unsigned short relDstBlk = rawDstBlk - reservoirPartStartBlock;
+							printf("Partition relative block mapping %d -> %d\n",relSrcBlk, relDstBlk);
+
+							printf("Absolute mapped start addresses 0x%x -> 0x%x\n",
+									srcAbsoluteStartAddress+relSrcBlk*erase,
+									resAbsoluteStartAddress+relDstBlk*erase);
+							printf("Partition relative mapped start addresses 0x%x -> 0x%x\n",
+									relSrcBlk*erase, relDstBlk*erase);
+
+							//Set mapping entry. For duplicate entries, later entries replace former ones.
+							//*Assumption*: Bad blocks in reservoir area will not be mapped themselves in
+							//the mapping table. User partition blocks will not be mapped to bad blocks
+							//(only) in the reservoir area. This has to be confirmed on a wider range of
+							//devices.
+							pMapping[relSrcBlk] = relDstBlk;
+
+						}
+						mappingEntry+=2;
+					}
+					break; //We found the mapping table, no need to search further
+				}
+
+
+			}
+		}
+
+	}
+	bml_over_mtd_read_close(readctx);
+
+	if (foundMappingTable == 0)
+	{
+		fprintf(stderr, "Cannot find mapping table in reservoir partition.\n");
+		free(pMapping);
+		return NULL;
+	}
+
+	//Consistency and validity check
+	int mappingValid = 1;
+	readctx = bml_over_mtd_read_partition(pSrcPart);
+	if (readctx == NULL)
+	{
+		fprintf(stderr, "Cannot open source partition for reading.\n");
+		free(pMapping);
+		return NULL;
+	}
+	int currBlock = 0;
+	for (;currBlock < numSrcBlocks; ++currBlock)
+	{
+		loff_t pos = lseek64(readctx->fd, currBlock*erase, SEEK_SET);
+		int mgbb = ioctl(readctx->fd, MEMGETBADBLOCK, &pos);
+		if (mgbb == 0)
+		{
+			if (pMapping[currBlock]!=0xffff)
+			{
+				fprintf(stderr, "Consistency error: Good block has mapping entry %d -> %d\n", currBlock, pMapping[currBlock]);
+				mappingValid = 0;
+			}
+		} else
+		{
+			//Bad block!
+			if (pMapping[currBlock]==0xffff)
+			{
+				fprintf(stderr, "Consistency error: Bad block has no mapping entry \n");
+				mappingValid = 0;
+			} else
+			{
+				BmlOverMtdReadContext* reservoirReadCtx = bml_over_mtd_read_partition(pReservoirPart);
+				if (reservoirReadCtx == 0)
+				{
+					fprintf(stderr, "Reservoir partition cannot be opened for reading in consistency check.\n");
+					mappingValid = 0;
+				} else
+				{
+					pos = lseek64(reservoirReadCtx->fd, pMapping[currBlock]*erase, SEEK_SET);
+					mgbb = ioctl(reservoirReadCtx->fd, MEMGETBADBLOCK, &pos);
+					if (mgbb == 0)
+					{
+						printf("Bad block has properly mapped reservoir block %d -> %d\n",currBlock, pMapping[currBlock]);
+					}
+					else
+					{
+						fprintf(stderr, "Consistency error: Mapped block is bad, too. (%d -> %d)\n",currBlock, pMapping[currBlock]);
+						mappingValid = 0;
+					}
+
+				}
+				bml_over_mtd_read_close(reservoirReadCtx);
+			}
+
+		}
+
+	}
+	bml_over_mtd_read_close(readctx);
+
+
+	if (!mappingValid)
+	{
+		free(pMapping);
+		return NULL;
+	}
+
+	return pMapping;
+}
+
+static void ReleaseBlockMapping(const unsigned short* blockMapping)
+{
+	free((void*)blockMapping);
+}
+
+static int dump_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart,
+		const unsigned short* blockMapping, const char* filename)
+{
+	int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+	if (fd < 0)
+	{
+		fprintf(stderr, "error opening %s", filename);
+		return -1;
+	}
+	BmlOverMtdReadContext* pSrcRead = bml_over_mtd_read_partition(pSrcPart);
+	if (pSrcRead == NULL)
+	{
+		close(fd);
+		fprintf(stderr, "dump_bml_partition: Error opening src part for reading.\n");
+		return -1;
+	}
+
+	BmlOverMtdReadContext* pResRead = bml_over_mtd_read_partition(pReservoirPart);
+	if (pResRead == NULL)
+	{
+		close(fd);
+		bml_over_mtd_read_close(pSrcRead);
+		fprintf(stderr, "dump_bml_partition: Error opening reservoir part for reading.\n");
+		return -1;
+	}
+
+
+	int numBlocks = pSrcPart->size / pSrcPart->erase_size;
+	int currblock = 0;
+	for (;currblock < numBlocks; ++currblock)
+	{
+		int srcFd = -1;
+		if (blockMapping[currblock] == 0xffff)
+		{
+			//Good block, use src partition
+			srcFd = pSrcRead->fd;
+			if (lseek64(pSrcRead->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1)
+			{
+				close(fd);
+				bml_over_mtd_read_close(pSrcRead);
+				bml_over_mtd_read_close(pResRead);
+				fprintf(stderr, "dump_bml_partition: lseek in src partition failed\n");
+				return -1;
+			}
+		} else
+		{
+			//Bad block, use mapped block in reservoir partition
+			srcFd = pResRead->fd;
+			if (lseek64(pResRead->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1)
+			{
+				close(fd);
+				bml_over_mtd_read_close(pSrcRead);
+				bml_over_mtd_read_close(pResRead);
+				fprintf(stderr, "dump_bml_partition: lseek in reservoir partition failed\n");
+				return -1;
+			}
+		}
+		size_t blockBytesRead = 0;
+		while (blockBytesRead < pSrcPart->erase_size)
+		{
+			ssize_t len = read(srcFd, pSrcRead->buffer + blockBytesRead,
+					pSrcPart->erase_size - blockBytesRead);
+			if (len <= 0)
+			{
+				close(fd);
+				bml_over_mtd_read_close(pSrcRead);
+				bml_over_mtd_read_close(pResRead);
+				fprintf(stderr, "dump_bml_partition: reading partition failed\n");
+				return -1;
+			}
+			blockBytesRead += len;
+		}
+
+		size_t blockBytesWritten = 0;
+		while (blockBytesWritten < pSrcPart->erase_size)
+		{
+			ssize_t len = write(fd, pSrcRead->buffer + blockBytesWritten,
+					pSrcPart->erase_size - blockBytesWritten);
+			if (len <= 0)
+			{
+				close(fd);
+				bml_over_mtd_read_close(pSrcRead);
+				bml_over_mtd_read_close(pResRead);
+				fprintf(stderr, "dump_bml_partition: writing partition dump file failed\n");
+				return -1;
+			}
+			blockBytesWritten += len;
+		}
+
+	}
+
+	bml_over_mtd_read_close(pSrcRead);
+	bml_over_mtd_read_close(pResRead);
+
+	if (close(fd)) {
+		unlink(filename);
+		printf("error closing %s", filename);
+		return -1;
+	}
+
+	return 0;
+}
+
+static ssize_t bml_over_mtd_write_block(int fd, ssize_t erase_size, char* data)
+{
+	off_t pos = lseek(fd, 0, SEEK_CUR);
+	if (pos == (off_t) -1) return -1;
+
+	ssize_t size = erase_size;
+	loff_t bpos = pos;
+	int ret = ioctl(fd, MEMGETBADBLOCK, &bpos);
+	if (ret != 0 && !(ret == -1 && errno == EOPNOTSUPP)) {
+		fprintf(stderr,
+				"Mapping failure: Trying to write bad block at 0x%08lx (ret %d errno %d)\n",
+				pos, ret, errno);
+		return -1;
+	}
+
+	struct erase_info_user erase_info;
+	erase_info.start = pos;
+	erase_info.length = size;
+	int retry;
+	for (retry = 0; retry < 2; ++retry) {
+		if (ioctl(fd, MEMERASE, &erase_info) < 0) {
+			fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+			continue;
+		}
+		if (lseek(fd, pos, SEEK_SET) != pos ||
+				write(fd, data, size) != size) {
+			fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+		}
+
+		char verify[size];
+		if (lseek(fd, pos, SEEK_SET) != pos ||
+				read(fd, verify, size) != size) {
+			fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+			continue;
+		}
+		if (memcmp(data, verify, size) != 0) {
+			fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n",
+					pos, strerror(errno));
+			continue;
+		}
+
+		if (retry > 0) {
+			fprintf(stderr, "mtd: wrote block after %d retries\n", retry);
+		}
+		fprintf(stderr, "mtd: successfully wrote block at %llx\n", pos);
+		return size;  // Success!
+	}
+
+
+	fprintf(stderr, "mtd: Block at %llx could not be properly written.\n", pos);
+	// Ran out of space on the device
+	errno = ENOSPC;
+	return -1;
+}
+
+static int flash_bml_partition(const MtdPartition* pSrcPart, const MtdPartition* pReservoirPart,
+		const unsigned short* blockMapping, const char* filename)
+{
+	int fd = open(filename, O_RDONLY);
+	if (fd < 0)
+	{
+		fprintf(stderr, "error opening %s", filename);
+		return -1;
+	}
+	BmlOverMtdWriteContext* pSrcWrite = bml_over_mtd_write_partition(pSrcPart);
+	if (pSrcWrite == NULL)
+	{
+		close(fd);
+		fprintf(stderr, "flash_bml_partition: Error opening src part for writing.\n");
+		return -1;
+	}
+
+#ifdef DUMMY_WRITING
+	close(pSrcWrite->fd);
+	pSrcWrite->fd = open("/sdcard/srcPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666);
+#endif
+
+	BmlOverMtdWriteContext* pResWrite = bml_over_mtd_write_partition(pReservoirPart);
+	if (pResWrite == NULL)
+	{
+		close(fd);
+		bml_over_mtd_write_close(pSrcWrite);
+		fprintf(stderr, "flash_bml_partition: Error opening reservoir part for writing.\n");
+		return -1;
+	}
+#ifdef DUMMY_WRITING
+	close(pResWrite->fd);
+	pResWrite->fd = open("/sdcard/resPartWriteDummy.bin", O_WRONLY|O_CREAT|O_TRUNC, 0666);
+#endif
+
+	struct stat fileStat;
+	if (fstat(fd, &fileStat) != 0)
+	{
+		close(fd);
+		bml_over_mtd_write_close(pSrcWrite);
+		bml_over_mtd_write_close(pResWrite);
+		fprintf(stderr, "flash_bml_partition: Failed to stat source file.\n");
+		return -1;
+
+	}
+	if (fileStat.st_size > pSrcPart->size)
+	{
+		close(fd);
+		bml_over_mtd_write_close(pSrcWrite);
+		bml_over_mtd_write_close(pResWrite);
+		fprintf(stderr, "flash_bml_partition: Source file too large for target partition.\n");
+		return -1;
+	}
+
+	int numBlocks = (fileStat.st_size +  pSrcPart->erase_size - 1) / pSrcPart->erase_size;
+	int currblock;
+	for (currblock = 0 ;currblock < numBlocks; ++currblock)
+	{
+		memset(pSrcWrite->buffer, 0xFF, pSrcPart->erase_size);
+		size_t blockBytesRead = 0;
+		while (blockBytesRead < pSrcPart->erase_size)
+		{
+			ssize_t len = read(fd, pSrcWrite->buffer + blockBytesRead,
+					pSrcPart->erase_size - blockBytesRead);
+			if (len < 0)
+			{
+				close(fd);
+				bml_over_mtd_write_close(pSrcWrite);
+				bml_over_mtd_write_close(pResWrite);
+				fprintf(stderr, "flash_bml_partition: read source file failed\n");
+				return -1;
+			}
+			if (len == 0)
+			{
+				//End of file
+				break;
+			}
+
+			blockBytesRead += len;
+		}
+
+
+
+		int srcFd = -1;
+		if (blockMapping[currblock] == 0xffff)
+		{
+			//Good block, use src partition
+			srcFd = pSrcWrite->fd;
+			if (lseek64(pSrcWrite->fd, currblock*pSrcPart->erase_size, SEEK_SET)==-1)
+			{
+				close(fd);
+				bml_over_mtd_write_close(pSrcWrite);
+				bml_over_mtd_write_close(pResWrite);
+				fprintf(stderr, "flash_bml_partition: lseek in src partition failed\n");
+				return -1;
+			}
+		} else
+		{
+			//Bad block, use mapped block in reservoir partition
+			srcFd = pResWrite->fd;
+			if (lseek64(pResWrite->fd, blockMapping[currblock]*pSrcPart->erase_size, SEEK_SET)==-1)
+			{
+				close(fd);
+				bml_over_mtd_write_close(pSrcWrite);
+				bml_over_mtd_write_close(pResWrite);
+				fprintf(stderr, "flash_bml_partition: lseek in reservoir partition failed\n");
+				return -1;
+			}
+		}
+		size_t blockBytesWritten = 0;
+		while (blockBytesWritten < pSrcPart->erase_size)
+		{
+#ifdef DUMMY_WRITING
+			ssize_t len = write(srcFd, pSrcWrite->buffer + blockBytesWritten,
+					pSrcPart->erase_size - blockBytesWritten);
+#else
+			ssize_t len = bml_over_mtd_write_block(srcFd, pSrcPart->erase_size, pSrcWrite->buffer);
+#endif
+			if (len <= 0)
+			{
+				close(fd);
+				bml_over_mtd_write_close(pSrcWrite);
+				bml_over_mtd_write_close(pResWrite);
+				fprintf(stderr, "flash_bml_partition: writing to partition failed\n");
+				return -1;
+			}
+			blockBytesWritten += len;
+		}
+
+
+	}
+
+	bml_over_mtd_write_close(pSrcWrite);
+	bml_over_mtd_write_close(pResWrite);
+
+	if (close(fd)) {
+		printf("error closing %s", filename);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int scan_partition(const MtdPartition* pPart)
+{
+	BmlOverMtdReadContext* readCtx = bml_over_mtd_read_partition(pPart);
+	if (readCtx == NULL)
+	{
+		fprintf(stderr, "Failed to open partition for reading.\n");
+		return -1;
+	}
+
+	int numBadBlocks = 0;
+	size_t numBlocks = pPart->size / pPart->erase_size;
+	size_t currBlock;
+	for (currBlock = 0; currBlock < numBlocks; ++currBlock)
+	{
+
+		loff_t pos = currBlock * pPart->erase_size;
+		int mgbb = ioctl(readCtx->fd, MEMGETBADBLOCK, &pos);
+		if (mgbb != 0)
+		{
+			printf("Bad block %d at 0x%x.\n", currBlock, (unsigned int)pos);
+			numBadBlocks++;
+		}
+	}
+
+	bml_over_mtd_read_close(readCtx);
+	if (numBadBlocks == 0)
+	{
+		printf("No bad blocks.\n");
+		return 0;
+	}
+	return -1 ;
+}
+
+int main(int argc, char **argv)
+{
+	if (argc != 7 && (argc != 3 || (argc == 3 && strcmp(argv[1],"scan"))!=0)
+			&& (argc != 6 || (argc == 6 && strcmp(argv[1],"scan"))!=0))
+		return die("Usage: %s dump|flash <partition> <partition_start_block> <reservoirpartition> <reservoir_start_block> <file>\n"
+				"E.g. %s dump boot 72 reservoir 2004 file.bin\n"
+				"Usage: %s scan <partition> [<partition_start_block> <reservoirpartition> <reservoir_start_block>]\n"
+				,argv[0], argv[0], argv[0]);
+	int num_partitions = mtd_scan_partitions();
+	const MtdPartition *pSrcPart = mtd_find_partition_by_name(argv[2]);
+	if (pSrcPart == NULL)
+		return die("Cannot find partition %s", argv[2]);
+
+	int scanResult = scan_partition(pSrcPart);
+
+	if (argc == 3 && strcmp(argv[1],"scan")==0)
+	{
+		return (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS);
+	}
+
+	int retVal = 0;
+	const MtdPartition* pReservoirPart = mtd_find_partition_by_name(argv[4]);
+	if (pReservoirPart == NULL)
+		return die("Cannot find partition %s", argv[4]);
+
+	int srcPartStartBlock = atoi(argv[3]);
+	int reservoirPartStartBlock = atoi(argv[5]);
+	const unsigned short* pMapping = CreateBlockMapping(pSrcPart, srcPartStartBlock,
+			pReservoirPart, reservoirPartStartBlock);
+
+	if (pMapping == NULL && scanResult == 0)
+	{
+		printf("Generating empty block mapping table for error-free partition.\n");
+		pMapping = CreateEmptyBlockMapping(pSrcPart);
+	}
+
+	if (argc == 6 && strcmp(argv[1],"scan")==0)
+	{
+		retVal = (scanResult == 0 ? 0 : EXIT_CODE_BAD_BLOCKS);
+	}
+
+	if (pMapping == NULL)
+		return die("Failed to create block mapping table");
+
+	if (strcmp(argv[1],"dump")==0)
+	{
+		retVal = dump_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]);
+		if (retVal == 0)
+			printf("Successfully dumped partition to %s\n", argv[6]);
+	}
+
+	if (strcmp(argv[1],"flash")==0)
+	{
+		retVal = flash_bml_partition(pSrcPart, pReservoirPart, pMapping, argv[6]);
+		if (retVal == 0)
+			printf("Successfully wrote %s to partition\n", argv[6]);
+
+	}
+
+
+	ReleaseBlockMapping(pMapping);
+	return retVal;
+}
+
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index e4d2a60..8cb04be 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -28,13 +28,6 @@
 
 #include "mtdutils.h"
 
-struct MtdPartition {
-    int device_index;
-    unsigned int size;
-    unsigned int erase_size;
-    char *name;
-};
-
 struct MtdReadContext {
     const MtdPartition *partition;
     char *buffer;
@@ -345,7 +338,7 @@
             read += ctx->partition->erase_size;
         }
 
-        if (read >= len) {
+        if (read >= (int)len) {
             return read;
         }
 
@@ -569,3 +562,276 @@
     }
     return pos;
 }
+
+#define BLOCK_SIZE    2048
+#define SPARE_SIZE    (BLOCK_SIZE >> 5)
+#define HEADER_SIZE 2048
+
+int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename)
+{
+    const MtdPartition *ptn;
+    MtdWriteContext *write;
+    void *data;
+    unsigned sz;
+
+    if (mtd_scan_partitions() <= 0)
+    {
+        printf("error scanning partitions");
+        return -1;
+    }
+    const MtdPartition *partition = mtd_find_partition_by_name(partition_name);
+    if (partition == NULL)
+    {
+        printf("can't find %s partition", partition_name);
+        return -1;
+    }
+
+    int fd = open(filename, O_RDONLY);
+    if (fd < 0)
+    {
+        printf("error opening %s", filename);
+        return -1;
+    }
+
+    char header[HEADER_SIZE];
+    int headerlen = read(fd, header, sizeof(header));
+    if (headerlen <= 0)
+    {
+        printf("error reading %s header", filename);
+        return -1;
+    }
+
+    // Skip the header (we'll come back to it), write everything else
+    printf("flashing %s from %s\n", partition_name, filename);
+
+    MtdWriteContext *out = mtd_write_partition(partition);
+    if (out == NULL)
+    {
+       printf("error writing %s", partition_name);
+       return -1;
+    }
+
+    char buf[HEADER_SIZE];
+    memset(buf, 0, headerlen);
+    int wrote = mtd_write_data(out, buf, headerlen);
+    if (wrote != headerlen)
+    {
+        printf("error writing %s", partition_name);
+        return -1;
+    }
+
+    int len;
+    while ((len = read(fd, buf, sizeof(buf))) > 0) {
+        wrote = mtd_write_data(out, buf, len);
+        if (wrote != len)
+        {
+            printf("error writing %s", partition_name);
+            return -1;
+        }
+    }
+    if (len < 0)
+    {
+       printf("error reading %s", filename);
+       return -1;
+    }
+
+    if (mtd_write_close(out))
+    {
+        printf("error closing %s", partition_name);
+        return -1;
+    }
+
+    // Now come back and write the header last
+
+    out = mtd_write_partition(partition);
+    if (out == NULL)
+    {
+        printf("error re-opening %s", partition_name);
+        return -1;
+    }
+
+    wrote = mtd_write_data(out, header, headerlen);
+    if (wrote != headerlen)
+    {
+        printf("error re-writing %s", partition_name);
+        return -1;
+    }
+
+    // Need to write a complete block, so write the rest of the first block
+    size_t block_size;
+    if (mtd_partition_info(partition, NULL, &block_size, NULL))
+    {
+        printf("error getting %s block size", partition_name);
+        return -1;
+    }
+
+    if (lseek(fd, headerlen, SEEK_SET) != headerlen)
+    {
+        printf("error rewinding %s", filename);
+        return -1;
+    }
+
+    int left = block_size - headerlen;
+    while (left < 0) left += block_size;
+    while (left > 0) {
+        len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left);
+        if (len <= 0){
+            printf("error reading %s", filename);
+            return -1;
+        }
+        if (mtd_write_data(out, buf, len) != len)
+        {
+            printf("error writing %s", partition_name);
+            return -1;
+        }
+
+        left -= len;
+    }
+
+    if (mtd_write_close(out))
+    {
+        printf("error closing %s", partition_name);
+        return -1;
+    }
+    return 0;
+}
+
+
+int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename)
+{
+    MtdReadContext *in;
+    const MtdPartition *partition;
+    char buf[BLOCK_SIZE + SPARE_SIZE];
+    size_t partition_size;
+    size_t read_size;
+    size_t total;
+    int fd;
+    int wrote;
+    int len;
+
+    if (mtd_scan_partitions() <= 0)
+    {
+        printf("error scanning partitions");
+        return -1;
+    }
+
+    partition = mtd_find_partition_by_name(partition_name);
+    if (partition == NULL)
+    {
+        printf("can't find %s partition", partition_name);
+        return -1;
+    }
+
+    if (mtd_partition_info(partition, &partition_size, NULL, NULL)) {
+        printf("can't get info of partition %s", partition_name);
+        return -1;
+    }
+
+    if (!strcmp(filename, "-")) {
+        fd = fileno(stdout);
+    }
+    else {
+        fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+    }
+
+    if (fd < 0)
+    {
+       printf("error opening %s", filename);
+       return -1;
+    }
+
+    in = mtd_read_partition(partition);
+    if (in == NULL) {
+        close(fd);
+        unlink(filename);
+        printf("error opening %s: %s\n", partition_name, strerror(errno));
+        return -1;
+    }
+
+    total = 0;
+    while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) {
+        wrote = write(fd, buf, len);
+        if (wrote != len) {
+            close(fd);
+            unlink(filename);
+            printf("error writing %s", filename);
+            return -1;
+        }
+        total += BLOCK_SIZE;
+    }
+
+    mtd_read_close(in);
+
+    if (close(fd)) {
+        unlink(filename);
+        printf("error closing %s", filename);
+        return -1;
+    }
+    return 0;
+}
+
+int cmd_mtd_erase_raw_partition(const char *partition_name)
+{
+    MtdWriteContext *out;
+    size_t erased;
+    size_t total_size;
+    size_t erase_size;
+
+    if (mtd_scan_partitions() <= 0)
+    {
+        printf("error scanning partitions");
+        return -1;
+    }
+    const MtdPartition *p = mtd_find_partition_by_name(partition_name);
+    if (p == NULL)
+    {
+        printf("can't find %s partition", partition_name);
+        return -1;
+    }
+
+    out = mtd_write_partition(p);
+    if (out == NULL)
+    {
+        printf("could not estabilish write context for %s", partition_name);
+        return -1;
+    }
+
+    // do the actual erase, -1 = full partition erase
+    erased = mtd_erase_blocks(out, -1);
+
+    // erased = bytes erased, if zero, something borked
+    if (!erased)
+    {
+        printf("error erasing %s", partition_name);
+        return -1;
+    }
+
+    return 0;
+}
+
+int cmd_mtd_erase_partition(const char *partition, const char *filesystem)
+{
+    return cmd_mtd_erase_raw_partition(partition);
+}
+
+
+int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only)
+{
+    mtd_scan_partitions();
+    const MtdPartition *p;
+    p = mtd_find_partition_by_name(partition);
+    if (p == NULL) {
+        return -1;
+    }
+    return mtd_mount_partition(p, mount_point, filesystem, read_only);
+}
+
+int cmd_mtd_get_partition_device(const char *partition, char *device)
+{
+    mtd_scan_partitions();
+    MtdPartition *p = mtd_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    sprintf(device, "/dev/block/mtdblock%d", p->device_index);
+    return 0;
+}
diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h
index 45d3ebc..c57d45d 100644
--- a/mtdutils/mtdutils.h
+++ b/mtdutils/mtdutils.h
@@ -53,4 +53,11 @@
 off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
 int mtd_write_close(MtdWriteContext *);
 
+struct MtdPartition {
+    int device_index;
+    unsigned int size;
+    unsigned int erase_size;
+    char *name;
+};
+
 #endif  // MTDUTILS_H_
diff --git a/nandroid-md5.sh b/nandroid-md5.sh
new file mode 100755
index 0000000..03d4a46
--- /dev/null
+++ b/nandroid-md5.sh
@@ -0,0 +1,12 @@
+#!/sbin/sh
+cd $1
+rm -f /tmp/nandroid.md5
+md5sum * .* > /tmp/nandroid.md5
+cp /tmp/nandroid.md5 .
+# need this because wildcard seems to cause md5sum to return 1
+if [ -f nandroid.md5 ]
+then
+  return 0
+else
+  return 1
+fi
\ No newline at end of file
diff --git a/nandroid.c b/nandroid.c
new file mode 100644
index 0000000..315660c
--- /dev/null
+++ b/nandroid.c
@@ -0,0 +1,621 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#include "bootloader.h"
+#include "common.h"
+#include "cutils/properties.h"
+#include "firmware.h"
+#include "install.h"
+#include "minui/minui.h"
+#include "minzip/DirUtil.h"
+#include "roots.h"
+#include "recovery_ui.h"
+
+#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h"
+#include "../../external/yaffs2/yaffs2/utils/unyaffs.h"
+
+#include <sys/vfs.h>
+
+#include "extendedcommands.h"
+#include "nandroid.h"
+#include "mounts.h"
+
+#include "flashutils/flashutils.h"
+#include <libgen.h>
+
+void nandroid_generate_timestamp_path(const char* backup_path)
+{
+    time_t t = time(NULL);
+    struct tm *tmp = localtime(&t);
+    if (tmp == NULL)
+    {
+        struct timeval tp;
+        gettimeofday(&tp, NULL);
+        sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
+    }
+    else
+    {
+        strftime(backup_path, PATH_MAX, "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
+    }
+}
+
+static int print_and_error(const char* message) {
+    ui_print("%s", message);
+    return 1;
+}
+
+static int yaffs_files_total = 0;
+static int yaffs_files_count = 0;
+static void yaffs_callback(const char* filename)
+{
+    if (filename == NULL)
+        return;
+    const char* justfile = basename(filename);
+    char tmp[PATH_MAX];
+    strcpy(tmp, justfile);
+    if (tmp[strlen(tmp) - 1] == '\n')
+        tmp[strlen(tmp) - 1] = NULL;
+    if (strlen(tmp) < 30)
+        ui_print("%s", tmp);
+    yaffs_files_count++;
+    if (yaffs_files_total != 0)
+        ui_set_progress((float)yaffs_files_count / (float)yaffs_files_total);
+    ui_reset_text_col();
+}
+
+static void compute_directory_stats(const char* directory)
+{
+    char tmp[PATH_MAX];
+    sprintf(tmp, "find %s | wc -l > /tmp/dircount", directory);
+    __system(tmp);
+    char count_text[100];
+    FILE* f = fopen("/tmp/dircount", "r");
+    fread(count_text, 1, sizeof(count_text), f);
+    fclose(f);
+    yaffs_files_count = 0;
+    yaffs_files_total = atoi(count_text);
+    ui_reset_progress();
+    ui_show_progress(1, 0);
+}
+
+typedef void (*file_event_callback)(const char* filename);
+typedef int (*nandroid_backup_handler)(const char* backup_path, const char* backup_file_image, int callback);
+
+static int mkyaffs2image_wrapper(const char* backup_path, const char* backup_file_image, int callback) {
+    char backup_file_image_with_extension[PATH_MAX];
+    sprintf(backup_file_image_with_extension, "%s.img", backup_file_image);
+    return mkyaffs2image(backup_path, backup_file_image_with_extension, 0, callback ? yaffs_callback : NULL);
+}
+
+static int tar_compress_wrapper(const char* backup_path, const char* backup_file_image, int callback) {
+    char tmp[PATH_MAX];
+    if (strcmp(backup_path, "/data") == 0 && volume_for_path("/sdcard") == NULL)
+      sprintf(tmp, "cd $(dirname %s) ; tar cvf %s.tar --exclude 'media' $(basename %s) ; exit $?", backup_path, backup_file_image, backup_path);
+    else
+      sprintf(tmp, "cd $(dirname %s) ; tar cvf %s.tar $(basename %s) ; exit $?", backup_path, backup_file_image, backup_path);
+
+    FILE *fp = __popen(tmp, "r");
+    if (fp == NULL) {
+        ui_print("Unable to execute tar.\n");
+        return -1;
+    }
+
+    while (fgets(tmp, PATH_MAX, fp) != NULL) {
+        tmp[PATH_MAX - 1] = NULL;
+        if (callback)
+            yaffs_callback(tmp);
+    }
+
+    return __pclose(fp);
+}
+
+static nandroid_backup_handler get_backup_handler(const char *backup_path) {
+    Volume *v = volume_for_path(backup_path);
+    if (v == NULL) {
+        ui_print("Unable to find volume.\n");
+        return NULL;
+    }
+    MountedVolume *mv = find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv == NULL) {
+        ui_print("Unable to find mounted volume: %s\n", v->mount_point);
+        return NULL;
+    }
+
+    if (strcmp(backup_path, "/data") == 0 && is_data_media()) {
+        return tar_compress_wrapper;
+    }
+
+    // cwr5, we prefer tar for everything except yaffs2
+    if (strcmp("yaffs2", mv->filesystem) == 0) {
+        return mkyaffs2image_wrapper;
+    }
+
+    char str[255];
+    char* partition;
+    property_get("ro.cwm.prefer_tar", str, "true");
+    if (strcmp("true", str) != 0) {
+        return mkyaffs2image_wrapper;
+    }
+
+    return tar_compress_wrapper;
+}
+
+
+int nandroid_backup_partition_extended(const char* backup_path, const char* mount_point, int umount_when_finished) {
+    int ret = 0;
+    char* name = basename(mount_point);
+
+    struct stat file_info;
+    int callback = stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info) != 0;
+    
+    ui_print("Backing up %s...\n", name);
+    if (0 != (ret = ensure_path_mounted(mount_point) != 0)) {
+        ui_print("Can't mount %s!\n", mount_point);
+        return ret;
+    }
+    compute_directory_stats(mount_point);
+    char tmp[PATH_MAX];
+    scan_mounted_volumes();
+    Volume *v = volume_for_path(mount_point);
+    MountedVolume *mv = NULL;
+    if (v != NULL)
+        mv = find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv == NULL || mv->filesystem == NULL)
+        sprintf(tmp, "%s/%s.auto", backup_path, name);
+    else
+        sprintf(tmp, "%s/%s.%s", backup_path, name, mv->filesystem);
+    nandroid_backup_handler backup_handler = get_backup_handler(mount_point);
+    if (backup_handler == NULL) {
+        ui_print("Error finding an appropriate backup handler.\n");
+        return -2;
+    }
+    ret = backup_handler(mount_point, tmp, callback);
+    if (umount_when_finished) {
+        ensure_path_unmounted(mount_point);
+    }
+    if (0 != ret) {
+        ui_print("Error while making a backup image of %s!\n", mount_point);
+        return ret;
+    }
+    return 0;
+}
+
+int nandroid_backup_partition(const char* backup_path, const char* root) {
+    Volume *vol = volume_for_path(root);
+    // make sure the volume exists before attempting anything...
+    if (vol == NULL || vol->fs_type == NULL)
+        return NULL;
+
+    // see if we need a raw backup (mtd)
+    char tmp[PATH_MAX];
+    int ret;
+    if (strcmp(vol->fs_type, "mtd") == 0 ||
+            strcmp(vol->fs_type, "bml") == 0 ||
+            strcmp(vol->fs_type, "emmc") == 0) {
+        const char* name = basename(root);
+        sprintf(tmp, "%s/%s.img", backup_path, name);
+        ui_print("Backing up %s image...\n", name);
+        if (0 != (ret = backup_raw_partition(vol->fs_type, vol->device, tmp))) {
+            ui_print("Error while backing up %s image!", name);
+            return ret;
+        }
+        return 0;
+    }
+
+    return nandroid_backup_partition_extended(backup_path, root, 1);
+}
+
+int nandroid_backup(const char* backup_path)
+{
+    ui_set_background(BACKGROUND_ICON_INSTALLING);
+    
+    if (ensure_path_mounted(backup_path) != 0) {
+        return print_and_error("Can't mount backup path.\n");
+    }
+    
+    Volume* volume = volume_for_path(backup_path);
+    if (NULL == volume)
+        return print_and_error("Unable to find volume for backup path.\n");
+    int ret;
+    struct statfs s;
+    if (0 != (ret = statfs(volume->mount_point, &s)))
+        return print_and_error("Unable to stat backup path.\n");
+    uint64_t bavail = s.f_bavail;
+    uint64_t bsize = s.f_bsize;
+    uint64_t sdcard_free = bavail * bsize;
+    uint64_t sdcard_free_mb = sdcard_free / (uint64_t)(1024 * 1024);
+    ui_print("SD Card space free: %lluMB\n", sdcard_free_mb);
+    if (sdcard_free_mb < 150)
+        ui_print("There may not be enough free space to complete backup... continuing...\n");
+    
+    char tmp[PATH_MAX];
+    sprintf(tmp, "mkdir -p %s", backup_path);
+    __system(tmp);
+
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/boot")))
+        return ret;
+
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/recovery")))
+        return ret;
+
+    Volume *vol = volume_for_path("/wimax");
+    if (vol != NULL && 0 == stat(vol->device, &s))
+    {
+        char serialno[PROPERTY_VALUE_MAX];
+        ui_print("Backing up WiMAX...\n");
+        serialno[0] = 0;
+        property_get("ro.serialno", serialno, "");
+        sprintf(tmp, "%s/wimax.%s.img", backup_path, serialno);
+        ret = backup_raw_partition(vol->fs_type, vol->device, tmp);
+        if (0 != ret)
+            return print_and_error("Error while dumping WiMAX image!\n");
+    }
+
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/system")))
+        return ret;
+
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/data")))
+        return ret;
+
+    if (has_datadata()) {
+        if (0 != (ret = nandroid_backup_partition(backup_path, "/datadata")))
+            return ret;
+    }
+
+    if (0 != stat("/sdcard/.android_secure", &s))
+    {
+        ui_print("No /sdcard/.android_secure found. Skipping backup of applications on external storage.\n");
+    }
+    else
+    {
+        if (0 != (ret = nandroid_backup_partition_extended(backup_path, "/sdcard/.android_secure", 0)))
+            return ret;
+    }
+
+    if (0 != (ret = nandroid_backup_partition_extended(backup_path, "/cache", 0)))
+        return ret;
+
+    vol = volume_for_path("/sd-ext");
+    if (vol == NULL || 0 != stat(vol->device, &s))
+    {
+        ui_print("No sd-ext found. Skipping backup of sd-ext.\n");
+    }
+    else
+    {
+        if (0 != ensure_path_mounted("/sd-ext"))
+            ui_print("Could not mount sd-ext. sd-ext backup may not be supported on this device. Skipping backup of sd-ext.\n");
+        else if (0 != (ret = nandroid_backup_partition(backup_path, "/sd-ext")))
+            return ret;
+    }
+
+    ui_print("Generating md5 sum...\n");
+    sprintf(tmp, "nandroid-md5.sh %s", backup_path);
+    if (0 != (ret = __system(tmp))) {
+        ui_print("Error while generating md5 sum!\n");
+        return ret;
+    }
+    
+    sync();
+    ui_set_background(BACKGROUND_ICON_NONE);
+    ui_reset_progress();
+    ui_print("\nBackup complete!\n");
+    return 0;
+}
+
+typedef int (*format_function)(char* root);
+
+static void ensure_directory(const char* dir) {
+    char tmp[PATH_MAX];
+    sprintf(tmp, "mkdir -p %s", dir);
+    __system(tmp);
+}
+
+typedef int (*nandroid_restore_handler)(const char* backup_file_image, const char* backup_path, int callback);
+
+static int unyaffs_wrapper(const char* backup_file_image, const char* backup_path, int callback) {
+    return unyaffs(backup_file_image, backup_path, callback ? yaffs_callback : NULL);
+}
+
+static int tar_extract_wrapper(const char* backup_file_image, const char* backup_path, int callback) {
+    char tmp[PATH_MAX];
+    sprintf(tmp, "cd $(dirname %s) ; tar xvf %s ; exit $?", backup_path, backup_file_image);
+
+    char path[PATH_MAX];
+    FILE *fp = __popen(tmp, "r");
+    if (fp == NULL) {
+        ui_print("Unable to execute tar.\n");
+        return -1;
+    }
+
+    while (fgets(path, PATH_MAX, fp) != NULL) {
+        if (callback)
+            yaffs_callback(path);
+    }
+
+    return __pclose(fp);
+}
+
+static nandroid_restore_handler get_restore_handler(const char *backup_path) {
+    Volume *v = volume_for_path(backup_path);
+    if (v == NULL) {
+        ui_print("Unable to find volume.\n");
+        return NULL;
+    }
+    scan_mounted_volumes();
+    MountedVolume *mv = find_mounted_volume_by_mount_point(v->mount_point);
+    if (mv == NULL) {
+        ui_print("Unable to find mounted volume: %s\n", v->mount_point);
+        return NULL;
+    }
+
+    if (strcmp(backup_path, "/data") == 0 && is_data_media()) {
+        return tar_extract_wrapper;
+    }
+
+    // cwr 5, we prefer tar for everything unless it is yaffs2
+    char str[255];
+    char* partition;
+    property_get("ro.cwm.prefer_tar", str, "false");
+    if (strcmp("true", str) != 0) {
+        return unyaffs_wrapper;
+    }
+
+    if (strcmp("yaffs2", mv->filesystem) == 0) {
+        return unyaffs_wrapper;
+    }
+
+    return tar_extract_wrapper;
+}
+
+int nandroid_restore_partition_extended(const char* backup_path, const char* mount_point, int umount_when_finished) {
+    int ret = 0;
+    char* name = basename(mount_point);
+
+    nandroid_restore_handler restore_handler = NULL;
+    const char *filesystems[] = { "yaffs2", "ext2", "ext3", "ext4", "vfat", "rfs", NULL };
+    const char* backup_filesystem = NULL;
+    Volume *vol = volume_for_path(mount_point);
+    const char *device = NULL;
+    if (vol != NULL)
+        device = vol->device;
+
+    char tmp[PATH_MAX];
+    sprintf(tmp, "%s/%s.img", backup_path, name);
+    struct stat file_info;
+    if (0 != (ret = statfs(tmp, &file_info))) {
+        // can't find the backup, it may be the new backup format?
+        // iterate through the backup types
+        printf("couldn't find default\n");
+        char *filesystem;
+        int i = 0;
+        while ((filesystem = filesystems[i]) != NULL) {
+            sprintf(tmp, "%s/%s.%s.img", backup_path, name, filesystem);
+            if (0 == (ret = statfs(tmp, &file_info))) {
+                backup_filesystem = filesystem;
+                restore_handler = unyaffs_wrapper;
+                break;
+            }
+            sprintf(tmp, "%s/%s.%s.tar", backup_path, name, filesystem);
+            if (0 == (ret = statfs(tmp, &file_info))) {
+                backup_filesystem = filesystem;
+                restore_handler = tar_extract_wrapper;
+                break;
+            }
+            i++;
+        }
+
+        if (backup_filesystem == NULL || restore_handler == NULL) {
+            ui_print("%s.img not found. Skipping restore of %s.\n", name, mount_point);
+            return 0;
+        }
+        else {
+            printf("Found new backup image: %s\n", tmp);
+        }
+
+        // If the fs_type of this volume is "auto", let's revert to using a
+        // rm -rf, rather than trying to do a ext3/ext4/whatever format.
+        // This is because some phones (like DroidX) will freak out if you
+        // reformat the /system or /data partitions, and not boot due to
+        // a locked bootloader.
+        // The "auto" fs type preserves the file system, and does not
+        // trigger that lock.
+        // Or of volume does not exist (.android_secure), just rm -rf.
+        if (vol == NULL || 0 == strcmp(vol->fs_type, "auto"))
+            backup_filesystem = NULL;
+    }
+
+    ensure_directory(mount_point);
+
+    int callback = stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info) != 0;
+
+    ui_print("Restoring %s...\n", name);
+    if (backup_filesystem == NULL) {
+        if (0 != (ret = format_volume(mount_point))) {
+            ui_print("Error while formatting %s!\n", mount_point);
+            return ret;
+        }
+    }
+    else if (0 != (ret = format_device(device, mount_point, backup_filesystem))) {
+        ui_print("Error while formatting %s!\n", mount_point);
+        return ret;
+    }
+
+    if (0 != (ret = ensure_path_mounted(mount_point))) {
+        ui_print("Can't mount %s!\n", mount_point);
+        return ret;
+    }
+
+    if (restore_handler == NULL)
+        restore_handler = get_restore_handler(mount_point);
+    if (restore_handler == NULL) {
+        ui_print("Error finding an appropriate restore handler.\n");
+        return -2;
+    }
+    if (0 != (ret = restore_handler(tmp, mount_point, callback))) {
+        ui_print("Error while restoring %s!\n", mount_point);
+        return ret;
+    }
+
+    if (umount_when_finished) {
+        ensure_path_unmounted(mount_point);
+    }
+    
+    return 0;
+}
+
+int nandroid_restore_partition(const char* backup_path, const char* root) {
+    Volume *vol = volume_for_path(root);
+    // make sure the volume exists...
+    if (vol == NULL || vol->fs_type == NULL)
+        return 0;
+
+    // see if we need a raw restore (mtd)
+    char tmp[PATH_MAX];
+    if (strcmp(vol->fs_type, "mtd") == 0 ||
+            strcmp(vol->fs_type, "bml") == 0 ||
+            strcmp(vol->fs_type, "emmc") == 0) {
+        int ret;
+        const char* name = basename(root);
+        ui_print("Erasing %s before restore...\n", name);
+        if (0 != (ret = format_volume(root))) {
+            ui_print("Error while erasing %s image!", name);
+            return ret;
+        }
+        sprintf(tmp, "%s%s.img", backup_path, root);
+        ui_print("Restoring %s image...\n", name);
+        if (0 != (ret = restore_raw_partition(vol->fs_type, vol->device, tmp))) {
+            ui_print("Error while flashing %s image!", name);
+            return ret;
+        }
+        return 0;
+    }
+    return nandroid_restore_partition_extended(backup_path, root, 1);
+}
+
+int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext, int restore_wimax)
+{
+    ui_set_background(BACKGROUND_ICON_INSTALLING);
+    ui_show_indeterminate_progress();
+    yaffs_files_total = 0;
+
+    if (ensure_path_mounted(backup_path) != 0)
+        return print_and_error("Can't mount backup path\n");
+    
+    char tmp[PATH_MAX];
+
+    ui_print("Checking MD5 sums...\n");
+    sprintf(tmp, "cd %s && md5sum -c nandroid.md5", backup_path);
+    if (0 != __system(tmp))
+        return print_and_error("MD5 mismatch!\n");
+    
+    int ret;
+
+    if (restore_boot && NULL != volume_for_path("/boot") && 0 != (ret = nandroid_restore_partition(backup_path, "/boot")))
+        return ret;
+    
+    struct stat s;
+    Volume *vol = volume_for_path("/wimax");
+    if (restore_wimax && vol != NULL && 0 == stat(vol->device, &s))
+    {
+        char serialno[PROPERTY_VALUE_MAX];
+        
+        serialno[0] = 0;
+        property_get("ro.serialno", serialno, "");
+        sprintf(tmp, "%s/wimax.%s.img", backup_path, serialno);
+
+        struct stat st;
+        if (0 != stat(tmp, &st))
+        {
+            ui_print("WARNING: WiMAX partition exists, but nandroid\n");
+            ui_print("         backup does not contain WiMAX image.\n");
+            ui_print("         You should create a new backup to\n");
+            ui_print("         protect your WiMAX keys.\n");
+        }
+        else
+        {
+            ui_print("Erasing WiMAX before restore...\n");
+            if (0 != (ret = format_volume("/wimax")))
+                return print_and_error("Error while formatting wimax!\n");
+            ui_print("Restoring WiMAX image...\n");
+            if (0 != (ret = restore_raw_partition(vol->fs_type, vol->device, tmp)))
+                return ret;
+        }
+    }
+
+    if (restore_system && 0 != (ret = nandroid_restore_partition(backup_path, "/system")))
+        return ret;
+
+    if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "/data")))
+        return ret;
+        
+    if (has_datadata()) {
+        if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "/datadata")))
+            return ret;
+    }
+
+    if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, "/sdcard/.android_secure", 0)))
+        return ret;
+
+    if (restore_cache && 0 != (ret = nandroid_restore_partition_extended(backup_path, "/cache", 0)))
+        return ret;
+
+    if (restore_sdext && 0 != (ret = nandroid_restore_partition(backup_path, "/sd-ext")))
+        return ret;
+
+    sync();
+    ui_set_background(BACKGROUND_ICON_NONE);
+    ui_reset_progress();
+    ui_print("\nRestore complete!\n");
+    return 0;
+}
+
+int nandroid_usage()
+{
+    printf("Usage: nandroid backup\n");
+    printf("Usage: nandroid restore <directory>\n");
+    return 1;
+}
+
+int nandroid_main(int argc, char** argv)
+{
+    if (argc > 3 || argc < 2)
+        return nandroid_usage();
+    
+    if (strcmp("backup", argv[1]) == 0)
+    {
+        if (argc != 2)
+            return nandroid_usage();
+        
+        char backup_path[PATH_MAX];
+        nandroid_generate_timestamp_path(backup_path);
+        return nandroid_backup(backup_path);
+    }
+
+    if (strcmp("restore", argv[1]) == 0)
+    {
+        if (argc != 3)
+            return nandroid_usage();
+        return nandroid_restore(argv[2], 1, 1, 1, 1, 1, 0);
+    }
+    
+    return nandroid_usage();
+}
diff --git a/nandroid.h b/nandroid.h
new file mode 100644
index 0000000..836f192
--- /dev/null
+++ b/nandroid.h
@@ -0,0 +1,8 @@
+#ifndef NANDROID_H
+#define NANDROID_H
+
+int nandroid_main(int argc, char** argv);
+int nandroid_backup(const char* backup_path);
+int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext, int restore_wimax);
+
+#endif
\ No newline at end of file
diff --git a/recovery.c b/recovery.c
index 1e3eb5a..2b3543e 100644
--- a/recovery.c
+++ b/recovery.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +29,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <dirent.h>
+#include <sys/stat.h>
 
 #include "bootloader.h"
 #include "common.h"
@@ -39,6 +41,9 @@
 #include "roots.h"
 #include "recovery_ui.h"
 
+#include "extendedcommands.h"
+#include "flashutils/flashutils.h"
+
 static const struct option OPTIONS[] = {
   { "send_intent", required_argument, NULL, 's' },
   { "update_package", required_argument, NULL, 'u' },
@@ -54,6 +59,9 @@
 static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
 static const char *CACHE_ROOT = "/cache";
 static const char *SDCARD_ROOT = "/sdcard";
+static int allow_display_toggle = 1;
+static int poweroff = 0;
+static const char *SDCARD_PACKAGE_FILE = "/sdcard/update.zip";
 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
 static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
 
@@ -133,6 +141,7 @@
     if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1);
 
     FILE *fp = fopen(path, mode);
+    if (fp == NULL && path != COMMAND_FILE) LOGE("Can't open %s\n", path);
     return fp;
 }
 
@@ -152,7 +161,9 @@
 get_args(int *argc, char ***argv) {
     struct bootloader_message boot;
     memset(&boot, 0, sizeof(boot));
-    get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure
+    if (device_flash_type() == MTD) {
+        get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure
+    }
 
     if (boot.command[0] != 0 && boot.command[0] != 255) {
         LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
@@ -162,8 +173,10 @@
         LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
     }
 
+    struct stat file_info;
+
     // --- if arguments weren't supplied, look in the bootloader control block
-    if (*argc <= 1) {
+    if (*argc <= 1 && 0 != stat("/tmp/.ignorebootmessage", &file_info)) {
         boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
         const char *arg = strtok(boot.recovery, "\n");
         if (arg != NULL && !strcmp(arg, "recovery")) {
@@ -207,10 +220,12 @@
         strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
         strlcat(boot.recovery, "\n", sizeof(boot.recovery));
     }
-    set_bootloader_message(&boot);
+    if (device_flash_type() == MTD) {
+        set_bootloader_message(&boot);
+    }
 }
 
-static void
+void
 set_sdcard_update_bootloader_message() {
     struct bootloader_message boot;
     memset(&boot, 0, sizeof(boot));
@@ -269,10 +284,12 @@
     copy_log_file(LAST_LOG_FILE, false);
     chmod(LAST_LOG_FILE, 0640);
 
-    // Reset to mormal system boot so recovery won't cycle indefinitely.
-    struct bootloader_message boot;
-    memset(&boot, 0, sizeof(boot));
-    set_bootloader_message(&boot);
+    if (device_flash_type() == MTD) {
+        // Reset to mormal system boot so recovery won't cycle indefinitely.
+        struct bootloader_message boot;
+        memset(&boot, 0, sizeof(boot));
+        set_bootloader_message(&boot);
+    }
 
     // Remove the command file, so recovery won't repeat indefinitely.
     if (ensure_path_mounted(COMMAND_FILE) != 0 ||
@@ -390,9 +407,8 @@
 }
 
 static char**
-prepend_title(const char** headers) {
-    char* title[] = { "Android system recovery <"
-                          EXPAND(RECOVERY_API_VERSION) "e>",
+prepend_title(char** headers) {
+    char* title[] = { EXPAND(RECOVERY_VERSION),
                       "",
                       NULL };
 
@@ -412,18 +428,23 @@
     return new_headers;
 }
 
-static int
+int
 get_menu_selection(char** headers, char** items, int menu_only,
                    int initial_selection) {
     // throw away keys pressed previously, so user doesn't
     // accidentally trigger menu items.
     ui_clear_key_queue();
 
-    ui_start_menu(headers, items, initial_selection);
+    int item_count = ui_start_menu(headers, items, initial_selection);
     int selected = initial_selection;
     int chosen_item = -1;
 
-    while (chosen_item < 0) {
+    // Some users with dead enter keys need a way to turn on power to select.
+    // Jiggering across the wrapping menu is one "secret" way to enable it.
+    // We can't rely on /cache or /sdcard since they may not be available.
+    int wrap_count = 0;
+
+    while (chosen_item < 0 && chosen_item != GO_BACK) {
         int key = ui_wait_key();
         int visible = ui_text_visible();
 
@@ -439,6 +460,8 @@
 
         int action = device_handle_key(key, visible);
 
+        int old_selected = selected;
+
         if (action < 0) {
             switch (action) {
                 case HIGHLIGHT_UP:
@@ -451,16 +474,40 @@
                     break;
                 case SELECT_ITEM:
                     chosen_item = selected;
+                    if (ui_get_showing_back_button()) {
+                        if (chosen_item == item_count) {
+                            chosen_item = GO_BACK;
+                        }
+                    }
                     break;
                 case NO_ACTION:
                     break;
+                case GO_BACK:
+                    chosen_item = GO_BACK;
+                    break;
             }
         } else if (!menu_only) {
             chosen_item = action;
         }
+
+        if (abs(selected - old_selected) > 1) {
+            wrap_count++;
+            if (wrap_count == 3) {
+                wrap_count = 0;
+                if (ui_get_showing_back_button()) {
+                    ui_print("Back menu button disabled.\n");
+                    ui_set_showing_back_button(0);
+                }
+                else {
+                    ui_print("Back menu button enabled.\n");
+                    ui_set_showing_back_button(1);
+                }
+            }
+        }
     }
 
     ui_end_menu();
+    ui_clear_key_queue();
     return chosen_item;
 }
 
@@ -631,6 +678,11 @@
     device_wipe_data();
     erase_volume("/data");
     erase_volume("/cache");
+    if (has_datadata()) {
+        erase_volume("/datadata");
+    }
+    erase_volume("/sd-ext");
+    erase_volume("/sdcard/.android_secure");
     ui_print("Data wipe complete.\n");
 }
 
@@ -642,7 +694,9 @@
         finish_recovery(NULL);
         ui_reset_progress();
 
+        allow_display_toggle = 1;
         int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0);
+        allow_display_toggle = 0;
 
         // device-specific code may take some action here.  It may
         // return one of the core actions handled in the switch
@@ -652,6 +706,7 @@
         int status;
         switch (chosen_item) {
             case ITEM_REBOOT:
+                poweroff=0;
                 return;
 
             case ITEM_WIPE_DATA:
@@ -660,40 +715,34 @@
                 break;
 
             case ITEM_WIPE_CACHE:
-                ui_print("\n-- Wiping cache...\n");
-                erase_volume("/cache");
-                ui_print("Cache wipe complete.\n");
-                if (!ui_text_visible()) return;
+                if (confirm_selection("Confirm wipe?", "Yes - Wipe Cache"))
+                {
+                    ui_print("\n-- Wiping cache...\n");
+                    erase_volume("/cache");
+                    ui_print("Cache wipe complete.\n");
+                    if (!ui_text_visible()) return;
+                }
                 break;
 
             case ITEM_APPLY_SDCARD:
-                status = update_directory(SDCARD_ROOT, SDCARD_ROOT);
-                if (status >= 0) {
-                    if (status != INSTALL_SUCCESS) {
-                        ui_set_background(BACKGROUND_ICON_ERROR);
-                        ui_print("Installation aborted.\n");
-                    } else if (!ui_text_visible()) {
-                        return;  // reboot if logs aren't visible
-                    } else {
-                        ui_print("\nInstall from sdcard complete.\n");
-                    }
-                }
-                break;
-            case ITEM_APPLY_CACHE:
-                // Don't unmount cache at the end of this.
-                status = update_directory(CACHE_ROOT, NULL);
-                if (status >= 0) {
-                    if (status != INSTALL_SUCCESS) {
-                        ui_set_background(BACKGROUND_ICON_ERROR);
-                        ui_print("Installation aborted.\n");
-                    } else if (!ui_text_visible()) {
-                        return;  // reboot if logs aren't visible
-                    } else {
-                        ui_print("\nInstall from cache complete.\n");
-                    }
-                }
+                show_install_update_menu();
                 break;
 
+            case ITEM_NANDROID:
+                show_nandroid_menu();
+                break;
+
+            case ITEM_PARTITION:
+                show_partition_menu();
+                break;
+
+            case ITEM_ADVANCED:
+                show_advanced_menu();
+                break;
+                
+            case ITEM_POWEROFF:
+                poweroff = 1;
+                return;
         }
     }
 }
@@ -705,6 +754,43 @@
 
 int
 main(int argc, char **argv) {
+	if (strcmp(basename(argv[0]), "recovery") != 0)
+	{
+	    if (strstr(argv[0], "flash_image") != NULL)
+	        return flash_image_main(argc, argv);
+	    if (strstr(argv[0], "volume") != NULL)
+	        return volume_main(argc, argv);
+	    if (strstr(argv[0], "edify") != NULL)
+	        return edify_main(argc, argv);
+	    if (strstr(argv[0], "dump_image") != NULL)
+	        return dump_image_main(argc, argv);
+	    if (strstr(argv[0], "erase_image") != NULL)
+	        return erase_image_main(argc, argv);
+	    if (strstr(argv[0], "mkyaffs2image") != NULL)
+	        return mkyaffs2image_main(argc, argv);
+	    if (strstr(argv[0], "unyaffs") != NULL)
+	        return unyaffs_main(argc, argv);
+        if (strstr(argv[0], "nandroid"))
+            return nandroid_main(argc, argv);
+        if (strstr(argv[0], "reboot"))
+            return reboot_main(argc, argv);
+#ifdef BOARD_RECOVERY_HANDLES_MOUNT
+        if (strstr(argv[0], "mount") && argc == 2 && !strstr(argv[0], "umount"))
+        {
+            load_volume_table();
+            return ensure_path_mounted(argv[1]);
+        }
+#endif
+        if (strstr(argv[0], "poweroff")){
+            return reboot_main(argc, argv);
+        }
+        if (strstr(argv[0], "setprop"))
+            return setprop_main(argc, argv);
+		return busybox_driver(argc, argv);
+	}
+    __system("/sbin/postrecoveryboot.sh");
+
+    int is_user_initiated_recovery = 0;
     time_t start = time(NULL);
 
     // If these fail, there's not really anywhere to complain...
@@ -714,8 +800,10 @@
 
     device_ui_init(&ui_parameters);
     ui_init();
-    ui_set_background(BACKGROUND_ICON_INSTALLING);
+    ui_print(EXPAND(RECOVERY_VERSION)"\n");
     load_volume_table();
+    process_volumes();
+    LOGI("Processing arguments.\n");
     get_args(&argc, &argv);
 
     int previous_runs = 0;
@@ -723,13 +811,18 @@
     const char *update_package = NULL;
     int wipe_data = 0, wipe_cache = 0;
 
+    LOGI("Checking arguments.\n");
     int arg;
     while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
         switch (arg) {
         case 'p': previous_runs = atoi(optarg); break;
         case 's': send_intent = optarg; break;
         case 'u': update_package = optarg; break;
-        case 'w': wipe_data = wipe_cache = 1; break;
+        case 'w': 
+#ifndef BOARD_RECOVERY_ALWAYS_WIPES
+		wipe_data = wipe_cache = 1;
+#endif
+		break;
         case 'c': wipe_cache = 1; break;
         case 't': ui_show_text(1); break;
         case '?':
@@ -738,6 +831,7 @@
         }
     }
 
+    LOGI("device_recovery_start()\n");
     device_recovery_start();
 
     printf("Command:");
@@ -779,17 +873,54 @@
         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
         if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");
     } else {
+        LOGI("Checking for extendedcommand...\n");
         status = INSTALL_ERROR;  // No command specified
+        // we are starting up in user initiated recovery here
+        // let's set up some default options
+        signature_check_enabled = 0;
+        script_assert_enabled = 0;
+        is_user_initiated_recovery = 1;
+        ui_set_show_text(1);
+        ui_set_background(BACKGROUND_ICON_CLOCKWORK);
+        
+        if (extendedcommand_file_exists()) {
+            LOGI("Running extendedcommand...\n");
+            int ret;
+            if (0 == (ret = run_and_remove_extendedcommand())) {
+                status = INSTALL_SUCCESS;
+                ui_set_show_text(0);
+            }
+            else {
+                handle_failure(ret);
+            }
+        } else {
+            LOGI("Skipping execution of extendedcommand, file not found...\n");
+        }
     }
 
-    if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
+    if (status != INSTALL_SUCCESS && !is_user_initiated_recovery) ui_set_background(BACKGROUND_ICON_ERROR);
     if (status != INSTALL_SUCCESS || ui_text_visible()) {
         prompt_and_wait();
     }
 
+    // If there is a radio image pending, reboot now to install it.
+    maybe_install_firmware_update(send_intent);
+
     // Otherwise, get ready to boot the main system...
     finish_recovery(send_intent);
-    ui_print("Rebooting...\n");
-    android_reboot(ANDROID_RB_RESTART, 0, 0);
+
+    sync();
+    if(!poweroff) {
+        ui_print("Rebooting...\n");
+        android_reboot(ANDROID_RB_RESTART, 0, 0);
+    }
+    else {
+        ui_print("Shutting down...\n");
+        android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+    }
     return EXIT_SUCCESS;
 }
+
+int get_allow_toggle_display() {
+    return allow_display_toggle;
+}
diff --git a/recovery_ui.h b/recovery_ui.h
index 5f01770..ec5f4fa 100644
--- a/recovery_ui.h
+++ b/recovery_ui.h
@@ -70,13 +70,19 @@
 #define HIGHLIGHT_UP        -2
 #define HIGHLIGHT_DOWN      -3
 #define SELECT_ITEM         -4
+#define GO_BACK             -5
 
 #define ITEM_REBOOT          0
 #define ITEM_APPLY_EXT       1
 #define ITEM_APPLY_SDCARD    1  // historical synonym for ITEM_APPLY_EXT
 #define ITEM_WIPE_DATA       2
 #define ITEM_WIPE_CACHE      3
+// unused in cwr
 #define ITEM_APPLY_CACHE     4
+#define ITEM_NANDROID        4
+#define ITEM_PARTITION       5
+#define ITEM_ADVANCED        6
+#define ITEM_POWEROFF        7
 
 // Header text to display above the main menu.
 extern char* MENU_HEADERS[];
@@ -84,4 +90,10 @@
 // Text of menu items.
 extern char* MENU_ITEMS[];
 
+int
+get_menu_selection(char** headers, char** items, int menu_only, int initial_selection);
+
+void
+set_sdcard_update_bootloader_message();
+
 #endif
diff --git a/res/images/icon_clockwork.png b/res/images/icon_clockwork.png
new file mode 100644
index 0000000..e696bc5
--- /dev/null
+++ b/res/images/icon_clockwork.png
Binary files differ
diff --git a/res/images/icon_firmware_error.png b/res/images/icon_firmware_error.png
new file mode 100644
index 0000000..0c32c9e
--- /dev/null
+++ b/res/images/icon_firmware_error.png
Binary files differ
diff --git a/res/images/icon_firmware_install.png b/res/images/icon_firmware_install.png
new file mode 100755
index 0000000..2da9e5f
--- /dev/null
+++ b/res/images/icon_firmware_install.png
Binary files differ
diff --git a/roots.c b/roots.c
index cb7e067..e28eedc 100644
--- a/roots.c
+++ b/roots.c
@@ -23,13 +23,38 @@
 #include <ctype.h>
 
 #include "mtdutils/mtdutils.h"
-#include "mtdutils/mounts.h"
+#include "mounts.h"
 #include "roots.h"
 #include "common.h"
 #include "make_ext4fs.h"
 
-static int num_volumes = 0;
-static Volume* device_volumes = NULL;
+#include "flashutils/flashutils.h"
+#include "extendedcommands.h"
+
+int num_volumes;
+Volume* device_volumes;
+
+int get_num_volumes() {
+    return num_volumes;
+}
+
+Volume* get_device_volumes() {
+    return device_volumes;
+}
+
+static int is_null(const char* sz) {
+    if (sz == NULL)
+        return 1;
+    if (strcmp("NULL", sz) == 0)
+        return 1;
+    return 0;
+}
+
+static char* dupe_string(const char* sz) {
+    if (is_null(sz))
+        return NULL;
+    return strdup(sz);
+}
 
 static int parse_options(char* options, Volume* volume) {
     char* option;
@@ -38,6 +63,13 @@
 
         if (strncmp(option, "length=", 7) == 0) {
             volume->length = strtoll(option+7, NULL, 10);
+        } else if (strncmp(option, "fstype2=", 8) == 0) {
+            volume->fs_type2 = volume->fs_type;
+            volume->fs_type = strdup(option);
+        } else if (strncmp(option, "fs_options=", 11) == 0) {
+            volume->fs_options = strdup(option);
+        } else if (strncmp(option, "fs_options2=", 12) == 0) {
+            volume->fs_options2 = strdup(option);
         } else {
             LOGE("bad option \"%s\"\n", option);
             return -1;
@@ -55,6 +87,9 @@
     device_volumes[0].fs_type = "ramdisk";
     device_volumes[0].device = NULL;
     device_volumes[0].device2 = NULL;
+    device_volumes[0].fs_type2 = NULL;
+    device_volumes[0].fs_options = NULL;
+    device_volumes[0].fs_options2 = NULL;
     device_volumes[0].length = 0;
     num_volumes = 1;
 
@@ -136,9 +171,52 @@
     return NULL;
 }
 
+int try_mount(const char* device, const char* mount_point, const char* fs_type, const char* fs_options) {
+    if (device == NULL || mount_point == NULL || fs_type == NULL)
+        return -1;
+    int ret = 0;
+    if (fs_options == NULL) {
+        ret = mount(device, mount_point, fs_type,
+                       MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
+    }
+    else {
+        char mount_cmd[PATH_MAX];
+        sprintf(mount_cmd, "mount -t %s -o%s %s %s", fs_type, fs_options, device, mount_point);
+        ret = __system(mount_cmd);
+    }
+    if (ret == 0)
+        return 0;
+    LOGW("failed to mount %s (%s)\n", device, strerror(errno));
+    return ret;
+}
+
+int is_data_media() {
+    Volume *data = volume_for_path("/data");
+    return data != NULL && strcmp(data->fs_type, "auto") == 0 && volume_for_path("/sdcard") == NULL;
+}
+
+void setup_data_media() {
+    rmdir("/sdcard");
+    mkdir("/data/media", 0755);
+    symlink("/data/media", "/sdcard");
+}
+
 int ensure_path_mounted(const char* path) {
+    return ensure_path_mounted_at_mount_point(path, NULL);
+}
+
+int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point) {
     Volume* v = volume_for_path(path);
     if (v == NULL) {
+        // no /sdcard? let's assume /data/media
+        if (strstr(path, "/sdcard") == path && is_data_media()) {
+            LOGW("using /data/media, no /sdcard found.\n");
+            int ret;
+            if (0 != (ret = ensure_path_mounted("/data")))
+                return ret;
+            setup_data_media();
+            return 0;
+        }
         LOGE("unknown volume for path [%s]\n", path);
         return -1;
     }
@@ -154,14 +232,17 @@
         return -1;
     }
 
+    if (NULL == mount_point)
+        mount_point = v->mount_point;
+
     const MountedVolume* mv =
-        find_mounted_volume_by_mount_point(v->mount_point);
+        find_mounted_volume_by_mount_point(mount_point);
     if (mv) {
         // volume is already mounted
         return 0;
     }
 
-    mkdir(v->mount_point, 0755);  // in case it doesn't already exist
+    mkdir(mount_point, 0755);  // in case it doesn't already exist
 
     if (strcmp(v->fs_type, "yaffs2") == 0) {
         // mount an MTD partition as a YAFFS2 filesystem.
@@ -170,35 +251,46 @@
         partition = mtd_find_partition_by_name(v->device);
         if (partition == NULL) {
             LOGE("failed to find \"%s\" partition to mount at \"%s\"\n",
-                 v->device, v->mount_point);
+                 v->device, mount_point);
             return -1;
         }
-        return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0);
+        return mtd_mount_partition(partition, mount_point, v->fs_type, 0);
     } else if (strcmp(v->fs_type, "ext4") == 0 ||
+               strcmp(v->fs_type, "ext3") == 0 ||
+               strcmp(v->fs_type, "rfs") == 0 ||
                strcmp(v->fs_type, "vfat") == 0) {
-        result = mount(v->device, v->mount_point, v->fs_type,
-                       MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
-        if (result == 0) return 0;
-
-        if (v->device2) {
-            LOGW("failed to mount %s (%s); trying %s\n",
-                 v->device, strerror(errno), v->device2);
-            result = mount(v->device2, v->mount_point, v->fs_type,
-                           MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");
-            if (result == 0) return 0;
-        }
-
-        LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));
-        return -1;
+        if ((result = try_mount(v->device, mount_point, v->fs_type, v->fs_options)) == 0)
+            return 0;
+        if ((result = try_mount(v->device2, mount_point, v->fs_type, v->fs_options)) == 0)
+            return 0;
+        if ((result = try_mount(v->device, mount_point, v->fs_type2, v->fs_options2)) == 0)
+            return 0;
+        if ((result = try_mount(v->device2, mount_point, v->fs_type2, v->fs_options2)) == 0)
+            return 0;
+        return result;
+    } else {
+        // let's try mounting with the mount binary and hope for the best.
+        char mount_cmd[PATH_MAX];
+        sprintf(mount_cmd, "mount %s", path);
+        return __system(mount_cmd);
     }
 
-    LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point);
+    LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, mount_point);
     return -1;
 }
 
 int ensure_path_unmounted(const char* path) {
+    // if we are using /data/media, do not ever unmount volumes /data or /sdcard
+    if (volume_for_path("/sdcard") == NULL && (strstr(path, "/sdcard") == path || strstr(path, "/data") == path)) {
+        return 0;
+    }
+
     Volume* v = volume_for_path(path);
     if (v == NULL) {
+        // no /sdcard? let's assume /data/media
+        if (strstr(path, "/sdcard") == path && is_data_media()) {
+            return ensure_path_unmounted("/data");
+        }
         LOGE("unknown volume for path [%s]\n", path);
         return -1;
     }
@@ -227,6 +319,13 @@
 int format_volume(const char* volume) {
     Volume* v = volume_for_path(volume);
     if (v == NULL) {
+        // no /sdcard? let's assume /data/media
+        if (strstr(volume, "/sdcard") == volume && is_data_media()) {
+            return format_unknown_device(NULL, volume, NULL);
+        }
+        // silent failure for sd-ext
+        if (strcmp(volume, "/sd-ext") == 0)
+            return -1;
         LOGE("unknown volume \"%s\"\n", volume);
         return -1;
     }
@@ -236,8 +335,11 @@
         return -1;
     }
     if (strcmp(v->mount_point, volume) != 0) {
+#if 0
         LOGE("can't give path \"%s\" to format_volume\n", volume);
         return -1;
+#endif
+        return format_unknown_device(v->device, volume, NULL);
     }
 
     if (ensure_path_unmounted(volume) != 0) {
@@ -277,6 +379,9 @@
         return 0;
     }
 
+#if 0
     LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type);
     return -1;
+#endif
+    return format_unknown_device(v->device, volume, v->fs_type);
 }
diff --git a/roots.h b/roots.h
index cf59bfd..af82280 100644
--- a/roots.h
+++ b/roots.h
@@ -28,6 +28,7 @@
 // Make sure that the volume 'path' is on is mounted.  Returns 0 on
 // success (volume is mounted).
 int ensure_path_mounted(const char* path);
+int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point);
 
 // Make sure that the volume 'path' is on is mounted.  Returns 0 on
 // success (volume is unmounted);
@@ -38,4 +39,11 @@
 // it is mounted.
 int format_volume(const char* volume);
 
+int get_num_volumes();
+
+Volume* get_device_volumes();
+
+int is_data_media();
+void setup_data_media();
+
 #endif  // RECOVERY_ROOTS_H_
diff --git a/setprop.c b/setprop.c
new file mode 100644
index 0000000..63ad2b4
--- /dev/null
+++ b/setprop.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+#include <cutils/properties.h>
+
+int setprop_main(int argc, char *argv[])
+{
+    if(argc != 3) {
+        fprintf(stderr,"usage: setprop <key> <value>\n");
+        return 1;
+    }
+
+    if(property_set(argv[1], argv[2])){
+        fprintf(stderr,"could not set property\n");
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/ui.c b/ui.c
index 25df3d0..5a9895e 100644
--- a/ui.c
+++ b/ui.c
@@ -33,11 +33,27 @@
 #include "minui/minui.h"
 #include "recovery_ui.h"
 
+extern int __system(const char *command);
+
+#ifdef BOARD_HAS_NO_SELECT_BUTTON
+static int gShowBackButton = 1;
+#else
+static int gShowBackButton = 0;
+#endif
+
 #define MAX_COLS 96
 #define MAX_ROWS 32
 
-#define CHAR_WIDTH 10
-#define CHAR_HEIGHT 18
+#define MENU_MAX_COLS 64
+#define MENU_MAX_ROWS 250
+
+#ifndef BOARD_LDPI_RECOVERY
+  #define CHAR_WIDTH 10
+  #define CHAR_HEIGHT 18
+#else
+  #define CHAR_WIDTH 7
+  #define CHAR_HEIGHT 16
+#endif
 
 #define UI_WAIT_KEY_TIMEOUT_SEC    120
 
@@ -54,10 +70,15 @@
 static gr_surface *gProgressBarIndeterminate;
 static gr_surface gProgressBarEmpty;
 static gr_surface gProgressBarFill;
+static int ui_has_initialized = 0;
+static int ui_log_stdout = 1;
 
 static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
     { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" },
     { &gBackgroundIcon[BACKGROUND_ICON_ERROR],      "icon_error" },
+    { &gBackgroundIcon[BACKGROUND_ICON_CLOCKWORK],  "icon_clockwork" },
+    { &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING], "icon_firmware_install" },
+    { &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_ERROR], "icon_firmware_error" },
     { &gProgressBarEmpty,               "progress_empty" },
     { &gProgressBarFill,                "progress_fill" },
     { NULL,                             NULL },
@@ -86,9 +107,10 @@
 static int show_text = 0;
 static int show_text_ever = 0;   // has show_text ever been 1?
 
-static char menu[MAX_ROWS][MAX_COLS];
+static char menu[MENU_MAX_ROWS][MENU_MAX_COLS];
 static int show_menu = 0;
 static int menu_top = 0, menu_items = 0, menu_sel = 0;
+static int menu_show_start = 0;             // this is line which menu display is starting at
 
 // Key event input queue
 static pthread_mutex_t key_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -185,10 +207,16 @@
   }
 }
 
+//#define MENU_TEXT_COLOR 255, 160, 49, 255
+#define MENU_TEXT_COLOR 0, 191, 255, 255
+#define NORMAL_TEXT_COLOR 200, 200, 200, 255
+#define HEADER_TEXT_COLOR NORMAL_TEXT_COLOR
+
 // Redraw everything on the screen.  Does not flip pages.
 // Should only be called with gUpdateMutex locked.
 static void draw_screen_locked(void)
 {
+    if (!ui_has_initialized) return;
     draw_background_locked(gCurrentIcon);
     draw_progress_locked();
 
@@ -197,29 +225,43 @@
         gr_fill(0, 0, gr_fb_width(), gr_fb_height());
 
         int i = 0;
+        int j = 0;
+        int row = 0;            // current row that we are drawing on
         if (show_menu) {
-            gr_color(64, 96, 255, 255);
-            gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT,
-                    gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1);
+            gr_color(MENU_TEXT_COLOR);
+            gr_fill(0, (menu_top + menu_sel - menu_show_start) * CHAR_HEIGHT,
+                    gr_fb_width(), (menu_top + menu_sel - menu_show_start + 1)*CHAR_HEIGHT+1);
 
-            for (; i < menu_top + menu_items; ++i) {
+            gr_color(HEADER_TEXT_COLOR);
+            for (i = 0; i < menu_top; ++i) {
+                draw_text_line(i, menu[i]);
+                row++;
+            }
+
+            if (menu_items - menu_show_start + menu_top >= MAX_ROWS)
+                j = MAX_ROWS - menu_top;
+            else
+                j = menu_items - menu_show_start;
+
+            gr_color(MENU_TEXT_COLOR);
+            for (i = menu_show_start + menu_top; i < (menu_show_start + menu_top + j); ++i) {
                 if (i == menu_top + menu_sel) {
                     gr_color(255, 255, 255, 255);
-                    draw_text_line(i, menu[i]);
-                    gr_color(64, 96, 255, 255);
+                    draw_text_line(i - menu_show_start , menu[i]);
+                    gr_color(MENU_TEXT_COLOR);
                 } else {
-                    draw_text_line(i, menu[i]);
+                    gr_color(MENU_TEXT_COLOR);
+                    draw_text_line(i - menu_show_start, menu[i]);
                 }
+                row++;
             }
-            gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
-                    gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
-            ++i;
+            gr_fill(0, row*CHAR_HEIGHT+CHAR_HEIGHT/2-1,
+                    gr_fb_width(), row*CHAR_HEIGHT+CHAR_HEIGHT/2+1);
         }
 
-        gr_color(255, 255, 0, 255);
-
-        for (; i < text_rows; ++i) {
-            draw_text_line(i, text[(i+text_top) % text_rows]);
+        gr_color(NORMAL_TEXT_COLOR);
+        for (; row < text_rows; ++row) {
+            draw_text_line(row, text[(row+text_top) % text_rows]);
         }
     }
 }
@@ -228,6 +270,7 @@
 // Should only be called with gUpdateMutex locked.
 static void update_screen_locked(void)
 {
+    if (!ui_has_initialized) return;
     draw_screen_locked();
     gr_flip();
 }
@@ -236,6 +279,7 @@
 // Should only be called with gUpdateMutex locked.
 static void update_progress_locked(void)
 {
+    if (!ui_has_initialized) return;
     if (show_text || !gPagesIdentical) {
         draw_screen_locked();    // Must redraw the whole screen
         gPagesIdentical = 1;
@@ -378,6 +422,7 @@
 
 void ui_init(void)
 {
+    ui_has_initialized = 1;
     gr_init();
     ev_init(input_callback, NULL);
 
@@ -441,6 +486,23 @@
     pthread_create(&t, NULL, input_thread, NULL);
 }
 
+char *ui_copy_image(int icon, int *width, int *height, int *bpp) {
+    pthread_mutex_lock(&gUpdateMutex);
+    draw_background_locked(gBackgroundIcon[icon]);
+    *width = gr_fb_width();
+    *height = gr_fb_height();
+    *bpp = sizeof(gr_pixel) * 8;
+    int size = *width * *height * sizeof(gr_pixel);
+    char *ret = malloc(size);
+    if (ret == NULL) {
+        LOGE("Can't allocate %d bytes for image\n", size);
+    } else {
+        memcpy(ret, gr_fb_data(), size);
+    }
+    pthread_mutex_unlock(&gUpdateMutex);
+    return ret;
+}
+
 void ui_set_background(int icon)
 {
     pthread_mutex_lock(&gUpdateMutex);
@@ -508,7 +570,8 @@
     vsnprintf(buf, 256, fmt, ap);
     va_end(ap);
 
-    fputs(buf, stdout);
+    if (ui_log_stdout)
+        fputs(buf, stdout);
 
     // This can get called before ui_init(), so be careful.
     pthread_mutex_lock(&gUpdateMutex);
@@ -529,7 +592,39 @@
     pthread_mutex_unlock(&gUpdateMutex);
 }
 
-void ui_start_menu(char** headers, char** items, int initial_selection) {
+void ui_printlogtail(int nb_lines) {
+    char * log_data;
+    char tmp[PATH_MAX];
+    FILE * f;
+    int line=0;
+    //don't log output to recovery.log
+    ui_log_stdout=0;
+    sprintf(tmp, "tail -n %d /tmp/recovery.log > /tmp/tail.log", nb_lines);
+    __system(tmp);
+    f = fopen("/tmp/tail.log", "rb");
+    if (f != NULL) {
+        while (line < nb_lines) {
+            log_data = fgets(tmp, PATH_MAX, f);
+            if (log_data == NULL) break;
+            ui_print("%s", tmp);
+            line++;
+        }
+        fclose(f);
+    }
+    ui_log_stdout=1;
+}
+
+void ui_reset_text_col()
+{
+    pthread_mutex_lock(&gUpdateMutex);
+    text_col = 0;
+    pthread_mutex_unlock(&gUpdateMutex);
+}
+
+#define MENU_ITEM_HEADER " - "
+#define MENU_ITEM_HEADER_LENGTH strlen(MENU_ITEM_HEADER)
+
+int ui_start_menu(char** headers, char** items, int initial_selection) {
     int i;
     pthread_mutex_lock(&gUpdateMutex);
     if (text_rows > 0 && text_cols > 0) {
@@ -539,17 +634,28 @@
             menu[i][text_cols-1] = '\0';
         }
         menu_top = i;
-        for (; i < text_rows; ++i) {
+        for (; i < MENU_MAX_ROWS; ++i) {
             if (items[i-menu_top] == NULL) break;
-            strncpy(menu[i], items[i-menu_top], text_cols-1);
+            strcpy(menu[i], MENU_ITEM_HEADER);
+            strncpy(menu[i] + MENU_ITEM_HEADER_LENGTH, items[i-menu_top], text_cols-1 - MENU_ITEM_HEADER_LENGTH);
             menu[i][text_cols-1] = '\0';
         }
+
+        if (gShowBackButton) {
+            strcpy(menu[i], " - +++++Go Back+++++");
+            ++i;
+        }
+
         menu_items = i - menu_top;
         show_menu = 1;
-        menu_sel = initial_selection;
+        menu_sel = menu_show_start = initial_selection;
         update_screen_locked();
     }
     pthread_mutex_unlock(&gUpdateMutex);
+    if (gShowBackButton) {
+        return menu_items - 1;
+    }
+    return menu_items;
 }
 
 int ui_menu_select(int sel) {
@@ -558,9 +664,21 @@
     if (show_menu > 0) {
         old_sel = menu_sel;
         menu_sel = sel;
-        if (menu_sel < 0) menu_sel = 0;
-        if (menu_sel >= menu_items) menu_sel = menu_items-1;
+
+        if (menu_sel < 0) menu_sel = menu_items + menu_sel;
+        if (menu_sel >= menu_items) menu_sel = menu_sel - menu_items;
+
+
+        if (menu_sel < menu_show_start && menu_show_start > 0) {
+            menu_show_start = menu_sel;
+        }
+
+        if (menu_sel - menu_show_start + menu_top >= text_rows) {
+            menu_show_start = menu_sel + menu_top - text_rows + 1;
+        }
+
         sel = menu_sel;
+
         if (menu_sel != old_sel) update_screen_locked();
     }
     pthread_mutex_unlock(&gUpdateMutex);
@@ -662,3 +780,15 @@
     key_queue_len = 0;
     pthread_mutex_unlock(&key_queue_mutex);
 }
+
+void ui_set_show_text(int value) {
+    show_text = value;
+}
+
+void ui_set_showing_back_button(int showBackButton) {
+    gShowBackButton = showBackButton;
+}
+
+int ui_get_showing_back_button() {
+    return gShowBackButton;
+}
diff --git a/updater/Android.mk b/updater/Android.mk
index 8d731db..86c4bb7 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -4,6 +4,7 @@
 
 updater_src_files := \
 	install.c \
+	../mounts.c \
 	updater.c
 
 #
@@ -24,6 +25,8 @@
 LOCAL_STATIC_LIBRARIES += libext4_utils libz
 endif
 
+LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
+
 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
 LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz
 LOCAL_STATIC_LIBRARIES += libmincrypt libbz
@@ -70,4 +73,4 @@
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
-include $(BUILD_EXECUTABLE)
+include $(BUILD_EXECUTABLE)
\ No newline at end of file
diff --git a/updater/install.c b/updater/install.c
index 0396bae..77a9819 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -34,11 +34,13 @@
 #include "mincrypt/sha.h"
 #include "minzip/DirUtil.h"
 #include "minelf/Retouch.h"
-#include "mtdutils/mounts.h"
+#include "mounts.h"
 #include "mtdutils/mtdutils.h"
 #include "updater.h"
 #include "applypatch/applypatch.h"
 
+#include "flashutils/flashutils.h"
+
 #ifdef USE_EXT4
 #include "make_ext4fs.h"
 #endif
@@ -249,6 +251,24 @@
         }
         result = location;
 #endif
+    } else if (strcmp(fs_type, "ext2") == 0) {
+        int status = format_ext2_device(location);
+        if (status != 0) {
+            fprintf(stderr, "%s: format_ext2_device failed (%d) on %s",
+                    name, status, location);
+            result = strdup("");
+            goto done;
+        }
+        result = location;
+    } else if (strcmp(fs_type, "ext3") == 0) {
+        int status = format_ext3_device(location);
+        if (status != 0) {
+            fprintf(stderr, "%s: format_ext3_device failed (%d) on %s",
+                    name, status, location);
+            result = strdup("");
+            goto done;
+        }
+        result = location;
     } else {
         fprintf(stderr, "%s: unsupported fs_type \"%s\" partition_type \"%s\"",
                 name, fs_type, partition_type);
@@ -806,66 +826,13 @@
         goto done;
     }
 
-    mtd_scan_partitions();
-    const MtdPartition* mtd = mtd_find_partition_by_name(partition);
-    if (mtd == NULL) {
-        fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
+    if (0 == restore_raw_partition(NULL, partition, filename))
+        result = strdup(partition);
+    else
         result = strdup("");
         goto done;
     }
 
-    MtdWriteContext* ctx = mtd_write_partition(mtd);
-    if (ctx == NULL) {
-        fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
-                name, partition);
-        result = strdup("");
-        goto done;
-    }
-
-    bool success;
-
-    if (contents->type == VAL_STRING) {
-        // we're given a filename as the contents
-        char* filename = contents->data;
-        FILE* f = fopen(filename, "rb");
-        if (f == NULL) {
-            fprintf(stderr, "%s: can't open %s: %s\n",
-                    name, filename, strerror(errno));
-            result = strdup("");
-            goto done;
-        }
-
-        success = true;
-        char* buffer = malloc(BUFSIZ);
-        int read;
-        while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
-            int wrote = mtd_write_data(ctx, buffer, read);
-            success = success && (wrote == read);
-        }
-        free(buffer);
-        fclose(f);
-    } else {
-        // we're given a blob as the contents
-        ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size);
-        success = (wrote == contents->size);
-    }
-    if (!success) {
-        fprintf(stderr, "mtd_write_data to %s failed: %s\n",
-                partition, strerror(errno));
-    }
-
-    if (mtd_erase_blocks(ctx, -1) == -1) {
-        fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition);
-    }
-    if (mtd_write_close(ctx) != 0) {
-        fprintf(stderr, "%s: error closing write of %s\n", name, partition);
-    }
-
-    printf("%s %s partition\n",
-           success ? "wrote" : "failed to write", partition);
-
-    result = success ? partition : strdup("");
-
 done:
     if (result != partition) FreeValue(partition_value);
     FreeValue(contents);
diff --git a/updater/updater.c b/updater/updater.c
index aa626d2..6bc4f40 100644
--- a/updater/updater.c
+++ b/updater/updater.c
@@ -63,6 +63,7 @@
     // Extract the script from the package.
 
     char* package_data = argv[3];
+    setenv("UPDATE_PACKAGE", package_data, 1);
     ZipArchive za;
     int err;
     err = mzOpenZipArchive(package_data, &za);
diff --git a/utilities/Android.mk b/utilities/Android.mk
new file mode 100755
index 0000000..7d95449
--- /dev/null
+++ b/utilities/Android.mk
@@ -0,0 +1,72 @@
+LOCAL_PATH := $(call my-dir)
+
+ifndef BOARD_HAS_SMALL_RECOVERY
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fix_permissions
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := parted
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := sdparted
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+endif
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := e2fsck
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := tune2fs
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+ifeq ($(BOARD_HAS_LARGE_FILESYSTEM),true)
+include $(CLEAR_VARS)
+LOCAL_MODULE := mke2fs
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+ifeq ($(BOARD_MKE2FS),)
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+else
+LOCAL_SRC_FILES := ../../../$(BOARD_MKE2FS)
+endif
+include $(BUILD_PREBUILT)
+endif
+
+BOARD_RECOVERY_RFS_CHECK := $(shell grep rfs $(TARGET_DEVICE_DIR)/recovery.fstab)
+ifneq ($(BOARD_RECOVERY_RFS_CHECK),)
+include $(CLEAR_VARS)
+LOCAL_MODULE := fat.format
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+endif
\ No newline at end of file
diff --git a/utilities/e2fsck b/utilities/e2fsck
new file mode 100755
index 0000000..2844a1d
--- /dev/null
+++ b/utilities/e2fsck
Binary files differ
diff --git a/utilities/fat.format b/utilities/fat.format
new file mode 100755
index 0000000..6a743e7
--- /dev/null
+++ b/utilities/fat.format
Binary files differ
diff --git a/utilities/fix_permissions b/utilities/fix_permissions
new file mode 100755
index 0000000..a6db514
--- /dev/null
+++ b/utilities/fix_permissions
@@ -0,0 +1,484 @@
+#! /system/bin/sh
+#
+# Warning: if you want to run this script in cm-recovery change the above to #!/sbin/sh
+#
+# fix_permissions - fixes permissions on Android data directories after upgrade
+# shade@chemlab.org
+#
+# original concept: http://blog.elsdoerfer.name/2009/05/25/android-fix-package-uid-mismatches/
+# implementation by: Cyanogen
+# improved by: ankn, smeat, thenefield, farmatito, rikupw, Kastro
+#
+# v1.1-v1.31r3 - many improvements and concepts from XDA developers.
+# v1.34 through v2.00 -  A lot of frustration [by Kastro]
+# v2.01	- Completely rewrote the script for SPEED, thanks for the input farmatito
+#         /data/data depth recursion is tweaked;
+#         fixed single mode;
+#         functions created for modularity;
+#         logging can be disabled via CLI for more speed;
+#         runtime computation added to end (Runtime: mins secs);
+#         progress (current # of total) added to screen;
+#         fixed CLI argument parsing, now you can have more than one option!;
+#         debug cli option;
+#         verbosity can be disabled via CLI option for less noise;;
+#         [by Kastro, (XDA: k4str0), twitter;mattcarver]
+# v2.02 - ignore com.htc.resources.apk if it exists and minor code cleanups,
+#         fix help text, implement simulated run (-s) [farmatito]
+# v2.03 - fixed chown group ownership output [Kastro]
+# v2.04 - replaced /system/sd with $SD_EXT_DIRECTORY [Firerat]
+VERSION="2.04"
+
+# Defaults
+DEBUG=0 # Debug off by default
+LOGGING=1 # Logging on by default
+VERBOSE=1 # Verbose on by default
+
+# Messages
+UID_MSG="Changing user ownership for:"
+GID_MSG="Changing group ownership for:"
+PERM_MSG="Changing permissions for:"
+
+# Programs needed
+ECHO="busybox echo"
+GREP="busybox grep"
+EGREP="busybox egrep"
+CAT="busybox cat"
+CHOWN="busybox chown"
+CHMOD="busybox chmod"
+MOUNT="busybox mount"
+UMOUNT="busybox umount"
+CUT="busybox cut"
+FIND="busybox find"
+LS="busybox ls"
+TR="busybox tr"
+TEE="busybox tee"
+TEST="busybox test"
+SED="busybox sed"
+RM="busybox rm"
+WC="busybox wc"
+EXPR="busybox expr"
+DATE="busybox date"
+
+# Initialise vars
+CODEPATH=""
+UID=""
+GID=""
+PACKAGE=""
+REMOVE=0
+NOSYSTEM=0
+ONLY_ONE=""
+SIMULATE=0
+SYSREMOUNT=0
+SYSMOUNT=0
+DATAMOUNT=0
+SYSSDMOUNT=0
+FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
+FP_STARTEPOCH=$( $DATE +%s )
+if $TEST "$SD_EXT_DIRECTORY" = ""; then
+    #check for mount point, /system/sd included in tests for backward compatibility
+    for MP in /sd-ext /system/sd;do
+        if $TEST -d $MP; then
+            SD_EXT_DIRECTORY=$MP
+            break
+        fi
+    done
+fi
+fp_usage()
+{
+   $ECHO "Usage $0 [OPTIONS] [APK_PATH]"
+   $ECHO "      -d         turn on debug"
+   $ECHO "      -f         fix only package APK_PATH"
+   $ECHO "      -l         disable logging for this run (faster)"
+   $ECHO "      -r         remove stale data directories"
+   $ECHO "                 of uninstalled packages while fixing permissions"
+   $ECHO "      -s         simulate only"
+   $ECHO "      -u         check only non-system directories"
+   $ECHO "      -v         disable verbosity for this run (less output)"
+   $ECHO "      -V         print version"
+   $ECHO "      -h         this help"
+}
+
+fp_parseargs()
+{
+   # Parse options
+   while $TEST $# -ne 0; do
+      case "$1" in
+         -d)
+            DEBUG=1
+         ;;
+         -f)
+            if $TEST $# -lt 2; then
+               $ECHO "$0: missing argument for option $1"
+               exit 1
+            else
+               if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then
+                  ONLY_ONE=$2
+                  shift;
+               else
+                  $ECHO "$0: missing argument for option $1"
+                  exit 1
+               fi
+            fi
+         ;;
+         -r)
+            REMOVE=1
+         ;;
+         -s)
+            SIMULATE=1
+         ;;
+         -l)
+            if $TEST $LOGGING -eq 0; then
+               LOGGING=1
+            else
+               LOGGING=0
+            fi
+         ;;
+         -v)
+            if $TEST $VERBOSE -eq 0; then
+               VERBOSE=1
+            else
+               VERBOSE=0
+            fi
+         ;;
+         -u)
+            NOSYSTEM=1
+         ;;
+         -V)
+            $ECHO "$0 $VERSION"
+            exit 0
+         ;;
+         -h)
+            fp_usage
+            exit 0
+         ;;
+         -*)
+            $ECHO "$0: unknown option $1"
+            $ECHO
+            fp_usage
+            exit 1
+         ;;
+      esac
+      shift;
+   done
+}
+
+fp_print()
+{
+   MSG=$@
+   if $TEST $LOGGING -eq 1; then
+      $ECHO $MSG | $TEE -a $LOG_FILE
+   else
+      $ECHO $MSG
+   fi
+}
+
+fp_start()
+{
+   if $TEST $SIMULATE -eq 0 ; then
+      if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then
+         DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 )
+         if $TEST $DEBUG -eq 1; then
+            fp_print "/system mounted on $DEVICE"
+         fi
+         if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then
+            $MOUNT -o remount,rw $DEVICE /system
+            SYSREMOUNT=1
+         fi
+      else
+         $MOUNT /system > /dev/null 2>&1
+         SYSMOUNT=1
+      fi
+      
+      if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then
+         $MOUNT /data > /dev/null 2>&1
+         DATAMOUNT=1
+      fi
+      
+      if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " $SD_EXT_DIRECTORY " "/proc/mounts" ) -eq 0; then
+         $MOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
+         SYSSDMOUNT=1
+      fi
+   fi
+   if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then
+      LOG_FILE="/data/fix_permissions.log"
+   else
+      LOG_FILE="/sdcard/fix_permissions.log"
+   fi
+   if $TEST ! -e "$LOG_FILE"; then
+      > $LOG_FILE
+   fi
+   
+   fp_print "$0 $VERSION started at $FP_STARTTIME"
+}
+
+fp_chown_uid()
+{
+   FP_OLDUID=$1
+   FP_UID=$2
+   FP_FILE=$3
+   
+   #if user ownership doesn't equal then change them
+   if $TEST "$FP_OLDUID" != "$FP_UID"; then
+      if $TEST $VERBOSE -ne 0; then
+         fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'"
+      fi
+      if $TEST $SIMULATE -eq 0; then
+         $CHOWN $FP_UID "$FP_FILE"
+      fi
+   fi
+}
+
+fp_chown_gid()
+{
+   FP_OLDGID=$1
+   FP_GID=$2
+   FP_FILE=$3
+   
+   #if group ownership doesn't equal then change them
+   if $TEST "$FP_OLDGID" != "$FP_GID"; then
+      if $TEST $VERBOSE -ne 0; then
+         fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'"
+      fi
+      if $TEST $SIMULATE -eq 0; then
+         $CHOWN :$FP_GID "$FP_FILE"
+      fi
+   fi
+}
+
+fp_chmod()
+{
+   FP_OLDPER=$1
+   FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 )
+   FP_PERSTR=$2
+   FP_PERNUM=$3
+   FP_FILE=$4
+   
+   #if the permissions are not equal
+   if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then
+      if $TEST $VERBOSE -ne 0; then
+         fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)"
+      fi
+      #change the permissions
+      if $TEST $SIMULATE -eq 0; then
+         $CHMOD $FP_PERNUM "$FP_FILE"
+      fi
+   fi
+}
+
+fp_all()
+{
+   FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $WC -l )
+   I=0
+   $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | while read all_line; do
+      I=$( $EXPR $I + 1 )
+      fp_package "$all_line" $I $FP_NUMS
+   done
+}
+
+fp_single()
+{
+   FP_SFOUND=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE | wc -l )
+   if $TEST $FP_SFOUND -gt 1; then
+      fp_print "Cannot perform single operation on $FP_SFOUND matched package(s)."
+      elif $TEST $FP_SFOUND = "" -o $FP_SFOUND -eq 0; then
+      fp_print "Could not find the package you specified in the packages.xml file."
+   else
+      FP_SPKG=$( $CAT /data/system/packages.xml | $EGREP "^<package.*serId" | $GREP -v framework-res.apk | $GREP -v com.htc.resources.apk | $GREP -i $ONLY_ONE )
+      fp_package "${FP_SPKG}" 1 1
+   fi
+}
+
+fp_package()
+{
+   pkgline=$1
+   curnum=$2
+   endnum=$3
+   CODEPATH=$( $ECHO $pkgline | $SED 's%.* codePath="\(.*\)".*%\1%' |  $CUT -d '"' -f1 )
+   PACKAGE=$( $ECHO $pkgline | $SED 's%.* name="\(.*\)".*%\1%' | $CUT -d '"' -f1 )
+   UID=$( $ECHO $pkgline | $SED 's%.*serId="\(.*\)".*%\1%' |  $CUT -d '"' -f1 )
+   GID=$UID
+   APPDIR=$( $ECHO $CODEPATH | $SED 's%^\(.*\)/.*%\1%' )
+   APK=$( $ECHO $CODEPATH | $SED 's%^.*/\(.*\..*\)$%\1%' )
+   
+   #debug
+   if $TEST $DEBUG -eq 1; then
+      fp_print "CODEPATH: $CODEPATH APPDIR: $APPDIR APK:$APK UID/GID:$UID:$GID"
+   fi
+   
+   #check for existence of apk
+   if $TEST -e $CODEPATH;  then
+      fp_print "Processing ($curnum of $endnum): $PACKAGE..."
+      
+      #lets get existing permissions of CODEPATH
+      OLD_UGD=$( $LS -ln "$CODEPATH" )
+      OLD_PER=$( $ECHO $OLD_UGD | $CUT -d ' ' -f1 )
+      OLD_UID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f3 )
+      OLD_GID=$( $ECHO $OLD_UGD | $CUT -d ' ' -f4 )
+      
+      #apk source dirs
+      if $TEST "$APPDIR" = "/system/app"; then
+         #skip system apps if set
+         if $TEST "$NOSYSTEM" = "1"; then
+            fp_print "***SKIPPING SYSTEM APP ($PACKAGE)!"
+            return
+         fi
+         fp_chown_uid $OLD_UID 0 "$CODEPATH"
+         fp_chown_gid $OLD_GID 0 "$CODEPATH"
+         fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
+         elif $TEST "$APPDIR" = "/data/app" || $TEST "$APPDIR" = "/sd-ext/app"; then
+         fp_chown_uid $OLD_UID 1000 "$CODEPATH"
+         fp_chown_gid $OLD_GID 1000 "$CODEPATH"
+         fp_chmod $OLD_PER "rw-r--r--" 644 "$CODEPATH"
+         elif $TEST "$APPDIR" = "/data/app-private" || $TEST "$APPDIR" = "/sd-ext/app-private"; then
+         fp_chown_uid $OLD_UID 1000 "$CODEPATH"
+         fp_chown_gid $OLD_GID $GID "$CODEPATH"
+         fp_chmod $OLD_PER "rw-r-----" 640 "$CODEPATH"
+      fi
+   else
+      fp_print "$CODEPATH does not exist ($curnum of $endnum). Reinstall..."
+      if $TEST $REMOVE -eq 1; then
+         if $TEST -d /data/data/$PACKAGE ; then
+            fp_print "Removing stale dir /data/data/$PACKAGE"
+            if $TEST $SIMULATE -eq 0 ; then
+               $RM -R /data/data/$PACKAGE
+            fi
+         fi
+      fi
+   fi
+   
+   #the data/data for the package
+   if $TEST -d "/data/data/$PACKAGE"; then
+      #find all directories in /data/data/$PACKAGE
+      $FIND /data/data/$PACKAGE -type d -exec $LS -ldn {} \; | while read dataline; do
+         #get existing permissions of that directory
+         OLD_PER=$( $ECHO $dataline | $CUT -d ' ' -f1 )
+         OLD_UID=$( $ECHO $dataline | $CUT -d ' ' -f3 )
+         OLD_GID=$( $ECHO $dataline | $CUT -d ' ' -f4 )
+         FILEDIR=$( $ECHO $dataline | $CUT -d ' ' -f9 )
+         FOURDIR=$( $ECHO $FILEDIR | $CUT -d '/' -f5 )
+         
+         #set defaults for iteration
+         ISLIB=0
+         REVPERM=755
+         REVPSTR="rwxr-xr-x"
+         REVUID=$UID
+         REVGID=$GID
+         
+         if $TEST "$FOURDIR" = ""; then
+            #package directory, perms:755 owner:$UID:$GID
+            fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
+            elif $TEST "$FOURDIR" = "lib"; then
+            #lib directory, perms:755 owner:1000:1000
+            #lib files, perms:755 owner:1000:1000
+            ISLIB=1
+            REVPERM=755
+            REVPSTR="rwxr-xr-x"
+            REVUID=1000
+            REVGID=1000
+            fp_chmod $OLD_PER "rwxr-xr-x" 755 "$FILEDIR"
+            elif $TEST "$FOURDIR" = "shared_prefs"; then
+            #shared_prefs directories, perms:771 owner:$UID:$GID
+            #shared_prefs files, perms:660 owner:$UID:$GID
+            REVPERM=660
+            REVPSTR="rw-rw----"
+            fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
+            elif $TEST "$FOURDIR" = "databases"; then
+            #databases directories, perms:771 owner:$UID:$GID
+            #databases files, perms:660 owner:$UID:$GID
+            REVPERM=660
+            REVPSTR="rw-rw----"
+            fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
+            elif $TEST "$FOURDIR" = "cache"; then
+            #cache directories, perms:771 owner:$UID:$GID
+            #cache files, perms:600 owner:$UID:GID
+            REVPERM=600
+            REVPSTR="rw-------"
+            fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
+         else
+            #other directories, perms:771 owner:$UID:$GID
+            REVPERM=771
+            REVPSTR="rwxrwx--x"
+            fp_chmod $OLD_PER "rwxrwx--x" 771 "$FILEDIR"
+         fi
+         
+         #change ownership of directories matched
+         if $TEST "$ISLIB" = "1"; then
+            fp_chown_uid $OLD_UID 1000 "$FILEDIR"
+            fp_chown_gid $OLD_GID 1000 "$FILEDIR"
+         else
+            fp_chown_uid $OLD_UID $UID "$FILEDIR"
+            fp_chown_gid $OLD_GID $GID "$FILEDIR"
+         fi
+         
+         #if any files exist in directory with improper permissions reset them
+         $FIND $FILEDIR -type f -maxdepth 1 ! -perm $REVPERM -exec $LS -ln {} \; | while read subline; do
+            OLD_PER=$( $ECHO $subline | $CUT -d ' ' -f1 )
+            SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
+            fp_chmod $OLD_PER $REVPSTR $REVPERM "$SUBFILE"
+         done
+         
+         #if any files exist in directory with improper user reset them
+         $FIND $FILEDIR -type f -maxdepth 1 ! -user $REVUID -exec $LS -ln {} \; | while read subline; do
+            OLD_UID=$( $ECHO $subline | $CUT -d ' ' -f3 )
+            SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
+            fp_chown_uid $OLD_UID $REVUID "$SUBFILE"
+         done
+         
+         #if any files exist in directory with improper group reset them
+         $FIND $FILEDIR -type f -maxdepth 1 ! -group $REVGID -exec $LS -ln {} \; | while read subline; do
+            OLD_GID=$( $ECHO $subline | $CUT -d ' ' -f4 )
+            SUBFILE=$( $ECHO $subline | $CUT -d ' ' -f9 )
+            fp_chown_gid $OLD_GID $REVGID "$SUBFILE"
+         done
+      done
+   fi
+}
+
+date_diff()
+{
+   if $TEST $# -ne 2; then
+      FP_DDM="E"
+      FP_DDS="E"
+      return
+   fi
+   FP_DDD=$( $EXPR $2 - $1 )
+   FP_DDM=$( $EXPR $FP_DDD / 60 )
+   FP_DDS=$( $EXPR $FP_DDD % 60 )
+}
+
+fp_end()
+{
+   if $TEST $SYSREMOUNT -eq 1; then
+      $MOUNT -o remount,ro $DEVICE /system > /dev/null 2>&1
+   fi
+   
+   if $TEST $SYSSDMOUNT -eq 1; then
+      $UMOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1
+   fi
+   
+   if $TEST $SYSMOUNT -eq 1; then
+      $UMOUNT /system > /dev/null 2>&1
+   fi
+   
+   if $TEST $DATAMOUNT -eq 1; then
+      $UMOUNT /data > /dev/null 2>&1
+   fi
+   
+   FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" )
+   FP_ENDEPOCH=$( $DATE +%s )
+   
+   date_diff $FP_STARTEPOCH $FP_ENDEPOCH
+   
+   fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)"
+}
+
+#MAIN SCRIPT
+
+fp_parseargs $@
+fp_start
+if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then
+   fp_single "$ONLY_ONE"
+else
+   fp_all
+fi
+fp_end
diff --git a/utilities/mke2fs b/utilities/mke2fs
new file mode 100755
index 0000000..3270154
--- /dev/null
+++ b/utilities/mke2fs
Binary files differ
diff --git a/utilities/parted b/utilities/parted
new file mode 100755
index 0000000..bb3d432
--- /dev/null
+++ b/utilities/parted
Binary files differ
diff --git a/utilities/sdparted b/utilities/sdparted
new file mode 100755
index 0000000..74e24a6
--- /dev/null
+++ b/utilities/sdparted
@@ -0,0 +1,655 @@
+#!/sbin/sh
+
+# do logging, if not excluded with -x
+LOGFILE="/data/sdparted.log"
+[ "$1" != "-x" ] && echo "$0" "$@" >> "$LOGFILE" && "$0" -x "$@" 2>&1 | tee -a "$LOGFILE" && exit
+shift
+
+ShowError() { echo ; echo " err: $1" ; echo ; exit 1 ; }
+
+ShowMessage() { echo ; echo " msg: $1" ; }
+
+ShowHelp() {
+
+cat <<DONEHELP
+
+$SCRIPTNAME v$SCRIPTREV created by $MYNAME
+
+if you use this script in your work, please give some credit. thanks.
+
+requirements: cm-recovery-v1.4
+
+usage: $SCRIPTNAME [options]
+
+
+options:
+
+ --fatsize|-fs SIZE[MG]     set the size of the fat32 partition to <SIZE>.
+                            default=total sdcard size - (ext + swap)
+						
+ --extsize|-es SIZE[MG]     set the size of the ext partition to <SIZE>.
+                            default=$EXTSIZE
+						
+ --swapsize|-ss SIZE[MG]    set the size of the swap partition to <SIZE>.
+                            if set to 0, no swap partition will be created.
+                            default=$SWAPSIZE
+						
+ --extfs|-efs TYPE          set the filesystem of ext partition to <TYPE>.
+                            valid types=ext2, ext3, ext4
+                            default=$EXTFS
+
+
+ --upgradefs|-ufs TYPE      upgrades existing ext partition to <TYPE>.
+                            this operation will NOT wipe your sdcard and
+                            cannot be used with any partition creation options.
+                            valid types=ext3, ext4
+
+ --downgradefs|-dfs TYPE    downgrades existing ext partition to <TYPE>.
+                            this operation will NOT wipe your sdcard and
+                            cannot be used with any partition creation options.
+                            valid types=ext2
+
+							
+ --interactive|-i           interactive mode
+
+ --help|-h                  display this help
+
+ --printonly|-po            display sdcard information
+
+ --silent|-s                do not prompt user, not even initial warning.
+
+
+examples:
+ $SCRIPTNAME                     creates swap=$SWAPSIZE ext2=$EXTSIZE fat32=remaining free space
+ $SCRIPTNAME -efs ext4           creates swap=$SWAPSIZE ext4=$EXTSIZE fat32=remaining free space
+ $SCRIPTNAME -fs 1.5G -efs ext3  creates swap=$SWAPSIZE ext3=$EXTSIZE fat32=1536
+ $SCRIPTNAME -es 256M -ss 0      creates no swap ext2=256 fat32=remaining free space
+ $SCRIPTNAME -ufs ext4           upgrades ext partition to ext4
+
+DONEHELP
+
+}
+
+UserAbort() {
+	
+	WHILEEXIT=
+
+	while [ -z "$WHILEEXIT" ]
+	do
+		echo -n "do you want to continue? (Y/n) "
+		read response
+		echo
+		[ "$response" = "Y" ] || [ "$response" = "n" ] || [ "$response" = "N" ] && WHILEEXIT="$response"
+	done
+	
+	echo "$response" > /dev/null 2>&1 >>"$LOGFILE"
+	
+	[ "$response" != "Y" ]
+	
+}
+
+UnmountAll () {
+
+	# unmount all partitions so we can work with $SDPATH
+	# i'm assuming no more than 3 partitions
+	# maybe make a little more elegant later
+	echo -n "unmounting all partitions..."
+	umount "$FATPATH" > /dev/null 2>&1 >>"$LOGFILE"
+	umount "$EXTPATH" > /dev/null 2>&1 >>"$LOGFILE"
+	umount "$SWAPPATH" > /dev/null 2>&1 >>"$LOGFILE"
+	echo "done"
+	echo
+	
+}
+
+
+CheckReqs() {
+
+	echo -n "checking script requirements..."
+	# check for valid sdcard
+	[ -e $SDPATH ] || ShowError "$SDPATH does not exist!"
+	
+	# look for necessary programs
+	[ -e $CMPARTED ] || ShowError "$CMPARTED does not exist!"
+	[ -e $CMTUNE2FS ] || ShowError "$CMTUNE2FS does not exist!"
+	[ -e $CME2FSCK ] || ShowError "$CME2FSCK does not exist!"
+	
+	# verify cm-v1.4
+	PARTEDREV=`"$CMPARTED" "$SDPATH" version | grep Parted | cut -d" " -f3`
+	[ "$PARTEDREV" == "1.8.8.1.179-aef3" ] || ShowError "you are not using parted v1.8.8.1.179-aef3!"
+	echo "done"
+	echo
+	
+}
+
+CheckTableType() {
+
+	TABLETYPE=`"$CMPARTED" "$SDPATH" print | grep Table: | cut -d" " -f3`
+	
+	[ "$TABLETYPE" == "loop" -o "$TABLETYPE" == "msdos" ] && TTISOK=1 || TTISOK=0
+	[ "$TABLETYPE" == "loop" ] && TTISLOOP=1 || TTISLOOP=0
+	[ "$TABLETYPE" == "msdos" ] && TTISMSDOS=1 || TTISMOSDOS=0
+	
+}
+
+ValidateExtArg() {
+
+	FUNC_RET="nonzero"
+		
+	# validating argument
+	[ "$1" != "ext2" ] && [ "$1" != "ext3" ] && [ "$1" != "ext4" ] && FUNC_RET=
+		
+	[ -z "$FUNC_RET" ] && [ -z "$IMODE" ] && ShowError "$1 is not a valid filesystem."
+	[ -z "$FUNC_RET" ] && [ -n "$IMODE" ] && ShowMessage "$1 is not a valid filesystem."
+	
+	# return valid argument
+	[ -n "$FUNC_RET" ] && FUNC_RET="$1"
+	
+}
+
+ValidateSizeArg() {
+
+	# check for zero-length arg to protect expr length
+	[ -z "$1" ] && ShowError "zero-length argument passed to size-validator"
+		
+	SIZEMB=
+	ARGLEN=`expr length $1`
+	SIZELEN=$(($ARGLEN-1))
+	SIZEARG=`expr substr $1 1 $SIZELEN`
+	SIZEUNIT=`expr substr $1 $ARGLEN 1`
+	
+	# check if SIZEARG is an integer
+	if [ $SIZEARG -eq $SIZEARG 2> /dev/null ] ; then
+		# look for G
+		[ "$SIZEUNIT" == "G" ] && SIZEMB=$(($SIZEARG * 1024))
+		# look for M
+		[ "$SIZEUNIT" == "M" ] && SIZEMB=$SIZEARG
+		# no units on arg AND prevents using bogus size units
+		[ -z "$SIZEMB" ] && [ $SIZEUNIT -eq $SIZEUNIT 2> /dev/null ] && SIZEMB=$1
+	# check if SIZEARG is a floating point number, GB only
+	elif [ `expr index "$SIZEARG" .` != 0 ] && [ "$SIZEUNIT" == "G" ]  ; then
+		INT=`echo "$SIZEARG" | cut -d"." -f1`
+		FRAC=`echo "$SIZEARG" | cut -d"." -f2`
+		SIGDIGITS=`expr length $FRAC`
+		
+		[ -z "$INT" ] && INT=0
+		INTMB=$(($INT * 1024))
+		FRACMB=$((($FRAC * 1024) / (10**$SIGDIGITS)))
+		SIZEMB=$(($INTMB + $FRACMB))
+	# it's not a valid size
+	else
+		[ -z "$IMODE" ] && ShowError "$1 is not a valid size"
+	fi
+	
+	[ -z "$SIZEMB" ] && [ -n "$IMODE" ] && ShowMessage "$1 is not a valid size"
+	
+	# return valid argument in MB
+	FUNC_RET=$SIZEMB
+	
+}
+
+CalculatePartitions() {
+
+	# get size of sdcard in MB & do some math
+	SDSIZEMB=`"$CMPARTED" "$SDPATH" unit MB print | grep $SDPATH | cut -d" " -f3`
+	SDSIZE=${SDSIZEMB%MB}
+	[ -n "$FATSIZE" ] || FATSIZE=$(($SDSIZE - $EXTSIZE - $SWAPSIZE))
+	EXTEND=$(($FATSIZE + $EXTSIZE))
+	SWAPEND=$(($EXTEND + $SWAPSIZE))
+	
+	# check for fatsize of 0
+	[ $FATSIZE -le 0 ] && ShowError "must have a fat32 partition greater than 0MB"
+	
+	# check for zero-length sdsize...
+	# indicative of parted not reporting length 
+	# correctly b/c of error on sdcard
+	[ -z "$SDSIZE" ] && ShowError "zero-length argument passed to partition-calculator"
+	
+	# make sure we're not being asked to do the impossible
+	[ $(($FATSIZE + $EXTSIZE + $SWAPSIZE)) -gt $SDSIZE ] && [ -z "$IMODE" ] && ShowError "sum of requested partitions is greater than sdcard size"
+
+}
+
+
+UpgradeDowngradeOnly() {
+
+	if [ -n "$UEXTFSONLY" ] ; then
+		echo
+		[ -n "$CREATEPART" ] && ShowError "cannot use upgrade option when creating partitions, use -efs instead"
+		[ -n "$DEXTFSONLY" ] && ShowError "cannot upgrade AND downgrade, it just doesn't make sense"
+		echo "you have chosen to upgrade $EXTPATH to $UEXTFSONLY."
+		echo "this action will NOT delete any data from sdcard."
+		echo
+		[ -z "$SILENTRUN" ] &&	UserAbort && ShowError "script canceled by user"
+		echo
+		UpgradeExt "$UEXTFSONLY"
+		ShowCardInfo
+	elif [ -n "$DEXTFSONLY" ] ; then
+		echo
+		[ -n "$CREATEPART" ] && ShowError "cannot use downgrade option when creating partitions."
+		[ -n "$UEXTFSONLY" ] && ShowError "cannot downgrade AND upgrade, it just doesn't make sense."
+		echo "you have chosen to downgrade $EXTPATH to $DEXTFSONLY."
+		echo "this action will NOT delete any data from sdcard."
+		echo
+		[ -z "$SILENTRUN" ] &&	UserAbort && ShowError "script canceled by user"
+		echo
+		DowngradeExt "$DEXTFSONLY"
+		ShowCardInfo
+	fi
+	
+}
+
+PrepareSdCard() {
+
+	echo
+	if [ $TTISOK -eq 0 ] ; then
+		echo "partition 1 may not be aligned to cylinder boundaries."
+		echo "to continue, this must be corrected."
+	elif [ $TTISLOOP -gt 0 ] ; then
+		echo "your sdcard's partition table type is $TABLETYPE."
+		echo "to continue, partition table must be set to 'msdos'."
+	elif [ $TTISMSDOS -gt 0 ] ; then
+		# just a reminder..in a later version, 
+		# i may implement resizing of partitions, 
+		# so this will be unnecessary. but until then...
+		echo "to continue, all existing partitions must be removed."
+	else
+		# this is not good, and should never happen
+		# if it does, there is a serious problem
+		ShowError "sdcard failed table type check."
+	fi
+	
+	echo
+	echo "this action will remove all data from your sdcard."
+	echo
+	[ -z "$SILENTRUN" ] &&	UserAbort && ShowError "script canceled by user"
+	
+	[ $TTISOK -eq 0 ] && echo -n "correcting cylinder boundaries..."
+	[ $TTISLOOP -gt 0 ] && echo -n "setting partition table to msdos..."
+	[ $TTISMSDOS -gt 0 ] && echo -n "removing all partitions..."
+	
+	"$CMPARTED" -s "$SDPATH" mklabel msdos 2>&1 >>"$LOGFILE"
+	echo "done"
+	echo
+	
+}
+
+ShowActions() {
+
+	echo
+	echo "total size of sdcard=$SDSIZEMB"
+	echo
+	echo "the following actions will be performed:"
+	echo " -create $FATSIZE""MB fat32 partition"
+	[ $EXTSIZE -gt 0 ] && echo " -create $EXTSIZE""MB ext2 partition"
+	[ $SWAPSIZE -gt 0 ] && echo " -create $SWAPSIZE""MB swap partition"
+	[ "$EXTFS" != "ext2" ] && echo " -ext2 partition will be upgraded to $EXTFS"
+	echo
+	[ -z "$SILENTRUN" ] &&	UserAbort && ShowError "script canceled by user"
+	echo
+	
+}
+
+ShowCardInfo() {
+
+	CheckTableType
+	
+	echo
+	echo "retrieving current sdcard information..."
+	
+	if [ $TTISOK -gt 0 ] ; then
+		echo
+		parted "$SDPATH" print
+		echo
+		echo "script log is located @ /data/sdparted.log"
+		exit 0
+	else
+		echo
+		echo "partition 1 may not be aligned to cylinder boundaries."
+		ShowError "cannot complete print operation."
+	fi
+	echo
+	
+}
+
+
+PartitionSdCard() {
+
+	echo "performing selected actions..."
+	echo
+	
+	if [ $FATSIZE -gt 0 ] ; then
+		echo -n "creating fat32 partition..."
+		"$CMPARTED" -s "$SDPATH" mkpartfs primary fat32 0 "$FATSIZE"MB 2>&1 >>"$LOGFILE"
+		echo "done"
+	fi
+	
+	if [ $EXTSIZE -gt 0 ] ; then
+		echo -n "creating ext2 partition..."
+		"$CMPARTED" -s "$SDPATH" mkpartfs primary ext2 "$FATSIZE"MB "$EXTEND"MB 2>&1 >>"$LOGFILE"
+		"$CMTUNE2FS" -L sd-ext "$EXTPATH" 2>&1 >>"$LOGFILE"
+		echo "done"
+	fi
+	
+	if [ $SWAPSIZE -gt 0 ] ; then
+		echo -n "creating swap partition..."
+		"$CMPARTED" -s "$SDPATH" mkpartfs primary linux-swap "$EXTEND"MB "$SWAPEND"MB 2>&1 >>"$LOGFILE"
+		echo "done"
+	fi
+	echo
+	
+}
+
+UpgradeExt() {
+
+	# check for no upgrade
+	[ "$1" == "ext2" ] && return
+	# check for ext partition
+	[ ! -e "$EXTPATH" ] && ShowError "$EXTPATH does not exist"
+
+	# have to use -m switch for this check b/c parted incorrectly
+	# reports all ext partitions as ext2 when running print <number>
+	CHECKEXTFS=`"$CMPARTED" -m "$SDPATH" print | grep ext | cut -d":" -f5`
+	[ "$CHECKEXTFS" == "$1" ] && ShowError "$EXTPATH is already $1"
+
+	# grabbed the code bits for ext3 from upgrade_fs(credit:cyanogen)
+	# check for ext2...must upgrade to ext3 first b4 ext4
+	if [ "$1" == "ext3" -o "$1" == "ext4" ] ; then
+		echo -n "adding journaling to $EXTPATH..."
+		umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
+		"$CME2FSCK" -p "$EXTPATH" 2>&1 >>"$LOGFILE"
+		"$CMTUNE2FS" -c0 -i0 -j "$EXTPATH" 2>&1 >>"$LOGFILE"
+		echo "done"
+	fi
+
+	# and got convert to ext4 from xda-forum(credit:Denkai)
+	if [ "$1" == "ext4" ] ; then
+		echo -n "converting $EXTPATH to ext4 filesystem..."
+		umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
+		"$CMTUNE2FS" -O extents,uninit_bg,dir_index "$EXTPATH" 2>&1 >>"$LOGFILE"
+		"$CME2FSCK" -fpDC0 "$EXTPATH" 2>&1 >>"$LOGFILE"
+		echo "done"
+	fi
+	echo
+	
+}
+
+DowngradeExt() {
+
+	# check for ext partition
+	[ ! -e "$EXTPATH" ] && ShowError "$EXTPATH does not exist"
+
+	# have to use print for this check b/c parted incorrectly
+	# reports all ext partitions as ext2 when running print <number>
+	CHECKEXTFS=`"$CMPARTED" -m "$SDPATH" print | grep ext | cut -d":" -f5`
+	[ "$CHECKEXTFS" == "$1" ] && ShowError "$EXTPATH is already $1"
+	
+	if [ "$CHECKEXTFS" == "ext4" -o "$1" == "ext3" ] ; then
+		# interweb says downgrading from ext4 is not possible
+		# without a backup/restore procedure.
+		# if i figure it out, i'll implement it.
+		ShowError "downgrading from ext4 is not currently supported"
+	fi
+	
+	if [ "$1" == "ext2" ] ; then
+		echo -n "removing journaling from $EXTPATH..."
+		umount /system/sd > /dev/null 2>&1 >>"$LOGFILE"
+		"$CMTUNE2FS" -O ^has_journal "$EXTPATH" 2>&1 >>"$LOGFILE"
+		"$CME2FSCK" -fp "$EXTPATH" 2>&1 >>"$LOGFILE"
+		echo "done"
+	fi
+	echo
+	
+}
+
+
+Interactive() {
+
+cat <<DONEINSTRUCT
+
+sdparted interactive mode
+
+rules:
+1. no answer means you accept default value
+2. enter '0' for no partition
+
+DONEINSTRUCT
+
+	UserAbort && ShowError "script canceled by user"
+	
+	CalculatePartitions
+	
+	GetSwapSize
+	
+	FATSIZE=
+	
+	CalculatePartitions
+	
+	GetExtSize
+	
+	GetExtType
+	
+	FATSIZE=
+	
+	CalculatePartitions
+	
+	GetFatSize
+	
+}
+
+GetSwapSize() {
+
+	SWAPTEST=
+	
+	while [ -z "$SWAPTEST" ]
+	do
+		echo
+		echo -n "swap partition size [default=$SWAPSIZE]: "
+		read SWAPRESP
+		
+		[ -z "$SWAPRESP" ] && SWAPRESP="$SWAPSIZE"
+		echo "$SWAPRESP" > /dev/null 2>&1 >>"$LOGFILE"
+		
+		ValidateSizeArg "$SWAPRESP"
+		SWAPTEST="$FUNC_RET"
+		[ -n "$SWAPTEST" ] && [ $SWAPTEST -gt $SDSIZE ] && ShowMessage "$SWAPRESP > available space($(($SDSIZE))M)." && SWAPTEST=
+	done
+	
+	SWAPSIZE=$SWAPTEST
+	
+}
+
+GetExtSize() {
+
+	EXTTEST=
+	
+	while [ -z "$EXTTEST" ]
+	do
+		echo
+		echo -n "ext partition size [default=$EXTSIZE]: "
+		read EXTRESP
+		
+		[ -z "$EXTRESP" ] && EXTRESP="$EXTSIZE"
+		echo "$EXTRESP" > /dev/null 2>&1 >>"$LOGFILE"
+		
+		ValidateSizeArg "$EXTRESP"
+		EXTTEST="$FUNC_RET"
+		
+		[ -n "$EXTTEST" ] && [ $EXTTEST -gt $(($SDSIZE - $SWAPSIZE)) ] && ShowMessage "$EXTRESP > available space($(($SDSIZE - $SWAPSIZE))M)." && EXTTEST=
+	done
+	
+	EXTSIZE=$EXTTEST
+	
+}
+
+GetExtType() {
+
+	FSTEST=
+	
+	while [ -z "$FSTEST" ]
+	do
+		echo
+		echo -n "ext partition type [default=$EXTFS]: "
+		read FSRESP
+		
+		[ -z "$FSRESP" ] && FSRESP="$EXTFS"
+		echo "$FSRESP" > /dev/null 2>&1 >>"$LOGFILE"
+		
+		ValidateExtArg "$FSRESP"
+		FSTEST="$FUNC_RET"
+	done
+	
+	EXTFS="$FSTEST"
+	
+}
+
+GetFatSize() {
+
+	FATTEST=
+	
+	while [ -z "$FATTEST" ]
+	do
+		echo
+		echo -n "fat partition size [default=$FATSIZE]: "
+		read FATRESP
+		
+		[ -z "$FATRESP" ] && FATRESP="$FATSIZE"
+		echo "$FATRESP" > /dev/null 2>&1 >>"$LOGFILE"
+		
+		ValidateSizeArg "$FATRESP"
+		FATTEST="$FUNC_RET"
+		
+		[ -n "$FATTEST" ] && [ $FATTEST -gt $FATSIZE ] && ShowMessage "$FATRESP > available space($(($SDSIZE - $SWAPSIZE - $EXTSIZE))M)." && FATTEST=
+		[ -n "$FATTEST" ] && [ $FATTEST -le 0 ] && ShowMessage "must have a fat32 partition greater than 0MB" && FATTEST=
+	done
+	
+	FATSIZE=$FATTEST
+	
+}
+
+
+SCRIPTNAME="sdparted"
+SCRIPTREV="0.6"
+MYNAME="51dusty"
+
+IMODE=
+SILENTRUN=
+CREATEPART=
+FUNC_RET=
+
+UEXTFSONLY=
+DEXTFSONLY=
+
+TTISOK=
+TTISLOOP=
+TTISMSDOS=
+
+SDSIZE=
+SDSIZEMB=
+SDINFO=$(cat /etc/fstab | grep /sdcard | awk '{print $1}')
+if [ -L "$SDINFO" ]
+then
+	SDPATH=$(ls -l $SDINFO | awk '{print $11}')
+else
+	SDPATH=$SDINFO
+fi
+# we may now have an SDPATH, let's make sure its on mmcblkX or mmcblkXp1
+CHECK_SDPATH1=$(echo $SDPATH | grep mmcblk.$)
+CHECK_SDPATH2=$(echo $SDPATH | grep mmcblk.p1$)
+if [ -z "$CHECK_SDPATH1" ]
+then
+	if [ -z "$CHECK_SDPATH2" ]
+	then
+		echo fail1
+		unset SDPATH
+	else
+		LEN=${#SDPATH}
+		BLKLEN=$(expr $LEN - 2)
+		SDPATH=${SDPATH:0:$BLKLEN}
+	fi
+fi
+
+
+FATSIZE=
+FATTYPE="fat32"
+FATPATH=$SDPATH"p1"
+
+EXTSIZE=512
+EXTFS="ext2"
+EXTPATH=$SDPATH"p2"
+EXTEND=
+
+SWAPSIZE=32
+SWAPTYPE="linux-swap"
+SWAPPATH=$SDPATH"p3"
+SWAPEND=
+
+CMPARTED="/sbin/parted"
+CMTUNE2FS="/sbin/tune2fs"
+CME2FSCK="/sbin/e2fsck"
+
+# give the output some breathing room
+echo "$SCRIPTREV" >> "$LOGFILE" 
+echo
+
+# check for arguments
+while [ $# -gt 0 ] ; do
+  case "$1" in
+
+    -h|--help) ShowHelp ; exit 0 ;;
+    
+    -fs|--fatsize) shift ; ValidateSizeArg "$1" ; FATSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
+    -es|--extsize) shift ; ValidateSizeArg "$1" ; EXTSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
+	-ss|--swapsize) shift ; ValidateSizeArg "$1" ; SWAPSIZE="$FUNC_RET" ; CREATEPART="$1" ;;
+	-efs|--extfs) shift ; ValidateExtArg "$1" ; EXTFS="$FUNC_RET" ; CREATEPART="$1" ;;
+	
+	-ufs|--upgradefs) shift ; ValidateExtArg "$1" ; UEXTFSONLY="$FUNC_RET" ;;
+	-dfs|--downgradefs) shift ; ValidateExtArg "$1" ; DEXTFSONLY="$FUNC_RET" ;;
+	
+	-i|--interactive) IMODE="$1" ;;
+	
+	-s|--silent) SILENTRUN="$1" ;;
+	
+	-po|--printonly) ShowCardInfo ;;
+	
+	*) ShowHelp ; ShowError "unknown argument '$1'" ;;
+
+  esac
+  shift
+done
+
+# can't do silent when in interactive mode
+[ -n "$IMODE" ] && SILENTRUN=
+
+# make sure sdcard exists and all needed files are here
+CheckReqs
+
+# unmount all
+UnmountAll
+
+# upgrade only? downgrade only?
+UpgradeDowngradeOnly
+
+# check table
+CheckTableType
+
+# prep card
+PrepareSdCard
+
+# check for interactive mode
+[ -n "$IMODE" ] && Interactive
+
+# do some math
+CalculatePartitions
+
+# last chance to cancel
+ShowActions
+
+# partition card
+PartitionSdCard
+
+# upgrade fs if necessary
+UpgradeExt "$EXTFS"
+
+# say goodbye and show print output
+ShowCardInfo
diff --git a/utilities/tune2fs b/utilities/tune2fs
new file mode 100755
index 0000000..4bd02f6
--- /dev/null
+++ b/utilities/tune2fs
Binary files differ