Merge from ClockworkMod recovery

Change-Id: Id5b312147173ced559a62d97029acede6c2f8766
diff --git a/Android.mk b/Android.mk
index e6c3547..b0fc158 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,6 +5,7 @@
 include $(CLEAR_VARS)
 
 commands_recovery_local_path := $(LOCAL_PATH)
+# LOCAL_CPP_EXTENSION := .c
 
 LOCAL_SRC_FILES := \
     recovery.c \
@@ -13,15 +14,30 @@
     roots.c \
     ui.c \
     verifier.c \
-    encryptedfs_provisioning.c
+    encryptedfs_provisioning.c \
+    mounts.c \
+	extendedcommands.c \
+	nandroid.c \
+    reboot.c \
+    setprop.c
 
 LOCAL_MODULE := recovery
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
-RECOVERY_API_VERSION := 3
+RECOVERY_VERSION := ClockworkMod Recovery v2.5.1.8
+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_SDCARD_DEVICE_PRIMARY BOARD_SDCARD_DEVICE_SECONDARY BOARD_SDEXT_DEVICE BOARD_SDEXT_FILESYSTEM BOARD_DATA_DEVICE BOARD_DATA_FILESYSTEM BOARD_DATADATA_DEVICE BOARD_DATADATA_FILESYSTEM BOARD_CACHE_DEVICE BOARD_CACHE_FILESYSTEM BOARD_SYSTEM_DEVICE BOARD_SYSTEM_FILESYSTEM BOARD_HAS_DATADATA BOARD_DATA_FILESYSTEM_OPTIONS BOARD_DATADATA_FILESYSTEM_OPTIONS BOARD_CACHE_FILESYSTEM_OPTIONS BOARD_SYSTEM_FILESYSTEM_OPTIONS BOARD_HAS_MTD_CACHE BOARD_USES_BMLUTILS BOARD_USES_MMCUTILS BOARD_HAS_SMALL_RECOVERY BOARD_LDPI_RECOVERY BOARD_RECOVERY_IGNORE_BOOTABLES BOARD_HAS_NO_MISC_PARTITION BOARD_HAS_SDCARD_INTERNAL BOARD_SDCARD_DEVICE_INTERNAL BOARD_HIJACK_RECOVERY_PATH
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+  $(if $($(board_define)), \
+    $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+  ) \
+  )
+
 # 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
 # a (redundant) copy of the binary in /system/bin for user builds.
@@ -30,13 +46,18 @@
 LOCAL_MODULE_TAGS := eng
 
 LOCAL_STATIC_LIBRARIES :=
-ifeq ($(TARGET_RECOVERY_UI_LIB),)
+ifeq ($(BOARD_CUSTOM_RECOVERY_KEYMAPPING),)
   LOCAL_SRC_FILES += default_recovery_ui.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 += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image
+LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils
+
 LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils
 LOCAL_STATIC_LIBRARIES += libstdc++ libc
 
@@ -44,6 +65,46 @@
 
 include $(BUILD_EXECUTABLE)
 
+RECOVERY_LINKS := edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot
+
+# 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)
+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)
 
@@ -60,13 +121,17 @@
 include $(BUILD_EXECUTABLE)
 
 
+include $(commands_recovery_local_path)/bmlutils/Android.mk
+include $(commands_recovery_local_path)/flashutils/Android.mk
 include $(commands_recovery_local_path)/minui/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 :=
 
 endif   # TARGET_ARCH == arm
diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk
new file mode 100644
index 0000000..e95cb5f
--- /dev/null
+++ b/bmlutils/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CFLAGS += -DBOARD_BOOT_DEVICE=\"$(BOARD_BOOT_DEVICE)\"
+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..1cc28b2
--- /dev/null
+++ b/bmlutils/bmlutils.c
@@ -0,0 +1,100 @@
+#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
+
+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)
+{
+    char *bml;
+    if (strcmp(partition, "boot") == 0 || strcmp(partition, "recovery") == 0)
+        bml = "/dev/block/bml7";
+    else
+        return 6;
+
+    int ret = restore_internal("/dev/block/bml7", filename);
+    if (ret != 0)
+        return ret;
+
+    ret = restore_internal("/dev/block/bml8", filename);
+    return ret;
+}
+
+int cmd_bml_backup_raw_partition(const char *partition, const char *filename)
+{
+    char tmp[PATH_MAX];
+    sprintf(tmp, "dd of=%s if=/dev/block/bml7 bs=4096", filename);
+    return __system(tmp);
+}
+
+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;
+}
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 97e87ee..df26597 100644
--- a/common.h
+++ b/common.h
@@ -34,10 +34,13 @@
 // so keep the output short and not too cryptic.
 void ui_print(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
 
+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);
@@ -45,15 +48,25 @@
 // 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_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)
diff --git a/default_recovery_ui.c b/default_recovery_ui.c
index ce12787..9c192a2 100644
--- a/default_recovery_ui.c
+++ b/default_recovery_ui.c
@@ -18,15 +18,18 @@
 
 #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 sdcard",
                        "wipe data/factory reset",
                        "wipe cache partition",
