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