diff --git a/Android.mk b/Android.mk index 00fa078..b4c8f4f 100644 --- a/Android.mk +++ b/Android.mk @@ -19,20 +19,25 @@ LOCAL_SRC_FILES := \ extendedcommands.c \ nandroid.c \ ../../system/core/toolbox/reboot.c \ + firmware.c \ edifyscripting.c \ setprop.c +ADDITIONAL_RECOVERY_FILES := $(shell echo $$ADDITIONAL_RECOVERY_FILES) +LOCAL_SRC_FILES += $(ADDITIONAL_RECOVERY_FILES) + LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true ifdef I_AM_KOUSH RECOVERY_NAME := ClockworkMod Recovery +LOCAL_CFLAGS += -DI_AM_KOUSH else RECOVERY_NAME := CWM-based Recovery endif -RECOVERY_VERSION := $(RECOVERY_NAME) v4.0.1.5 +RECOVERY_VERSION := $(RECOVERY_NAME) v5.0.2.3 LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)" RECOVERY_API_VERSION := 2 diff --git a/bootloader.c b/bootloader.c index d455923..d4039d8 100644 --- a/bootloader.c +++ b/bootloader.c @@ -179,3 +179,153 @@ static int set_bootloader_message_block(const struct bootloader_message *in, } 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; +} \ No newline at end of file diff --git a/edifyscripting.c b/edifyscripting.c index 89554d7..389f1c9 100644 --- a/edifyscripting.c +++ b/edifyscripting.c @@ -233,7 +233,24 @@ Value* InstallZipFn(const char* name, State* state, int argc, Expr* argv[]) { 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); diff --git a/extendedcommands.c b/extendedcommands.c index 10083bb..2d0c598 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -40,6 +40,7 @@ #include "flashutils/flashutils.h" #include "edify/expr.h" #include +#include "mtdutils/mtdutils.h" int signature_check_enabled = 1; @@ -81,11 +82,13 @@ 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() { @@ -93,6 +96,10 @@ void show_install_update_menu() "", 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); @@ -111,7 +118,10 @@ void show_install_update_menu() break; } case ITEM_CHOOSE_ZIP: - show_choose_zip_menu(); + show_choose_zip_menu("/sdcard/"); + break; + case ITEM_CHOOSE_ZIP_INT: + show_choose_zip_menu("/emmc/"); break; default: return; @@ -301,10 +311,10 @@ char* choose_file_menu(const char* directory, const char* fileExtensionOrDirecto return return_value; } -void show_choose_zip_menu() +void show_choose_zip_menu(const char *mount_point) { - if (ensure_path_mounted("/sdcard") != 0) { - LOGE ("Can't mount /sdcard\n"); + if (ensure_path_mounted(mount_point) != 0) { + LOGE ("Can't mount %s\n", mount_point); return; } @@ -313,7 +323,7 @@ void show_choose_zip_menu() NULL }; - char* file = choose_file_menu("/sdcard/", ".zip", headers); + char* file = choose_file_menu(mount_point, ".zip", headers); if (file == NULL) return; static char* confirm_install = "Confirm install?"; @@ -410,6 +420,70 @@ int confirm_selection(const char* title, const char* confirm) #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(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"); @@ -729,7 +803,32 @@ int run_and_remove_extendedcommand() 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); } @@ -901,9 +1000,9 @@ void show_advanced_menu() __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"); - ui_print("Dalvik Cache wiped.\n"); break; } case 2: 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 +#include +#include + +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/install.c b/install.c index 0b35ee7..cae283c 100644 --- a/install.c +++ b/install.c @@ -92,6 +92,12 @@ handle_firmware_update(char* type, char* filename, ZipArchive* zip) { 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; diff --git a/nandroid-md5.sh b/nandroid-md5.sh index 0db53ee..03d4a46 100755 --- a/nandroid-md5.sh +++ b/nandroid-md5.sh @@ -1,4 +1,12 @@ #!/sbin/sh cd $1 -md5sum *img > nandroid.md5 -return $? \ No newline at end of file +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 index f4b2a74..30190fa 100644 --- a/nandroid.c +++ b/nandroid.c @@ -101,15 +101,17 @@ 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) { - return mkyaffs2image(backup_path, backup_file_image, 0, callback ? yaffs_callback : NULL); + 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 --exclude 'media' $(basename %s) ; exit $?", backup_path, backup_file_image, backup_path); + 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 $(basename %s) ; exit $?", backup_path, backup_file_image, backup_path); + 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) { @@ -132,7 +134,6 @@ static nandroid_backup_handler get_backup_handler(const char *backup_path) { 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); @@ -143,14 +144,15 @@ static nandroid_backup_handler get_backup_handler(const char *backup_path) { return tar_compress_wrapper; } - char str[255]; - char* partition; - property_get("ro.cwm.prefer_tar", str, "false"); - if (strcmp("true", str) != 0) { + // cwr5, we prefer tar for everything except yaffs2 + if (strcmp("yaffs2", mv->filesystem) == 0) { return mkyaffs2image_wrapper; } - if (strcmp("yaffs2", mv->filesystem) == 0) { + char str[255]; + char* partition; + property_get("ro.cwm.prefer_tar", str, "true"); + if (strcmp("true", str) != 0) { return mkyaffs2image_wrapper; } @@ -163,7 +165,7 @@ int nandroid_backup_partition_extended(const char* backup_path, const char* moun char* name = basename(mount_point); struct stat file_info; - int callback = stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info) == 0; + 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)) { @@ -172,7 +174,15 @@ int nandroid_backup_partition_extended(const char* backup_path, const char* moun } compute_directory_stats(mount_point); char tmp[PATH_MAX]; - sprintf(tmp, "%s/%s.img", backup_path, name); + 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"); @@ -357,6 +367,7 @@ static nandroid_restore_handler get_restore_handler(const char *backup_path) { 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"); @@ -374,37 +385,83 @@ static nandroid_restore_handler get_restore_handler(const char *backup_path) { 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", 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))) { - ui_print("%s.img not found. Skipping restore of %s.\n", name, mount_point); - return 0; + // 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; + int callback = stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info) != 0; ui_print("Restoring %s...\n", name); - /* - if (0 != (ret = ensure_root_path_unmounted(root))) { - ui_print("Can't unmount %s!\n", mount_point); - return ret; + if (backup_filesystem == NULL) { + if (0 != (ret = format_volume(mount_point))) { + ui_print("Error while formatting %s!\n", mount_point); + return ret; + } } - */ - if (0 != (ret = format_volume(mount_point))) { + 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; } - - nandroid_restore_handler restore_handler = get_restore_handler(mount_point); + + 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; diff --git a/recovery.c b/recovery.c index d2e3261..68d28ef 100644 --- a/recovery.c +++ b/recovery.c @@ -946,6 +946,9 @@ main(int argc, char **argv) { 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); if(!poweroff) diff --git a/res/images/icon_firmware_error.png b/res/images/icon_firmware_error.png new file mode 100644 index 0000000..0c32c9e Binary files /dev/null and b/res/images/icon_firmware_error.png differ diff --git a/res/images/icon_firmware_install.png b/res/images/icon_firmware_install.png new file mode 100755 index 0000000..2da9e5f Binary files /dev/null and b/res/images/icon_firmware_install.png differ diff --git a/ui.c b/ui.c index d953562..6c747c1 100644 --- a/ui.c +++ b/ui.c @@ -66,6 +66,8 @@ 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" }, { &gProgressBarIndeterminate[0], "indeterminate1" }, { &gProgressBarIndeterminate[1], "indeterminate2" }, { &gProgressBarIndeterminate[2], "indeterminate3" }, @@ -168,7 +170,8 @@ static void draw_text_line(int row, const char* t) { } } -#define MENU_TEXT_COLOR 255, 160, 49, 255 +//#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