+                       "install zip from sdcard",
+                       "backup and restore",
+                       "mounts and storage",
+                       "advanced",
                        NULL };
 
 int device_recovery_start() {
@@ -34,7 +37,14 @@
 }
 
 int device_toggle_display(volatile char* key_pressed, int key_code) {
-    return key_code == KEY_HOME;
+    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 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_reboot_now(volatile char* key_pressed, int key_code) {
@@ -44,16 +54,37 @@
 int device_handle_key(int key_code, int visible) {
     if (visible) {
         switch (key_code) {
+            case KEY_CAPSLOCK:
             case KEY_DOWN:
             case KEY_VOLUMEDOWN:
                 return HIGHLIGHT_DOWN;
 
+            case KEY_LEFTSHIFT:
             case KEY_UP:
             case KEY_VOLUMEUP:
                 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_CENTER:
+            case KEY_CAMERA:
+            case KEY_F21:
+            case KEY_SEND:
                 return SELECT_ITEM;
+            
+            case KEY_END:
+            case KEY_BACKSPACE:
+            case KEY_BACK:
+                if (!get_allow_toggle_display())
+                    return GO_BACK;
         }
     }
 
diff --git a/encryptedfs_provisioning.c b/encryptedfs_provisioning.c
index 601c817..678c09f 100644
--- a/encryptedfs_provisioning.c
+++ b/encryptedfs_provisioning.c
@@ -27,7 +27,7 @@
 #include "cutils/properties.h"
 #include "common.h"
 #include "mtdutils/mtdutils.h"
-#include "mtdutils/mounts.h"
+#include "mounts.h"
 #include "roots.h"
 
 const char* encrypted_fs_enabled_property      = "persist.security.secfs.enabled";
diff --git a/etc/init.rc b/etc/init.rc
index a675a4b..e6b43e0 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -10,6 +10,7 @@
     symlink /system/etc /etc
 
     mkdir /sdcard
+    mkdir /emmc
     mkdir /system
     mkdir /data
     mkdir /cache
diff --git a/extendedcommands.c b/extendedcommands.c
new file mode 100644
index 0000000..df22448
--- /dev/null
+++ b/extendedcommands.c
@@ -0,0 +1,972 @@
+#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"
+
+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);
+#ifndef BOARD_HAS_NO_MISC_PARTITION
+    set_sdcard_update_bootloader_message();
+#endif
+    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[] = {  "apply /sdcard/update.zip",
+                                "choose zip from sdcard",
+                                "toggle signature verification",
+                                "toggle script asserts",
+                                NULL };
+#define ITEM_APPLY_SDCARD     0
+#define ITEM_CHOOSE_ZIP       1
+#define ITEM_SIG_CHECK        2
+#define ITEM_ASSERTS          3
+
+void show_install_update_menu()
+{
+    static char* headers[] = {  "Apply update from .zip file on SD card",
+                                "",
+                                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();
+                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()
+{
+    if (ensure_path_mounted("/sdcard") != 0) {
+        LOGE ("Can't mount /sdcard\n");
+        return;
+    }
+
+    static char* headers[] = {  "Choose a zip to apply",
+                                "",
+                                NULL
+    };
+
+    char* file = choose_file_menu("/sdcard/", ".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()
+{
+    if (ensure_path_mounted("/sdcard") != 0) {
+        LOGE ("Can't mount /sdcard\n");
+        return;
+    }
+
+    static char* headers[] = {  "Choose an image to restore",
+                                "",
+                                NULL
+    };
+
+    char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, headers);
+    if (file == NULL)
+        return;
+
+    if (confirm_selection("Confirm restore?", "Yes - Restore"))
+        nandroid_restore(file, 1, 1, 1, 1, 1);
+}
+
+void show_mount_usb_storage_menu()
+{
+    char command[PATH_MAX];
+    Volume *vol = volume_for_path("/sdcard");
+    sprintf(command, "echo %s > /sys/devices/platform/usb_mass_storage/lun0/file", vol->device);
+    __system(command);
+    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;
+    }
+
+    __system("echo '' > /sys/devices/platform/usb_mass_storage/lun0/file");
+    __system("echo 0 > /sys/devices/platform/usb_mass_storage/lun0/enable");
+}
+
+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;
+}
+
+int format_unknown_device(const char* path)
+{
+    // if this is SDEXT:, don't worry about it.
+    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 (0 != ensure_path_mounted(path))
+    {
+        ui_print("Error mounting %s!\n", path);
+        ui_print("Skipping format...\n");
+        return 0;
+    }
+
+    static char tmp[PATH_MAX];
+    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
+
+void show_partition_menu()
+{
+    static char* headers[] = {  "Mounts and Storage Menu",
+                                "",
+                                NULL
+    };
+
+    typedef char* string;
+    string mounts[][3] = {
+        { "mount /system", "unmount /system", "/system" },
+        { "mount /data", "unmount /data", "/data" },
+        { "mount /cache", "unmount /cache", "/cache" },
+        { "mount /sdcard", "unmount /sdcard", "/sdcard:" },
+#ifdef BOARD_HAS_SDCARD_INTERNAL
+        { "mount /emmc", "unmount /emmc", "/emmc" },
+#endif
+        { "mount /sd-ext", "unmount /sd-ext", "/sd-ext" }
+    };
+
+    string devices[][2] = {
+        { "format boot", "boot:" },
+        { "format system", "/system" },
+        { "format data", "/data" },
+        { "format cache", "/cache" },
+        { "format sdcard", "/sdcard" },
+        { "format sd-ext", "/sd-ext" },
+#ifdef BOARD_HAS_SDCARD_INTERNAL
+        { "format internal sdcard", "/emmc" }
+#endif
+    };
+
+    const int MOUNTABLE_COUNT = sizeof(mounts) / sizeof(string) / 3;
+    const int DEVICE_COUNT = sizeof(devices) / sizeof(string) / 2;
+
+    static char* confirm_format  = "Confirm format?";
+    static char* confirm = "Yes - Format";
+
+    for (;;)
+    {
+        int ismounted[MOUNTABLE_COUNT];
+        int i;
+        static string options[MOUNTABLE_COUNT + DEVICE_COUNT + 1 + 1]; // mountables, format mtds, format mmcs, usb storage, null
+        for (i = 0; i < MOUNTABLE_COUNT; i++)
+        {
+            ismounted[i] = is_path_mounted(mounts[i][2]);
+            options[i] = ismounted[i] ? mounts[i][1] : mounts[i][0];
+        }
+
+        for (i = 0; i < DEVICE_COUNT; i++)
+        {
+            options[DEVICE_COUNT + i] = devices[i][0];
+        }
+
+        options[MOUNTABLE_COUNT + DEVICE_COUNT] = "mount USB storage";
+        options[MOUNTABLE_COUNT + DEVICE_COUNT + 1] = NULL;
+
+        int chosen_item = get_menu_selection(headers, options, 0, 0);
+        if (chosen_item == GO_BACK)
+            break;
+        if (chosen_item == MOUNTABLE_COUNT + DEVICE_COUNT)
+        {
+            show_mount_usb_storage_menu();
+        }
+        else if (chosen_item < MOUNTABLE_COUNT)
+        {
+            if (ismounted[chosen_item])
+            {
+                if (0 != ensure_path_unmounted(mounts[chosen_item][2]))
+                    ui_print("Error unmounting %s!\n", mounts[chosen_item][2]);
+            }
+            else
+            {
+                if (0 != ensure_path_mounted(mounts[chosen_item][2]))
+                    ui_print("Error mounting %s!\n", mounts[chosen_item][2]);
+            }
+        }
+        else if (chosen_item < MOUNTABLE_COUNT + DEVICE_COUNT)
+        {
+            chosen_item = chosen_item - MOUNTABLE_COUNT;
+            if (!confirm_selection(confirm_format, confirm))
+                continue;
+            ui_print("Formatting %s...\n", devices[chosen_item][1]);
+            if (0 != format_device(devices[chosen_item][1]))
+                ui_print("Error formatting %s!\n", devices[chosen_item][1]);
+            else
+                ui_print("Done.\n");
+        }
+    }
+}
+
+#define EXTENDEDCOMMAND_SCRIPT "/cache/recovery/extendedcommand"
+
+int extendedcommand_file_exists()
+{
+    struct stat file_info;
+    return 0 == stat(EXTENDEDCOMMAND_SCRIPT, &file_info);
+}
+
+int run_script_from_buffer(char* script_data, int script_len, char* filename)
+{
+    ui_print("not yet implemented.\n");
+    return -1;
+
+    /*
+    const AmCommandList *commands = parseAmendScript(script_data, script_len);
+    if (commands == NULL) {
+        printf("Syntax error in update script\n");
+        return 1;
+    } else {
+        printf("Parsed %.*s\n", script_len, filename);
+    }
+
+    int ret = execCommandList((ExecContext *)1, commands);
+    if (ret != 0) {
+        int num = ret;
+        char *line = NULL, *next = script_data;
+        while (next != NULL && ret-- > 0) {
+            line = next;
+            next = memchr(line, '\n', script_data + script_len - line);
+            if (next != NULL) *next++ = '\0';
+        }
+        printf("Failure at line %d:\n%s\n", num, next ? line : "(not found)");
+        return 1;
+    }
+    */
+}
+
+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;
+}
+
+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.");
+    }
+
+    sprintf(tmp, "/tmp/%s", basename(EXTENDEDCOMMAND_SCRIPT));
+    return run_script(tmp);
+}
+
+void show_nandroid_advanced_restore_menu()
+{
+    if (ensure_path_mounted("/sdcard") != 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* file = choose_file_menu("/sdcard/clockworkmod/backup/", 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",
+                            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);
+            break;
+        case 1:
+            if (confirm_selection(confirm_restore, "Yes - Restore system"))
+                nandroid_restore(file, 0, 1, 0, 0, 0);
+            break;
+        case 2:
+            if (confirm_selection(confirm_restore, "Yes - Restore data"))
+                nandroid_restore(file, 0, 0, 1, 0, 0);
+            break;
+        case 3:
+            if (confirm_selection(confirm_restore, "Yes - Restore cache"))
+                nandroid_restore(file, 0, 0, 0, 1, 0);
+            break;
+        case 4:
+            if (confirm_selection(confirm_restore, "Yes - Restore sd-ext"))
+                nandroid_restore(file, 0, 0, 0, 0, 1);
+            break;
+    }
+}
+
+void show_nandroid_menu()
+{
+    static char* headers[] = {  "Nandroid",
+                                "",
+                                NULL
+    };
+
+    static char* list[] = { "Backup",
+                            "Restore",
+                            "Advanced Restore",
+                            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();
+            break;
+        case 2:
+            show_nandroid_advanced_restore_menu();
+            break;
+    }
+}
+
+void wipe_battery_stats()
+{
+    ensure_path_mounted("/data");
+    remove("/data/system/batterystats.bin");
+    ensure_path_unmounted("/data");
+}
+
+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",
+#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:
+                __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "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");
+                }
+                ensure_path_unmounted("/data");
+                ui_print("Dalvik Cache wiped.\n");
+                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:
+            {
+                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 6:
+            {
+                ensure_path_mounted("/system");
+                ensure_path_mounted("/data");
+                ui_print("Fixing permissions...\n");
+                __system("fix_permissions");
+                ui_print("Done!\n");
+                break;
+            }
+            case 7:
+            {
+                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 *root_path, FILE *file)
+{
+    RootInfo *info = get_root_info_for_path(root_path);
+    if (info == NULL) {
+        LOGW("Unable to get root info for %s during fstab generation!", root_path);
+        return;
+    }
+    char device[PATH_MAX];
+    int ret = get_root_partition_device(root_path, device);
+    if (ret == 0)
+    {
+        fprintf(file, "%s ", device);
+    }
+    else
+    {
+        fprintf(file, "%s ", info->device);
+    }
+
+    fprintf(file, "%s ", info->mount_point);
+    fprintf(file, "%s %s\n", info->filesystem, info->filesystem_options == NULL ? "rw" : info->filesystem_options);
+}
+
+void create_fstab()
+{
+    __system("touch /etc/mtab");
+    FILE *file = fopen("/etc/fstab", "w");
+    if (file == NULL) {
+        LOGW("Unable to create /etc/fstab!");
+        return;
+    }
+    write_fstab_root("CACHE:", file);
+    write_fstab_root("DATA:", file);
+#ifdef BOARD_HAS_DATADATA
+    write_fstab_root("DATADATA:", file);
+#endif
+    write_fstab_root("SYSTEM:", file);
+    write_fstab_root("SDCARD:", file);
+    write_fstab_root("SDEXT:", file);
+    fclose(file);
+}
+*/
+
+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 format_device(const char* device) {
+    if (device == NULL)
+        return -1;
+    if (device[0] == '/') {
+        Volume *vol = volume_for_path(device);
+        if (vol == NULL)
+            return -1;
+        return erase_partition(device, vol->fs_type);
+    }
+    return erase_raw_partition(device);
+}
+
+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;
+}
diff --git a/extendedcommands.h b/extendedcommands.h
new file mode 100644
index 0000000..650a519
--- /dev/null
+++ b/extendedcommands.h
@@ -0,0 +1,46 @@
+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();
+
+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* root);
+
+void
+wipe_battery_stats();
+
+void create_fstab();
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..324480a
--- /dev/null
+++ b/flashutils/Android.mk
@@ -0,0 +1,98 @@
+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
+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..4db1f74
--- /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(argv[1], argv[2]);
+}
diff --git a/flashutils/erase_image.c b/flashutils/erase_image.c
new file mode 100644
index 0000000..c495255
--- /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(argv[1]);
+}
diff --git a/mtdutils/flash_image.c b/flashutils/flash_image.c
similarity index 93%
rename from mtdutils/flash_image.c
rename to flashutils/flash_image.c
index c776876..3966c42 100644
--- a/mtdutils/flash_image.c
+++ b/flashutils/flash_image.c
@@ -22,8 +22,8 @@
 #include <unistd.h>
 
 #include "cutils/log.h"
-#include "mtdutils.h"
 
+#if 0
 #define LOG_TAG "flash_image"
 
 #define HEADER_SIZE 2048  // size of header to compare for equality
@@ -138,3 +138,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(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..b71d4fa
--- /dev/null
+++ b/flashutils/flashutils.c
@@ -0,0 +1,160 @@
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "flashutils/flashutils.h"
+
+int the_flash_type = UNKNOWN;
+
+int device_flash_type()
+{
+    if (the_flash_type == UNKNOWN) {
+        if (access("/dev/block/bml1", 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";
+}
+
+// This was pulled from bionic: The default system command always looks
+// for shell in /system/bin/sh. This is bad.
+#define _PATH_BSHELL "/sbin/sh"
+
+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);
+}
+
+int restore_raw_partition(const char *partition, const char *filename)
+{
+    int type = device_flash_type();
+    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 *partition, const char *filename)
+{
+    int type = device_flash_type();
+    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:
+            return -1;
+    }
+}
+
+int erase_raw_partition(const char *partition)
+{
+    int type = device_flash_type();
+    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 = device_flash_type();
+    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 = device_flash_type();
+    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..d5dadcb
--- /dev/null
+++ b/flashutils/flashutils.h
@@ -0,0 +1,51 @@
+#ifndef FLASHUTILS_H
+#define FLASHUTILS_H
+
+int restore_raw_partition(const char *partition, const char *filename);
+int backup_raw_partition(const char *partition, const char *filename);
+int erase_raw_partition(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();
+
+int __system(const char *command);
+
+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();
+
+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 5bb3a78..2d8a4cd 100644
--- a/install.c
+++ b/install.c
@@ -28,14 +28,74 @@
 #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 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);
+    }
+
+    free(filename);
+
+    return INSTALL_SUCCESS;
+}
+
 // If the package contains an update binary, extract it and run it.
 static int
 try_update_binary(const char *path, ZipArchive *zip) {
@@ -43,7 +103,7 @@
             mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);
     if (binary_entry == NULL) {
         mzCloseZipArchive(zip);
-        return INSTALL_CORRUPT;
+        return INSTALL_UPDATE_BINARY_MISSING;
     }
 
     char* binary = "/tmp/update_binary";
@@ -117,6 +177,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) {
@@ -136,6 +199,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) {
@@ -156,6 +231,11 @@
         return INSTALL_ERROR;
     }
 
+    if (firmware_type != NULL) {
+        return handle_firmware_update(firmware_type, firmware_filename, zip);
+    } else {
+        return INSTALL_SUCCESS;
+    }
     return INSTALL_SUCCESS;
 }
 
@@ -248,27 +328,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/minui/Android.mk b/minui/Android.mk
index 91dd939..10f0f3f 100644
--- a/minui/Android.mk
+++ b/minui/Android.mk
@@ -7,6 +7,10 @@
     external/libpng\
     external/zlib
 
+ifneq ($(BOARD_LDPI_RECOVERY),)
+    LOCAL_CFLAGS += -DBOARD_LDPI_RECOVERY='"$(BOARD_LDPI_RECOVERY)"'
+endif
+
 LOCAL_MODULE := libminui
 
 include $(BUILD_STATIC_LIBRARY)
diff --git a/minui/events.c b/minui/events.c
index 3aed2a8..efdca95 100644
--- a/minui/events.c
+++ b/minui/events.c
@@ -19,16 +19,166 @@
 #include <fcntl.h>
 #include <dirent.h>
 #include <sys/poll.h>
+#include <limits.h>
 
 #include <linux/input.h>
 
+#include "../common.h"
+
 #include "minui.h"
 
 #define MAX_DEVICES 16
 
+#define VIBRATOR_TIMEOUT_FILE	"/sys/class/timed_output/vibrator/enable"
+#define VIBRATOR_TIME_MS	50
+
+#define PRESS_THRESHHOLD    10
+
+#define ABS_MT_POSITION_X 0x35
+#define ABS_MT_POSITION_Y 0x36
+#define ABS_MT_TOUCH_MAJOR 0x30
+#define SYN_MT_REPORT 2
+
+struct virtualkey {
+    int scancode;
+    int centerx, centery;
+    int width, height;
+};
+
+struct position {
+    int x, y;
+    int pressed;
+    struct input_absinfo xi, yi;
+};
+
+struct ev {
+    struct pollfd *fd;
+
+    struct virtualkey *vks;
+    int vk_count;
+
+    struct position p, mt_p;
+    int sent, mt_idx;
+};
+
 static struct pollfd ev_fds[MAX_DEVICES];
+static struct ev evs[MAX_DEVICES];
 static unsigned ev_count = 0;
 
+static inline int ABS(int x) {
+    return x<0?-x:x;
+}
+
+int vibrate(int timeout_ms)
+{
+    char str[20];
+    int fd;
+    int ret;
+
+    fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY);
+    if (fd < 0)
+        return -1;
+
+    ret = snprintf(str, sizeof(str), "%d", timeout_ms);
+    ret = write(fd, str, ret);
+    close(fd);
+
+    if (ret < 0)
+       return -1;
+
+    return 0;
+}
+
+/* Returns empty tokens */
+static char *vk_strtok_r(char *str, const char *delim, char **save_str)
+{
+    if(!str) {
+        if(!*save_str) return NULL;
+        str = (*save_str) + 1;
+    }
+    *save_str = strpbrk(str, delim);
+    if(*save_str) **save_str = '\0';
+    return str;
+}
+
+static int vk_init(struct ev *e)
+{
+    char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys.";
+    char vks[2048], *ts;
+    ssize_t len;
+    int vk_fd;
+    int i;
+
+    e->vk_count = 0;
+
+    len = strlen(vk_path);
+    len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(vk_path) - len), vk_path + len);
+    if (len <= 0)
+        return -1;
+
+    vk_fd = open(vk_path, O_RDONLY);
+    if (vk_fd < 0)
+        return -1;
+
+    len = read(vk_fd, vks, sizeof(vks)-1);
+    close(vk_fd);
+    if (len <= 0)
+        return -1;
+
+    vks[len] = '\0';
+
+    /* Parse a line like:
+        keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:...
+    */
+    for (ts = vks, e->vk_count = 1; *ts; ++ts) {
+        if (*ts == ':')
+            ++e->vk_count;
+    }
+
+    if (e->vk_count % 6) {
+        LOGW("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6);
+    }
+    e->vk_count /= 6;
+    if (e->vk_count <= 0)
+        return -1;
+
+    e->sent = 0;
+    e->mt_idx = 0;
+
+    ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi);
+    ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi);
+    e->p.pressed = 0;
+
+    ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi);
+    ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi);
+    e->mt_p.pressed = 0;
+
+    e->vks = malloc(sizeof(*e->vks) * e->vk_count);
+
+    for (i = 0; i < e->vk_count; ++i) {
+        char *token[6];
+        int j;
+
+        for (j = 0; j < 6; ++j) {
+            token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts);
+        }
+
+        if (strcmp(token[0], "0x01") != 0) {
+            /* Java does string compare, so we do too. */
+            LOGW("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]);
+            continue;
+        }
+
+        e->vks[i].scancode = strtol(token[1], NULL, 0);
+        e->vks[i].centerx = strtol(token[2], NULL, 0);
+        e->vks[i].centery = strtol(token[3], NULL, 0);
+        e->vks[i].width = strtol(token[4], NULL, 0);
+        e->vks[i].height = strtol(token[5], NULL, 0);
+    }
+
+    return 0;
+}
+
 int ev_init(void)
 {
     DIR *dir;
@@ -45,6 +195,11 @@
 
             ev_fds[ev_count].fd = fd;
             ev_fds[ev_count].events = POLLIN;
+            evs[ev_count].fd = &ev_fds[ev_count];
+
+            /* Load virtualkeys if there are any */
+            vk_init(&evs[ev_count]);
+
             ev_count++;
             if(ev_count == MAX_DEVICES) break;
         }
@@ -55,11 +210,135 @@
 
 void ev_exit(void)
 {
-    while (ev_count > 0) {
-        close(ev_fds[--ev_count].fd);
+    while (ev_count-- > 0) {
+	if (evs[ev_count].vk_count) {
+		free(evs[ev_count].vks);
+		evs[ev_count].vk_count = 0;
+	}
+        close(ev_fds[ev_count].fd);
     }
 }
 
+static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size)
+{
+    int screen_pos;
+
+    if (info->minimum == info->maximum)
+        return 0;
+
+    screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum);
+    return (screen_pos >= 0 && screen_pos < screen_size);
+}
+
+static int vk_tp_to_screen(struct position *p, int *x, int *y)
+{
+    if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum)
+        return 0;
+
+    *x = (p->x - p->xi.minimum) * (gr_fb_width() - 1) / (p->xi.maximum - p->xi.minimum);
+    *y = (p->y - p->yi.minimum) * (gr_fb_height() - 1) / (p->yi.maximum - p->yi.minimum);
+
+    if (*x >= 0 && *x < gr_fb_width() &&
+           *y >= 0 && *y < gr_fb_height()) {
+        return 0;
+    }
+
+    return 1;
+}
+
+/* Translate a virtual key in to a real key event, if needed */
+/* Returns non-zero when the event should be consumed */
+static int vk_modify(struct ev *e, struct input_event *ev)
+{
+    int i;
+    int x, y;
+
+    if (ev->type == EV_KEY) {
+        if (ev->code == BTN_TOUCH)
+            e->p.pressed = ev->value;
+        return 0;
+    }
+
+    if (ev->type == EV_ABS) {
+        switch (ev->code) {
+        case ABS_X:
+            e->p.x = ev->value;
+            return !vk_inside_display(e->p.x, &e->p.xi, gr_fb_width());
+        case ABS_Y:
+            e->p.y = ev->value;
+            return !vk_inside_display(e->p.y, &e->p.yi, gr_fb_height());
+        case ABS_MT_POSITION_X:
+            if (e->mt_idx) return 1;
+            e->mt_p.x = ev->value;
+            return !vk_inside_display(e->mt_p.x, &e->mt_p.xi, gr_fb_width());
+        case ABS_MT_POSITION_Y:
+            if (e->mt_idx) return 1;
+            e->mt_p.y = ev->value;
+            return !vk_inside_display(e->mt_p.y, &e->mt_p.yi, gr_fb_height());
+        case ABS_MT_TOUCH_MAJOR:
+            if (e->mt_idx) return 1;
+            if (e->sent)
+                e->mt_p.pressed = (ev->value > 0);
+            else
+                e->mt_p.pressed = (ev->value > PRESS_THRESHHOLD);
+            return 0;
+        }
+
+        return 0;
+    }
+
+    if (ev->type != EV_SYN)
+        return 0;
+
+    if (ev->code == SYN_MT_REPORT) {
+        /* Ignore the rest of the points */
+        ++e->mt_idx;
+        return 1;
+    }
+    if (ev->code != SYN_REPORT)
+        return 0;
+
+    /* Report complete */
+
+    e->mt_idx = 0;
+
+    if (!e->p.pressed && !e->mt_p.pressed) {
+        /* No touch */
+        e->sent = 0;
+        return 0;
+    }
+
+    if (!(e->p.pressed && vk_tp_to_screen(&e->p, &x, &y)) &&
+            !(e->mt_p.pressed && vk_tp_to_screen(&e->mt_p, &x, &y))) {
+        /* No touch inside vk area */
+        return 0;
+    }
+
+    if (e->sent) {
+        /* We've already sent a fake key for this touch */
+        return 1;
+    }
+
+    /* The screen is being touched on the vk area */
+    e->sent = 1;
+
+    for (i = 0; i < e->vk_count; ++i) {
+        int xd = ABS(e->vks[i].centerx - x);
+        int yd = ABS(e->vks[i].centery - y);
+        if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) {
+            /* Fake a key event */
+            ev->type = EV_KEY;
+            ev->code = e->vks[i].scancode;
+            ev->value = 1;
+
+            vibrate(VIBRATOR_TIME_MS);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
 int ev_get(struct input_event *ev, unsigned dont_wait)
 {
     int r;
@@ -72,7 +351,10 @@
             for(n = 0; n < ev_count; n++) {
                 if(ev_fds[n].revents & POLLIN) {
                     r = read(ev_fds[n].fd, ev, sizeof(*ev));
-                    if(r == sizeof(*ev)) return 0;
+                    if(r == sizeof(*ev)) {
+                        if (!vk_modify(&evs[n], ev))
+                            return 0;
+                    }
                 }
             }
         }
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/minui/graphics.c b/minui/graphics.c
index 4127c40..a96342f 100644
--- a/minui/graphics.c
+++ b/minui/graphics.c
@@ -29,7 +29,12 @@
 
 #include <pixelflinger/pixelflinger.h>
 
-#include "font_10x18.h"
+#ifndef BOARD_LDPI_RECOVERY
+	#include "font_10x18.h"
+#else
+	#include "font_7x16.h"
+#endif
+
 #include "minui.h"
 
 typedef struct {
@@ -85,7 +90,7 @@
     fb->version = sizeof(*fb);
     fb->width = vi.xres;
     fb->height = vi.yres;
-    fb->stride = vi.xres;
+    fb->stride = fi.line_length/2; /* stride is the number of pixels until the data of next row, >= xres */;
     fb->data = bits;
     fb->format = GGL_PIXEL_FORMAT_RGB_565;
     memset(fb->data, 0, vi.yres * vi.xres * 2);
@@ -95,8 +100,8 @@
     fb->version = sizeof(*fb);
     fb->width = vi.xres;
     fb->height = vi.yres;
-    fb->stride = vi.xres;
-    fb->data = (void*) (((unsigned) bits) + vi.yres * vi.xres * 2);
+    fb->stride = fi.line_length/2;
+    fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length / 2);
     fb->format = GGL_PIXEL_FORMAT_RGB_565;
     memset(fb->data, 0, vi.yres * vi.xres * 2);
 
diff --git a/minui/resources.c b/minui/resources.c
index 3d2c727..e055e68 100644
--- a/minui/resources.c
+++ b/minui/resources.c
@@ -125,7 +125,7 @@
 
     int y;
     if (channels == 3) {
-        for (y = 0; y < height; ++y) {
+        for (y = 0; y < (int)height; ++y) {
             unsigned char* pRow = pData + y * stride;
             png_read_row(png_ptr, pRow, NULL);
 
@@ -144,7 +144,7 @@
             }
         }
     } else {
-        for (y = 0; y < height; ++y) {
+        for (y = 0; y < (int)height; ++y) {
             unsigned char* pRow = pData + y * stride;
             png_read_row(png_ptr, pRow, NULL);
         }
diff --git a/mmcutils/Android.mk b/mmcutils/Android.mk
new file mode 100644
index 0000000..f1fe294
--- /dev/null
+++ b/mmcutils/Android.mk
@@ -0,0 +1,16 @@
+ifneq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_ARCH),arm)
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+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..44fdcf2
--- /dev/null
+++ b/mmcutils/mmcutils.c
@@ -0,0 +1,591 @@
+/*
+ * 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;
+}
+
+const MmcPartition *
+mmc_find_partition_by_name(const char *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"
+
+static 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
+mmc_format_ext3 (MmcPartition *partition) {
+    char device[128];
+    strcpy(device, partition->device_index);
+    // Run mke2fs
+    char *const mke2fs[] = {MKE2FS_BIN, "-j", device, NULL};
+    if(run_exec_process(mke2fs))
+        return -1;
+
+    // Run tune2fs
+    char *const tune2fs[] = {TUNE2FS_BIN, "-j", "-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_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;
+
+}
+
+
+// TODO: refactor this to not be a giant copy paste mess
+int
+mmc_raw_dump (const MmcPartition *partition, char *out_file) {
+    int ch;
+    FILE *in;
+    FILE *out;
+    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;
+
+    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_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)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    return mmc_raw_copy(p, filename);
+}
+
+int cmd_mmc_backup_raw_partition(const char *partition, const char *filename)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+    return mmc_raw_dump(p, filename);
+}
+
+int cmd_mmc_erase_raw_partition(const char *partition)
+{
+    mmc_scan_partitions();
+    const MmcPartition *p;
+    p = mmc_find_partition_by_name(partition);
+    if (p == NULL)
+        return -1;
+
+    // TODO: implement raw wipe
+    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..64e5813
--- /dev/null
+++ b/mmcutils/mmcutils.h
@@ -0,0 +1,88 @@
+/*
+ * 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);
+
+#endif  // MMCUTILS_H_
+
+
diff --git a/mtdutils/mounts.c b/mounts.c
similarity index 100%
rename from mtdutils/mounts.c
rename to mounts.c
diff --git a/mtdutils/mounts.h b/mounts.h
similarity index 100%
rename from mtdutils/mounts.h
rename to mounts.h
diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk
index 57ab579..90e97fd 100644
--- a/mtdutils/Android.mk
+++ b/mtdutils/Android.mk
@@ -5,20 +5,12 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-	mtdutils.c \
-	mounts.c
+	mtdutils.c
 
 LOCAL_MODULE := libmtdutils
 
 include $(BUILD_STATIC_LIBRARY)
 
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := flash_image.c
-LOCAL_MODULE := flash_image
-LOCAL_MODULE_TAGS := eng
-LOCAL_STATIC_LIBRARIES := libmtdutils
-LOCAL_SHARED_LIBRARIES := libcutils libc
-include $(BUILD_EXECUTABLE)
 
 endif	# TARGET_ARCH == arm
 endif	# !TARGET_SIMULATOR
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index 198f498..c8dbba4 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,295 @@
     }
     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;
+    }
+
+    // If the first part of the file matches the partition, skip writing
+
+    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;
+    }
+
+    MtdReadContext *in = mtd_read_partition(partition);
+    if (in == NULL) {
+        printf("error opening %s: %s\n", partition, strerror(errno));
+        // just assume it needs re-writing
+    } else {
+        char check[HEADER_SIZE];
+        int checklen = mtd_read_data(in, check, sizeof(check));
+        if (checklen <= 0) {
+            printf("error reading %s: %s\n", partition_name, strerror(errno));
+            // just assume it needs re-writing
+        } else if (checklen == headerlen && !memcmp(header, check, headerlen)) {
+            printf("header is the same, not flashing %s\n", partition_name);
+            return 0;
+        }
+        mtd_read_close(in);
+    }
+
+    // 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 100644
index 0000000..0db53ee
--- /dev/null
+++ b/nandroid-md5.sh
@@ -0,0 +1,4 @@
+#!/sbin/sh
+cd $1
+md5sum *img > nandroid.md5
+return $?
\ No newline at end of file
diff --git a/nandroid.c b/nandroid.c
new file mode 100644
index 0000000..4f7724a
--- /dev/null
+++ b/nandroid.c
@@ -0,0 +1,361 @@
+#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"
+
+int print_and_error(const char* message) {
+    ui_print("%s", message);
+    return 1;
+}
+
+int yaffs_files_total = 0;
+int yaffs_files_count = 0;
+void yaffs_callback(char* filename)
+{
+    char* justfile = basename(filename);
+    if (strlen(justfile) < 30)
+        ui_print("%s", justfile);
+    yaffs_files_count++;
+    if (yaffs_files_total != 0)
+        ui_set_progress((float)yaffs_files_count / (float)yaffs_files_total);
+    ui_reset_text_col();
+}
+
+void compute_directory_stats(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);
+}
+
+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;
+    mkyaffs2image_callback callback = NULL;
+    if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
+        callback = yaffs_callback;
+    }
+    
+    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];
+    sprintf(tmp, "%s/%s.img", backup_path, name);
+    ret = mkyaffs2image(mount_point, tmp, 0, callback);
+    if (umount_when_finished) {
+        ensure_path_unmounted(mount_point);
+    }
+    if (0 != ret) {
+        ui_print("Error while making a yaffs2 image of %s!\n", mount_point);
+        return ret;
+    }
+    return 0;
+}
+
+int nandroid_backup_partition(const char* backup_path, char* root) {
+    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("/sdcard") != 0)
+        return print_and_error("Can't mount /sdcard\n");
+    
+    int ret;
+    struct statfs s;
+    if (0 != (ret = statfs("/sdcard", &s)))
+        return print_and_error("Unable to stat /sdcard\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);
+
+#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES
+    ui_print("Backing up boot...\n");
+    sprintf(tmp, "%s/%s", backup_path, "boot.img");
+    ret = backup_raw_partition("boot", tmp);
+    if (0 != ret)
+        return print_and_error("Error while dumping boot image!\n");
+
+    ui_print("Backing up recovery...\n");
+    sprintf(tmp, "%s/%s", backup_path, "recovery.img");
+    ret = backup_raw_partition("recovery", tmp);
+    if (0 != ret)
+        return print_and_error("Error while dumping recovery image!\n");
+#endif
+
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/system")))
+        return ret;
+
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/data")))
+        return ret;
+
+#ifdef BOARD_HAS_DATADATA
+    if (0 != (ret = nandroid_backup_partition(backup_path, "/datadata")))
+        return ret;
+#endif
+
+    struct stat st;
+    if (0 != stat("/sdcard/.android_secure", &st))
+    {
+        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;
+
+    Volume *vol = volume_for_path("/sd-ext");
+    if (vol == NULL || 0 != stat(vol->device, &st))
+    {
+        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, "SDEXT:")))
+            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);
+}
+
+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);
+    
+    char tmp[PATH_MAX];
+    sprintf(tmp, "%s/%s.img", backup_path, name);
+    struct stat file_info;
+    if (0 != (ret = statfs(tmp, &file_info))) {
+        ui_print("%s.img not found. Skipping restore of %s.\n", name, mount_point);
+        return 0;
+    }
+
+    ensure_directory(mount_point);
+
+    unyaffs_callback callback = NULL;
+    if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) {
+        callback = yaffs_callback;
+    }
+
+    ui_print("Restoring %s...\n", name);
+    /*
+    if (0 != (ret = ensure_root_path_unmounted(root))) {
+        ui_print("Can't unmount %s!\n", mount_point);
+        return ret;
+    }
+    */
+    if (0 != (ret = format_device(mount_point))) {
+        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 (0 != (ret = unyaffs(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) {
+    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)
+{
+    ui_set_background(BACKGROUND_ICON_INSTALLING);
+    ui_show_indeterminate_progress();
+    yaffs_files_total = 0;
+
+    if (ensure_path_mounted("/sdcard") != 0)
+        return print_and_error("Can't mount /sdcard\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;
+#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES
+    if (restore_boot)
+    {
+        ui_print("Erasing boot before restore...\n");
+        if (0 != (ret = format_device("boot")))
+            return print_and_error("Error while formatting BOOT:!\n");
+        sprintf(tmp, "%s/boot.img", backup_path);
+        ui_print("Restoring boot image...\n");
+        if (0 != (ret = restore_raw_partition("boot", tmp))) {
+            ui_print("Error while flashing boot image!");
+            return ret;
+        }
+    }
+#endif
+    
+    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;
+        
+#ifdef BOARD_HAS_DATADATA
+    if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "/datadata")))
+        return ret;
+#endif
+
+    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;
+}
+
+void nandroid_generate_timestamp_path(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);
+    }
+}
+
+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);
+    }
+    
+    return nandroid_usage();
+}
diff --git a/nandroid.h b/nandroid.h
new file mode 100644
index 0000000..f124e11
--- /dev/null
+++ b/nandroid.h
@@ -0,0 +1,9 @@
+#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);
+void nandroid_generate_timestamp_path(char* backup_path);
+
+#endif
\ No newline at end of file
diff --git a/reboot.c b/reboot.c
new file mode 100644
index 0000000..04aa9ae
--- /dev/null
+++ b/reboot.c
@@ -0,0 +1,72 @@
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/reboot.h>
+#include <unistd.h>
+#include <cutils/properties.h>
+
+int reboot_main(int argc, char *argv[])
+{
+    int ret;
+    int nosync = 0;
+    int poweroff = 0;
+    int force = 0;
+
+    opterr = 0;
+    do {
+        int c;
+
+        c = getopt(argc, argv, "npf");
+        
+        if (c == EOF) {
+            break;
+        }
+        
+        switch (c) {
+        case 'n':
+            nosync = 1;
+            break;
+        case 'p':
+            poweroff = 1;
+            break;
+    	case 'f':
+	        force = 1;
+	        break;
+        case '?':
+            fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    } while (1);
+
+    if(argc > optind + 1) {
+        fprintf(stderr, "%s: too many arguments\n", argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if(!nosync)
+        sync();
+
+    if(force || argc > optind) {
+        if(poweroff)
+            ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL);
+        else if(argc > optind)
+            ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, argv[optind]);
+        else
+            ret = reboot(RB_AUTOBOOT);
+    } else {
+        if(poweroff) {
+            property_set("ctl.start", "poweroff");
+            ret = 0;
+        } else {
+            property_set("ctl.start", "reboot");
+            ret = 0;
+        }
+    }
+
+    if(ret < 0) {
+        perror("reboot");
+        exit(EXIT_FAILURE);
+    }
+    fprintf(stderr, "reboot returned\n");
+    return 0;
+}
diff --git a/recovery.c b/recovery.c
index 9ad075d..f91edc1 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.
@@ -29,6 +30,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <dirent.h>
+#include <sys/stat.h>
 
 #include "bootloader.h"
 #include "common.h"
@@ -40,6 +42,8 @@
 #include "recovery_ui.h"
 #include "encryptedfs_provisioning.h"
 
+#include "extendedcommands.h"
+
 static const struct option OPTIONS[] = {
   { "send_intent", required_argument, NULL, 's' },
   { "update_package", required_argument, NULL, 'u' },
@@ -55,6 +59,8 @@
 static const char *LOG_FILE = "/cache/recovery/log";
 static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
 static const char *SDCARD_ROOT = "/sdcard";
+static int allow_display_toggle = 1;
+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";
 
@@ -152,6 +158,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;
 }
 
@@ -171,7 +178,9 @@
 get_args(int *argc, char ***argv) {
     struct bootloader_message boot;
     memset(&boot, 0, sizeof(boot));
+#ifndef BOARD_HAS_NO_MISC_PARTITION
     get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure
+#endif
 
     if (boot.command[0] != 0 && boot.command[0] != 255) {
         LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
@@ -181,8 +190,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")) {
@@ -226,10 +237,13 @@
         strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
         strlcat(boot.recovery, "\n", sizeof(boot.recovery));
     }
+#ifndef BOARD_HAS_NO_MISC_PARTITION
     set_bootloader_message(&boot);
+#endif
 }
 
-static void
+#ifndef BOARD_HAS_NO_MISC_PARTITION
+void
 set_sdcard_update_bootloader_message() {
     struct bootloader_message boot;
     memset(&boot, 0, sizeof(boot));
@@ -237,6 +251,7 @@
     strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
     set_bootloader_message(&boot);
 }
+#endif
 
 // How much of the temp log we have copied to the copy in cache.
 static long tmplog_offset = 0;
@@ -288,10 +303,12 @@
     copy_log_file(LAST_LOG_FILE, false);
     chmod(LAST_LOG_FILE, 0640);
 
+#ifndef BOARD_HAS_NO_MISC_PARTITION
     // Reset to mormal system boot so recovery won't cycle indefinitely.
     struct bootloader_message boot;
     memset(&boot, 0, sizeof(boot));
     set_bootloader_message(&boot);
+#endif
 
     // Remove the command file, so recovery won't repeat indefinitely.
     if (ensure_path_mounted(COMMAND_FILE) != 0 ||
@@ -409,9 +426,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 };
 
@@ -431,23 +447,30 @@
     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();
 
         int action = device_handle_key(key, visible);
 
+        int old_selected = selected;
+
         if (action < 0) {
             switch (action) {
                 case HIGHLIGHT_UP:
@@ -460,16 +483,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;
 }
 
@@ -634,6 +681,10 @@
     device_wipe_data();
     erase_volume("/data");
     erase_volume("/cache");
+    erase_volume("/datadata");
+    erase_volume("/datadata");
+    erase_volume("/sd-ext");
+    erase_volume("/sdcard/.android_secure");
     ui_print("Data wipe complete.\n");
 }
 
@@ -645,7 +696,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
@@ -662,16 +715,20 @@
                 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:
-                ;
-                int status = sdcard_directory(SDCARD_ROOT);
-                if (status >= 0) {
+                if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip"))
+                {
+                    ui_print("\n-- Install from sdcard...\n");
+                    int status = install_package(SDCARD_PACKAGE_FILE);
                     if (status != INSTALL_SUCCESS) {
                         ui_set_background(BACKGROUND_ICON_ERROR);
                         ui_print("Installation aborted.\n");
@@ -682,6 +739,18 @@
                     }
                 }
                 break;
+            case ITEM_INSTALL_ZIP:
+                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;
         }
     }
 }
@@ -693,6 +762,29 @@
 
 int
 main(int argc, char **argv) {
+	if (strstr(argv[0], "recovery") == NULL)
+	{
+	    if (strstr(argv[0], "flash_image") != NULL)
+	        return flash_image_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);
+        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...
@@ -701,7 +793,7 @@
     printf("Starting recovery on %s", ctime(&start));
 
     ui_init();
-    ui_set_background(BACKGROUND_ICON_INSTALLING);
+    ui_print(EXPAND(RECOVERY_VERSION)"\n");
     load_volume_table();
     get_args(&argc, &argv);
 
@@ -757,7 +849,7 @@
     printf("\n");
 
     int status = INSTALL_SUCCESS;
-
+    
     if (toggle_secure_fs) {
         if (strcmp(encrypted_fs_mode,"on") == 0) {
             encrypted_fs_data.mode = MODE_ENCRYPTED_FS_ENABLED;
@@ -806,10 +898,31 @@
         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);
+        
+        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();
     }
@@ -821,3 +934,7 @@
     reboot(RB_AUTOBOOT);
     return EXIT_SUCCESS;
 }
+
+int get_allow_toggle_display() {
+    return allow_display_toggle;
+}
diff --git a/recovery_ui.h b/recovery_ui.h
index e451bdf..f34365f 100644
--- a/recovery_ui.h
+++ b/recovery_ui.h
@@ -64,11 +64,16 @@
 #define HIGHLIGHT_UP        -2
 #define HIGHLIGHT_DOWN      -3
 #define SELECT_ITEM         -4
+#define GO_BACK             -5
 
 #define ITEM_REBOOT          0
 #define ITEM_APPLY_SDCARD    1
 #define ITEM_WIPE_DATA       2
 #define ITEM_WIPE_CACHE      3
+#define ITEM_INSTALL_ZIP     4
+#define ITEM_NANDROID        5
+#define ITEM_PARTITION       6
+#define ITEM_ADVANCED        7
 
 // Header text to display above the main menu.
 extern char* MENU_HEADERS[];
@@ -76,4 +81,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/roots.c b/roots.c
index 90b82d7..184bfd0 100644
--- a/roots.c
+++ b/roots.c
@@ -23,7 +23,7 @@
 #include <ctype.h>
 
 #include "mtdutils/mtdutils.h"
-#include "mtdutils/mounts.h"
+#include "mounts.h"
 #include "roots.h"
 #include "common.h"
 #include "make_ext4fs.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 84a9b85..d973a50 100644
--- a/ui.c
+++ b/ui.c
@@ -29,11 +29,25 @@
 #include "minui/minui.h"
 #include "recovery_ui.h"
 
+#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 PROGRESSBAR_INDETERMINATE_STATES 6
 #define PROGRESSBAR_INDETERMINATE_FPS 15
@@ -43,6 +57,7 @@
 static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES];
 static gr_surface gProgressBarEmpty;
 static gr_surface gProgressBarFill;
+static int ui_has_initialized = 0;
 
 static const struct { gr_surface* surface; const char *name; } BITMAPS[] = {
     { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" },
@@ -79,9 +94,10 @@
 static int text_col = 0, text_row = 0, text_top = 0;
 static int show_text = 0;
 
-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;
@@ -148,10 +164,15 @@
   }
 }
 
+#define MENU_TEXT_COLOR 7, 133, 74, 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();
 
@@ -160,29 +181,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]);
         }
     }
 }
@@ -191,6 +226,7 @@
 // Should only be called with gUpdateMutex locked.
 static void update_screen_locked(void)
 {
+    if (!ui_has_initialized) return;
     draw_screen_locked();
     gr_flip();
 }
@@ -199,6 +235,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;
@@ -308,6 +345,7 @@
 
 void ui_init(void)
 {
+    ui_has_initialized = 1;
     gr_init();
     ev_init();
 
@@ -337,6 +375,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);
@@ -425,7 +480,17 @@
     pthread_mutex_unlock(&gUpdateMutex);
 }
 
-void ui_start_menu(char** headers, char** items, int initial_selection) {
+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) {
@@ -435,17 +500,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) {
@@ -454,9 +530,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);
@@ -513,3 +601,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 dcc6a49..5c43e1c 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,3 +73,9 @@
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 include $(BUILD_EXECUTABLE)
+
+
+file := $(PRODUCT_OUT)/utilities/update-binary
+ALL_PREBUILT += $(file)
+$(file) : $(TARGET_OUT)/bin/updater | $(ACP)
+	$(transform-prebuilt-to-target)
diff --git a/updater/install.c b/updater/install.c
index a596dc6..478055d 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009 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.
@@ -31,8 +32,10 @@
 #include "edify/expr.h"
 #include "mincrypt/sha.h"
 #include "minzip/DirUtil.h"
-#include "mtdutils/mounts.h"
+#include "mounts.h"
+#include "flashutils/flashutils.h"
 #include "mtdutils/mtdutils.h"
+#include "mmcutils/mmcutils.h"
 #include "updater.h"
 #include "applypatch/applypatch.h"
 
@@ -78,23 +81,11 @@
 
     mkdir(mount_point, 0755);
 
-    if (strcmp(partition_type, "MTD") == 0) {
-        mtd_scan_partitions();
-        const MtdPartition* mtd;
-        mtd = mtd_find_partition_by_name(location);
-        if (mtd == NULL) {
-            fprintf(stderr, "%s: no mtd partition named \"%s\"",
-                    name, location);
+    if (strcmp(partition_type, "MTD") == 0 || strcmp(partition_type, "MMC") == 0) {
+        if (0 == mount_partition(location, mount_point, get_default_filesystem(), 0))
+            result = mount_point;
+        else
             result = strdup("");
-            goto done;
-        }
-        if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) {
-            fprintf(stderr, "mtd mount of %s failed: %s\n",
-                    location, strerror(errno));
-            result = strdup("");
-            goto done;
-        }
-        result = mount_point;
     } else {
         if (mount(location, mount_point, fs_type,
                   MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
@@ -204,33 +195,11 @@
         goto done;
     }
 
-    if (strcmp(partition_type, "MTD") == 0) {
-        mtd_scan_partitions();
-        const MtdPartition* mtd = mtd_find_partition_by_name(location);
-        if (mtd == NULL) {
-            fprintf(stderr, "%s: no mtd partition named \"%s\"",
-                    name, location);
+    if (strcmp(partition_type, "MTD") == 0 || strcmp(partition_type, "MMC") == 0) {
+        if (0 != erase_partition(location, NULL)) {
             result = strdup("");
             goto done;
         }
-        MtdWriteContext* ctx = mtd_write_partition(mtd);
-        if (ctx == NULL) {
-            fprintf(stderr, "%s: can't write \"%s\"", name, location);
-            result = strdup("");
-            goto done;
-        }
-        if (mtd_erase_blocks(ctx, -1) == -1) {
-            mtd_write_close(ctx);
-            fprintf(stderr, "%s: failed to erase \"%s\"", name, location);
-            result = strdup("");
-            goto done;
-        }
-        if (mtd_write_close(ctx) != 0) {
-            fprintf(stderr, "%s: failed to close \"%s\"", name, location);
-            result = strdup("");
-            goto done;
-        }
-        result = location;
 #ifdef USE_EXT4
     } else if (strcmp(fs_type, "ext4") == 0) {
         reset_ext4fs_info();
@@ -247,7 +216,7 @@
         fprintf(stderr, "%s: unsupported fs_type \"%s\" partition_type \"%s\"",
                 name, fs_type, partition_type);
     }
-
+    result = location;
 done:
     free(fs_type);
     free(partition_type);
@@ -680,57 +649,10 @@
         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(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;
-
-    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);
-        if (!success) {
-            fprintf(stderr, "mtd_write_data to %s failed: %s\n",
-                    partition, strerror(errno));
-        }
-    }
-    free(buffer);
-    fclose(f);
-
-    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 from %s\n",
-           success ? "wrote" : "failed to write", partition, filename);
-
-    result = success ? partition : strdup("");
 
 done:
     if (result != partition) free(partition);
diff --git a/utilities/Android.mk b/utilities/Android.mk
new file mode 100644
index 0000000..f6b6e64
--- /dev/null
+++ b/utilities/Android.mk
@@ -0,0 +1,45 @@
+LOCAL_PATH := $(call my-dir)
+
+ifndef BOARD_HAS_SMALL_RECOVERY
+
+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 := 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)
+
+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)
+
+endif
diff --git a/utilities/e2fsck b/utilities/e2fsck
new file mode 100644
index 0000000..2844a1d
--- /dev/null
+++ b/utilities/e2fsck
Binary files differ
diff --git a/utilities/fix_permissions b/utilities/fix_permissions
new file mode 100644
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/parted b/utilities/parted
new file mode 100644
index 0000000..bb3d432
--- /dev/null
+++ b/utilities/parted
Binary files differ
diff --git a/utilities/sdparted b/utilities/sdparted
new file mode 100644
index 0000000..d2a46a4
--- /dev/null
+++ b/utilities/sdparted
@@ -0,0 +1,637 @@
+#!/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"
+		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=
+if [ -z "$SDPATH" ]
+then
+    SDPATH="/dev/block/mmcblk0"
+else
+    echo Found SDPATH=$SDPATH
+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