diff --git a/Android.mk b/Android.mk index e6c3547..b0fc158 100644 --- a/Android.mk +++ b/Android.mk @@ -5,6 +5,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) commands_recovery_local_path := $(LOCAL_PATH) +# LOCAL_CPP_EXTENSION := .c LOCAL_SRC_FILES := \ recovery.c \ @@ -13,15 +14,30 @@ LOCAL_SRC_FILES := \ roots.c \ ui.c \ verifier.c \ - encryptedfs_provisioning.c + encryptedfs_provisioning.c \ + mounts.c \ + extendedcommands.c \ + nandroid.c \ + reboot.c \ + setprop.c LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -RECOVERY_API_VERSION := 3 +RECOVERY_VERSION := ClockworkMod Recovery v2.5.1.8 +LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)" +RECOVERY_API_VERSION := 2 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) +BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_SDCARD_DEVICE_PRIMARY BOARD_SDCARD_DEVICE_SECONDARY BOARD_SDEXT_DEVICE BOARD_SDEXT_FILESYSTEM BOARD_DATA_DEVICE BOARD_DATA_FILESYSTEM BOARD_DATADATA_DEVICE BOARD_DATADATA_FILESYSTEM BOARD_CACHE_DEVICE BOARD_CACHE_FILESYSTEM BOARD_SYSTEM_DEVICE BOARD_SYSTEM_FILESYSTEM BOARD_HAS_DATADATA BOARD_DATA_FILESYSTEM_OPTIONS BOARD_DATADATA_FILESYSTEM_OPTIONS BOARD_CACHE_FILESYSTEM_OPTIONS BOARD_SYSTEM_FILESYSTEM_OPTIONS BOARD_HAS_MTD_CACHE BOARD_USES_BMLUTILS BOARD_USES_MMCUTILS BOARD_HAS_SMALL_RECOVERY BOARD_LDPI_RECOVERY BOARD_RECOVERY_IGNORE_BOOTABLES BOARD_HAS_NO_MISC_PARTITION BOARD_HAS_SDCARD_INTERNAL BOARD_SDCARD_DEVICE_INTERNAL BOARD_HIJACK_RECOVERY_PATH + +$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \ + $(if $($(board_define)), \ + $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \ + ) \ + ) + # This binary is in the recovery ramdisk, which is otherwise a copy of root. # It gets copied there in config/Makefile. LOCAL_MODULE_TAGS suppresses # a (redundant) copy of the binary in /system/bin for user builds. @@ -30,13 +46,18 @@ LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := -ifeq ($(TARGET_RECOVERY_UI_LIB),) +ifeq ($(BOARD_CUSTOM_RECOVERY_KEYMAPPING),) LOCAL_SRC_FILES += default_recovery_ui.c else - LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) + LOCAL_SRC_FILES += $(BOARD_CUSTOM_RECOVERY_KEYMAPPING) endif + LOCAL_STATIC_LIBRARIES += libext4_utils libz -LOCAL_STATIC_LIBRARIES += libminzip libunz libmtdutils libmincrypt +LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt + +LOCAL_STATIC_LIBRARIES += libbusybox libclearsilverregex libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image +LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils + LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils LOCAL_STATIC_LIBRARIES += libstdc++ libc @@ -44,6 +65,46 @@ LOCAL_C_INCLUDES += system/extras/ext4_utils include $(BUILD_EXECUTABLE) +RECOVERY_LINKS := edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot + +# nc is provided by external/netcat +RECOVERY_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS)) +$(RECOVERY_SYMLINKS): RECOVERY_BINARY := $(LOCAL_MODULE) +$(RECOVERY_SYMLINKS): $(LOCAL_INSTALLED_MODULE) + @echo "Symlink: $@ -> $(RECOVERY_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf $(RECOVERY_BINARY) $@ + +ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_SYMLINKS) + +# Now let's do recovery symlinks +BUSYBOX_LINKS := $(shell cat external/busybox/busybox-minimal.links) +RECOVERY_BUSYBOX_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(filter-out $(exclude),$(notdir $(BUSYBOX_LINKS)))) +$(RECOVERY_BUSYBOX_SYMLINKS): BUSYBOX_BINARY := busybox +$(RECOVERY_BUSYBOX_SYMLINKS): $(LOCAL_INSTALLED_MODULE) + @echo "Symlink: $@ -> $(BUSYBOX_BINARY)" + @mkdir -p $(dir $@) + @rm -rf $@ + $(hide) ln -sf $(BUSYBOX_BINARY) $@ + +ALL_DEFAULT_INSTALLED_MODULES += $(RECOVERY_BUSYBOX_SYMLINKS) + +include $(CLEAR_VARS) +LOCAL_MODULE := nandroid-md5.sh +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := nandroid-md5.sh +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := killrecovery.sh +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := killrecovery.sh +include $(BUILD_PREBUILT) include $(CLEAR_VARS) @@ -60,13 +121,17 @@ LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc include $(BUILD_EXECUTABLE) +include $(commands_recovery_local_path)/bmlutils/Android.mk +include $(commands_recovery_local_path)/flashutils/Android.mk include $(commands_recovery_local_path)/minui/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk +include $(commands_recovery_local_path)/mmcutils/Android.mk include $(commands_recovery_local_path)/tools/Android.mk include $(commands_recovery_local_path)/edify/Android.mk include $(commands_recovery_local_path)/updater/Android.mk include $(commands_recovery_local_path)/applypatch/Android.mk +include $(commands_recovery_local_path)/utilities/Android.mk commands_recovery_local_path := endif # TARGET_ARCH == arm diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk new file mode 100644 index 0000000..e95cb5f --- /dev/null +++ b/bmlutils/Android.mk @@ -0,0 +1,8 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CFLAGS += -DBOARD_BOOT_DEVICE=\"$(BOARD_BOOT_DEVICE)\" +LOCAL_SRC_FILES := bmlutils.c +LOCAL_MODULE := libbmlutils +LOCAL_MODULE_TAGS := eng +include $(BUILD_STATIC_LIBRARY) diff --git a/bmlutils/bmlutils.c b/bmlutils/bmlutils.c new file mode 100644 index 0000000..1cc28b2 --- /dev/null +++ b/bmlutils/bmlutils.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +extern int __system(const char *command); +#define BML_UNLOCK_ALL 0x8A29 ///< unlock all partition RO -> RW + +static int restore_internal(const char* bml, const char* filename) +{ + char buf[4096]; + int dstfd, srcfd, bytes_read, bytes_written, total_read = 0; + if (filename == NULL) + srcfd = 0; + else { + srcfd = open(filename, O_RDONLY | O_LARGEFILE); + if (srcfd < 0) + return 2; + } + dstfd = open(bml, O_RDWR | O_LARGEFILE); + if (dstfd < 0) + return 3; + if (ioctl(dstfd, BML_UNLOCK_ALL, 0)) + return 4; + do { + total_read += bytes_read = read(srcfd, buf, 4096); + if (!bytes_read) + break; + if (bytes_read < 4096) + memset(&buf[bytes_read], 0, 4096 - bytes_read); + if (write(dstfd, buf, 4096) < 4096) + return 5; + } while(bytes_read == 4096); + + close(dstfd); + close(srcfd); + + return 0; +} + +int cmd_bml_restore_raw_partition(const char *partition, const char *filename) +{ + char *bml; + if (strcmp(partition, "boot") == 0 || strcmp(partition, "recovery") == 0) + bml = "/dev/block/bml7"; + else + return 6; + + int ret = restore_internal("/dev/block/bml7", filename); + if (ret != 0) + return ret; + + ret = restore_internal("/dev/block/bml8", filename); + return ret; +} + +int cmd_bml_backup_raw_partition(const char *partition, const char *filename) +{ + char tmp[PATH_MAX]; + sprintf(tmp, "dd of=%s if=/dev/block/bml7 bs=4096", filename); + return __system(tmp); +} + +int cmd_bml_erase_raw_partition(const char *partition) +{ + // TODO: implement raw wipe + return 0; +} + +int cmd_bml_erase_partition(const char *partition, const char *filesystem) +{ + return -1; +} + +int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only) +{ + return -1; +} + +int cmd_bml_get_partition_device(const char *partition, char *device) +{ + return -1; +} diff --git a/bootloader.h b/bootloader.h index 2e749aa..3d4302f 100644 --- a/bootloader.h +++ b/bootloader.h @@ -47,4 +47,13 @@ struct bootloader_message { int get_bootloader_message(struct bootloader_message *out); int set_bootloader_message(const struct bootloader_message *in); +/* Write an update to the cache partition for update-radio or update-hboot. + * Note, this destroys any filesystem on the cache partition! + * The expected bitmap format is 240x320, 16bpp (2Bpp), RGB 5:6:5. + */ +int write_update_for_bootloader( + const char *update, int update_len, + int bitmap_width, int bitmap_height, int bitmap_bpp, + const char *busy_bitmap, const char *error_bitmap); + #endif diff --git a/common.h b/common.h index 97e87ee..df26597 100644 --- a/common.h +++ b/common.h @@ -34,10 +34,13 @@ void ui_clear_key_queue(); // so keep the output short and not too cryptic. void ui_print(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +void ui_reset_text_col(); +void ui_set_show_text(int value); + // Display some header text followed by a menu of items, which appears // at the top of the screen (in place of any scrolling ui_print() // output, if necessary). -void ui_start_menu(char** headers, char** items, int initial_selection); +int ui_start_menu(char** headers, char** items, int initial_selection); // Set the menu highlight to the given index, and return it (capped to // the range [0..numitems). int ui_menu_select(int sel); @@ -45,15 +48,25 @@ int ui_menu_select(int sel); // statements will be displayed. void ui_end_menu(); +int ui_get_showing_back_button(); +void ui_set_showing_back_button(int showBackButton); + // Set the icon (normally the only thing visible besides the progress bar). enum { BACKGROUND_ICON_NONE, BACKGROUND_ICON_INSTALLING, BACKGROUND_ICON_ERROR, + BACKGROUND_ICON_FIRMWARE_INSTALLING, + BACKGROUND_ICON_FIRMWARE_ERROR, NUM_BACKGROUND_ICONS }; void ui_set_background(int icon); +// Get a malloc'd copy of the screen image showing (only) the specified icon. +// Also returns the width, height, and bits per pixel of the returned image. +// TODO: Use some sort of "struct Bitmap" here instead of all these variables? +char *ui_copy_image(int icon, int *width, int *height, int *bpp); + // Show a progress bar and define the scope of the next operation: // portion - fraction of the progress bar the next operation will use // seconds - expected time interval (progress bar moves at this minimum rate) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index ce12787..9c192a2 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -18,15 +18,18 @@ #include "recovery_ui.h" #include "common.h" +#include "extendedcommands.h" -char* MENU_HEADERS[] = { "Android system recovery utility", - "", - NULL }; +char* MENU_HEADERS[] = { NULL }; char* MENU_ITEMS[] = { "reboot system now", "apply update from sdcard", "wipe data/factory reset", "wipe cache partition", + "install zip from sdcard", + "backup and restore", + "mounts and storage", + "advanced", NULL }; int device_recovery_start() { @@ -34,7 +37,14 @@ int device_recovery_start() { } int device_toggle_display(volatile char* key_pressed, int key_code) { - return key_code == KEY_HOME; + int alt = key_pressed[KEY_LEFTALT] || key_pressed[KEY_RIGHTALT]; + if (alt && key_code == KEY_L) + return 1; + // allow toggling of the display if the correct key is pressed, and the display toggle is allowed or the display is currently off + if (ui_get_showing_back_button()) { + return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_END); + } + return get_allow_toggle_display() && (key_code == KEY_HOME || key_code == KEY_MENU || key_code == KEY_POWER || key_code == KEY_END); } int device_reboot_now(volatile char* key_pressed, int key_code) { @@ -44,16 +54,37 @@ int device_reboot_now(volatile char* key_pressed, int key_code) { int device_handle_key(int key_code, int visible) { if (visible) { switch (key_code) { + case KEY_CAPSLOCK: case KEY_DOWN: case KEY_VOLUMEDOWN: return HIGHLIGHT_DOWN; + case KEY_LEFTSHIFT: case KEY_UP: case KEY_VOLUMEUP: return HIGHLIGHT_UP; + case KEY_POWER: + if (ui_get_showing_back_button()) { + return SELECT_ITEM; + } + if (!get_allow_toggle_display()) + return GO_BACK; + break; + case KEY_LEFTBRACE: case KEY_ENTER: + case BTN_MOUSE: + case KEY_CENTER: + case KEY_CAMERA: + case KEY_F21: + case KEY_SEND: return SELECT_ITEM; + + case KEY_END: + case KEY_BACKSPACE: + case KEY_BACK: + if (!get_allow_toggle_display()) + return GO_BACK; } } diff --git a/encryptedfs_provisioning.c b/encryptedfs_provisioning.c index 601c817..678c09f 100644 --- a/encryptedfs_provisioning.c +++ b/encryptedfs_provisioning.c @@ -27,7 +27,7 @@ #include "cutils/properties.h" #include "common.h" #include "mtdutils/mtdutils.h" -#include "mtdutils/mounts.h" +#include "mounts.h" #include "roots.h" const char* encrypted_fs_enabled_property = "persist.security.secfs.enabled"; diff --git a/etc/init.rc b/etc/init.rc index a675a4b..e6b43e0 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -10,6 +10,7 @@ on init symlink /system/etc /etc mkdir /sdcard + mkdir /emmc mkdir /system mkdir /data mkdir /cache diff --git a/extendedcommands.c b/extendedcommands.c new file mode 100644 index 0000000..df22448 --- /dev/null +++ b/extendedcommands.c @@ -0,0 +1,972 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "bootloader.h" +#include "common.h" +#include "cutils/properties.h" +#include "firmware.h" +#include "install.h" +#include "minui/minui.h" +#include "minzip/DirUtil.h" +#include "roots.h" +#include "recovery_ui.h" + +#include "../../external/yaffs2/yaffs2/utils/mkyaffs2image.h" +#include "../../external/yaffs2/yaffs2/utils/unyaffs.h" + +#include "extendedcommands.h" +#include "nandroid.h" +#include "mounts.h" + +int signature_check_enabled = 1; +int script_assert_enabled = 1; +static const char *SDCARD_UPDATE_FILE = "/sdcard/update.zip"; + +void +toggle_signature_check() +{ + signature_check_enabled = !signature_check_enabled; + ui_print("Signature Check: %s\n", signature_check_enabled ? "Enabled" : "Disabled"); +} + +void toggle_script_asserts() +{ + script_assert_enabled = !script_assert_enabled; + ui_print("Script Asserts: %s\n", script_assert_enabled ? "Enabled" : "Disabled"); +} + +int install_zip(const char* packagefilepath) +{ + ui_print("\n-- Installing: %s\n", packagefilepath); +#ifndef BOARD_HAS_NO_MISC_PARTITION + set_sdcard_update_bootloader_message(); +#endif + int status = install_package(packagefilepath); + ui_reset_progress(); + if (status != INSTALL_SUCCESS) { + ui_set_background(BACKGROUND_ICON_ERROR); + ui_print("Installation aborted.\n"); + return 1; + } + ui_set_background(BACKGROUND_ICON_NONE); + ui_print("\nInstall from sdcard complete.\n"); + return 0; +} + +char* INSTALL_MENU_ITEMS[] = { "apply /sdcard/update.zip", + "choose zip from sdcard", + "toggle signature verification", + "toggle script asserts", + NULL }; +#define ITEM_APPLY_SDCARD 0 +#define ITEM_CHOOSE_ZIP 1 +#define ITEM_SIG_CHECK 2 +#define ITEM_ASSERTS 3 + +void show_install_update_menu() +{ + static char* headers[] = { "Apply update from .zip file on SD card", + "", + NULL + }; + for (;;) + { + int chosen_item = get_menu_selection(headers, INSTALL_MENU_ITEMS, 0, 0); + switch (chosen_item) + { + case ITEM_ASSERTS: + toggle_script_asserts(); + break; + case ITEM_SIG_CHECK: + toggle_signature_check(); + break; + case ITEM_APPLY_SDCARD: + { + if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip")) + install_zip(SDCARD_UPDATE_FILE); + break; + } + case ITEM_CHOOSE_ZIP: + show_choose_zip_menu(); + break; + default: + return; + } + + } +} + +void free_string_array(char** array) +{ + if (array == NULL) + return; + char* cursor = array[0]; + int i = 0; + while (cursor != NULL) + { + free(cursor); + cursor = array[++i]; + } + free(array); +} + +char** gather_files(const char* directory, const char* fileExtensionOrDirectory, int* numFiles) +{ + char path[PATH_MAX] = ""; + DIR *dir; + struct dirent *de; + int total = 0; + int i; + char** files = NULL; + int pass; + *numFiles = 0; + int dirLen = strlen(directory); + + dir = opendir(directory); + if (dir == NULL) { + ui_print("Couldn't open directory.\n"); + return NULL; + } + + int extension_length = 0; + if (fileExtensionOrDirectory != NULL) + extension_length = strlen(fileExtensionOrDirectory); + + int isCounting = 1; + i = 0; + for (pass = 0; pass < 2; pass++) { + while ((de=readdir(dir)) != NULL) { + // skip hidden files + if (de->d_name[0] == '.') + continue; + + // NULL means that we are gathering directories, so skip this + if (fileExtensionOrDirectory != NULL) + { + // make sure that we can have the desired extension (prevent seg fault) + if (strlen(de->d_name) < extension_length) + continue; + // compare the extension + if (strcmp(de->d_name + strlen(de->d_name) - extension_length, fileExtensionOrDirectory) != 0) + continue; + } + else + { + struct stat info; + char fullFileName[PATH_MAX]; + strcpy(fullFileName, directory); + strcat(fullFileName, de->d_name); + stat(fullFileName, &info); + // make sure it is a directory + if (!(S_ISDIR(info.st_mode))) + continue; + } + + if (pass == 0) + { + total++; + continue; + } + + files[i] = (char*) malloc(dirLen + strlen(de->d_name) + 2); + strcpy(files[i], directory); + strcat(files[i], de->d_name); + if (fileExtensionOrDirectory == NULL) + strcat(files[i], "/"); + i++; + } + if (pass == 1) + break; + if (total == 0) + break; + rewinddir(dir); + *numFiles = total; + files = (char**) malloc((total+1)*sizeof(char*)); + files[total]=NULL; + } + + if(closedir(dir) < 0) { + LOGE("Failed to close directory."); + } + + if (total==0) { + return NULL; + } + + // sort the result + if (files != NULL) { + for (i = 0; i < total; i++) { + int curMax = -1; + int j; + for (j = 0; j < total - i; j++) { + if (curMax == -1 || strcmp(files[curMax], files[j]) < 0) + curMax = j; + } + char* temp = files[curMax]; + files[curMax] = files[total - i - 1]; + files[total - i - 1] = temp; + } + } + + return files; +} + +// pass in NULL for fileExtensionOrDirectory and you will get a directory chooser +char* choose_file_menu(const char* directory, const char* fileExtensionOrDirectory, const char* headers[]) +{ + char path[PATH_MAX] = ""; + DIR *dir; + struct dirent *de; + int numFiles = 0; + int numDirs = 0; + int i; + char* return_value = NULL; + int dir_len = strlen(directory); + + char** files = gather_files(directory, fileExtensionOrDirectory, &numFiles); + char** dirs = NULL; + if (fileExtensionOrDirectory != NULL) + dirs = gather_files(directory, NULL, &numDirs); + int total = numDirs + numFiles; + if (total == 0) + { + ui_print("No files found.\n"); + } + else + { + char** list = (char**) malloc((total + 1) * sizeof(char*)); + list[total] = NULL; + + + for (i = 0 ; i < numDirs; i++) + { + list[i] = strdup(dirs[i] + dir_len); + } + + for (i = 0 ; i < numFiles; i++) + { + list[numDirs + i] = strdup(files[i] + dir_len); + } + + for (;;) + { + int chosen_item = get_menu_selection(headers, list, 0, 0); + if (chosen_item == GO_BACK) + break; + static char ret[PATH_MAX]; + if (chosen_item < numDirs) + { + char* subret = choose_file_menu(dirs[chosen_item], fileExtensionOrDirectory, headers); + if (subret != NULL) + { + strcpy(ret, subret); + return_value = ret; + break; + } + continue; + } + strcpy(ret, files[chosen_item - numDirs]); + return_value = ret; + break; + } + free_string_array(list); + } + + free_string_array(files); + free_string_array(dirs); + return return_value; +} + +void show_choose_zip_menu() +{ + if (ensure_path_mounted("/sdcard") != 0) { + LOGE ("Can't mount /sdcard\n"); + return; + } + + static char* headers[] = { "Choose a zip to apply", + "", + NULL + }; + + char* file = choose_file_menu("/sdcard/", ".zip", headers); + if (file == NULL) + return; + static char* confirm_install = "Confirm install?"; + static char confirm[PATH_MAX]; + sprintf(confirm, "Yes - Install %s", basename(file)); + if (confirm_selection(confirm_install, confirm)) + install_zip(file); +} + +void show_nandroid_restore_menu() +{ + if (ensure_path_mounted("/sdcard") != 0) { + LOGE ("Can't mount /sdcard\n"); + return; + } + + static char* headers[] = { "Choose an image to restore", + "", + NULL + }; + + char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, headers); + if (file == NULL) + return; + + if (confirm_selection("Confirm restore?", "Yes - Restore")) + nandroid_restore(file, 1, 1, 1, 1, 1); +} + +void show_mount_usb_storage_menu() +{ + char command[PATH_MAX]; + Volume *vol = volume_for_path("/sdcard"); + sprintf(command, "echo %s > /sys/devices/platform/usb_mass_storage/lun0/file", vol->device); + __system(command); + static char* headers[] = { "USB Mass Storage device", + "Leaving this menu unmount", + "your SD card from your PC.", + "", + NULL + }; + + static char* list[] = { "Unmount", NULL }; + + for (;;) + { + int chosen_item = get_menu_selection(headers, list, 0, 0); + if (chosen_item == GO_BACK || chosen_item == 0) + break; + } + + __system("echo '' > /sys/devices/platform/usb_mass_storage/lun0/file"); + __system("echo 0 > /sys/devices/platform/usb_mass_storage/lun0/enable"); +} + +int confirm_selection(const char* title, const char* confirm) +{ + struct stat info; + if (0 == stat("/sdcard/clockworkmod/.no_confirm", &info)) + return 1; + + char* confirm_headers[] = { title, " THIS CAN NOT BE UNDONE.", "", NULL }; + char* items[] = { "No", + "No", + "No", + "No", + "No", + "No", + "No", + confirm, //" Yes -- wipe partition", // [7 + "No", + "No", + "No", + NULL }; + + int chosen_item = get_menu_selection(confirm_headers, items, 0, 0); + return chosen_item == 7; +} + +int format_unknown_device(const char* path) +{ + // if this is SDEXT:, don't worry about it. + if (0 == strcmp(path, "/sd-ext")) + { + struct stat st; + Volume *vol = volume_for_path("/sd-ext"); + if (vol == NULL || 0 != stat(vol->device, &st)) + { + ui_print("No app2sd partition found. Skipping format of /sd-ext.\n"); + return 0; + } + } + + if (0 != ensure_path_mounted(path)) + { + ui_print("Error mounting %s!\n", path); + ui_print("Skipping format...\n"); + return 0; + } + + static char tmp[PATH_MAX]; + sprintf(tmp, "rm -rf %s/*", path); + __system(tmp); + sprintf(tmp, "rm -rf %s/.*", path); + __system(tmp); + + ensure_path_unmounted(path); + return 0; +} + +//#define MOUNTABLE_COUNT 5 +//#define DEVICE_COUNT 4 +//#define MMC_COUNT 2 + +void show_partition_menu() +{ + static char* headers[] = { "Mounts and Storage Menu", + "", + NULL + }; + + typedef char* string; + string mounts[][3] = { + { "mount /system", "unmount /system", "/system" }, + { "mount /data", "unmount /data", "/data" }, + { "mount /cache", "unmount /cache", "/cache" }, + { "mount /sdcard", "unmount /sdcard", "/sdcard:" }, +#ifdef BOARD_HAS_SDCARD_INTERNAL + { "mount /emmc", "unmount /emmc", "/emmc" }, +#endif + { "mount /sd-ext", "unmount /sd-ext", "/sd-ext" } + }; + + string devices[][2] = { + { "format boot", "boot:" }, + { "format system", "/system" }, + { "format data", "/data" }, + { "format cache", "/cache" }, + { "format sdcard", "/sdcard" }, + { "format sd-ext", "/sd-ext" }, +#ifdef BOARD_HAS_SDCARD_INTERNAL + { "format internal sdcard", "/emmc" } +#endif + }; + + const int MOUNTABLE_COUNT = sizeof(mounts) / sizeof(string) / 3; + const int DEVICE_COUNT = sizeof(devices) / sizeof(string) / 2; + + static char* confirm_format = "Confirm format?"; + static char* confirm = "Yes - Format"; + + for (;;) + { + int ismounted[MOUNTABLE_COUNT]; + int i; + static string options[MOUNTABLE_COUNT + DEVICE_COUNT + 1 + 1]; // mountables, format mtds, format mmcs, usb storage, null + for (i = 0; i < MOUNTABLE_COUNT; i++) + { + ismounted[i] = is_path_mounted(mounts[i][2]); + options[i] = ismounted[i] ? mounts[i][1] : mounts[i][0]; + } + + for (i = 0; i < DEVICE_COUNT; i++) + { + options[DEVICE_COUNT + i] = devices[i][0]; + } + + options[MOUNTABLE_COUNT + DEVICE_COUNT] = "mount USB storage"; + options[MOUNTABLE_COUNT + DEVICE_COUNT + 1] = NULL; + + int chosen_item = get_menu_selection(headers, options, 0, 0); + if (chosen_item == GO_BACK) + break; + if (chosen_item == MOUNTABLE_COUNT + DEVICE_COUNT) + { + show_mount_usb_storage_menu(); + } + else if (chosen_item < MOUNTABLE_COUNT) + { + if (ismounted[chosen_item]) + { + if (0 != ensure_path_unmounted(mounts[chosen_item][2])) + ui_print("Error unmounting %s!\n", mounts[chosen_item][2]); + } + else + { + if (0 != ensure_path_mounted(mounts[chosen_item][2])) + ui_print("Error mounting %s!\n", mounts[chosen_item][2]); + } + } + else if (chosen_item < MOUNTABLE_COUNT + DEVICE_COUNT) + { + chosen_item = chosen_item - MOUNTABLE_COUNT; + if (!confirm_selection(confirm_format, confirm)) + continue; + ui_print("Formatting %s...\n", devices[chosen_item][1]); + if (0 != format_device(devices[chosen_item][1])) + ui_print("Error formatting %s!\n", devices[chosen_item][1]); + else + ui_print("Done.\n"); + } + } +} + +#define EXTENDEDCOMMAND_SCRIPT "/cache/recovery/extendedcommand" + +int extendedcommand_file_exists() +{ + struct stat file_info; + return 0 == stat(EXTENDEDCOMMAND_SCRIPT, &file_info); +} + +int run_script_from_buffer(char* script_data, int script_len, char* filename) +{ + ui_print("not yet implemented.\n"); + return -1; + + /* + const AmCommandList *commands = parseAmendScript(script_data, script_len); + if (commands == NULL) { + printf("Syntax error in update script\n"); + return 1; + } else { + printf("Parsed %.*s\n", script_len, filename); + } + + int ret = execCommandList((ExecContext *)1, commands); + if (ret != 0) { + int num = ret; + char *line = NULL, *next = script_data; + while (next != NULL && ret-- > 0) { + line = next; + next = memchr(line, '\n', script_data + script_len - line); + if (next != NULL) *next++ = '\0'; + } + printf("Failure at line %d:\n%s\n", num, next ? line : "(not found)"); + return 1; + } + */ +} + +int run_script(char* filename) +{ + struct stat file_info; + if (0 != stat(filename, &file_info)) { + printf("Error executing stat on file: %s\n", filename); + return 1; + } + + int script_len = file_info.st_size; + char* script_data = (char*)malloc(script_len + 1); + FILE *file = fopen(filename, "rb"); + fread(script_data, script_len, 1, file); + // supposedly not necessary, but let's be safe. + script_data[script_len] = '\0'; + fclose(file); + LOGI("Running script:\n"); + LOGI("\n%s\n", script_data); + + int ret = run_script_from_buffer(script_data, script_len, filename); + free(script_data); + return ret; +} + +int run_and_remove_extendedcommand() +{ + char tmp[PATH_MAX]; + sprintf(tmp, "cp %s /tmp/%s", EXTENDEDCOMMAND_SCRIPT, basename(EXTENDEDCOMMAND_SCRIPT)); + __system(tmp); + remove(EXTENDEDCOMMAND_SCRIPT); + int i = 0; + for (i = 20; i > 0; i--) { + ui_print("Waiting for SD Card to mount (%ds)\n", i); + if (ensure_path_mounted("/sdcard") == 0) { + ui_print("SD Card mounted...\n"); + break; + } + sleep(1); + } + remove("/sdcard/clockworkmod/.recoverycheckpoint"); + if (i == 0) { + ui_print("Timed out waiting for SD card... continuing anyways."); + } + + sprintf(tmp, "/tmp/%s", basename(EXTENDEDCOMMAND_SCRIPT)); + return run_script(tmp); +} + +void show_nandroid_advanced_restore_menu() +{ + if (ensure_path_mounted("/sdcard") != 0) { + LOGE ("Can't mount /sdcard\n"); + return; + } + + static char* advancedheaders[] = { "Choose an image to restore", + "", + "Choose an image to restore", + "first. The next menu will", + "you more options.", + "", + NULL + }; + + char* file = choose_file_menu("/sdcard/clockworkmod/backup/", NULL, advancedheaders); + if (file == NULL) + return; + + static char* headers[] = { "Nandroid Advanced Restore", + "", + NULL + }; + + static char* list[] = { "Restore boot", + "Restore system", + "Restore data", + "Restore cache", + "Restore sd-ext", + NULL + }; + + + static char* confirm_restore = "Confirm restore?"; + + int chosen_item = get_menu_selection(headers, list, 0, 0); + switch (chosen_item) + { + case 0: + if (confirm_selection(confirm_restore, "Yes - Restore boot")) + nandroid_restore(file, 1, 0, 0, 0, 0); + break; + case 1: + if (confirm_selection(confirm_restore, "Yes - Restore system")) + nandroid_restore(file, 0, 1, 0, 0, 0); + break; + case 2: + if (confirm_selection(confirm_restore, "Yes - Restore data")) + nandroid_restore(file, 0, 0, 1, 0, 0); + break; + case 3: + if (confirm_selection(confirm_restore, "Yes - Restore cache")) + nandroid_restore(file, 0, 0, 0, 1, 0); + break; + case 4: + if (confirm_selection(confirm_restore, "Yes - Restore sd-ext")) + nandroid_restore(file, 0, 0, 0, 0, 1); + break; + } +} + +void show_nandroid_menu() +{ + static char* headers[] = { "Nandroid", + "", + NULL + }; + + static char* list[] = { "Backup", + "Restore", + "Advanced Restore", + NULL + }; + + int chosen_item = get_menu_selection(headers, list, 0, 0); + switch (chosen_item) + { + case 0: + { + char backup_path[PATH_MAX]; + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + if (tmp == NULL) + { + struct timeval tp; + gettimeofday(&tp, NULL); + sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec); + } + else + { + strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp); + } + nandroid_backup(backup_path); + } + break; + case 1: + show_nandroid_restore_menu(); + break; + case 2: + show_nandroid_advanced_restore_menu(); + break; + } +} + +void wipe_battery_stats() +{ + ensure_path_mounted("/data"); + remove("/data/system/batterystats.bin"); + ensure_path_unmounted("/data"); +} + +void show_advanced_menu() +{ + static char* headers[] = { "Advanced and Debugging Menu", + "", + NULL + }; + + static char* list[] = { "Reboot Recovery", + "Wipe Dalvik Cache", + "Wipe Battery Stats", + "Report Error", + "Key Test", +#ifndef BOARD_HAS_SMALL_RECOVERY + "Partition SD Card", + "Fix Permissions", +#ifdef BOARD_HAS_SDCARD_INTERNAL + "Partition Internal SD Card", +#endif +#endif + NULL + }; + + for (;;) + { + int chosen_item = get_menu_selection(headers, list, 0, 0); + if (chosen_item == GO_BACK) + break; + switch (chosen_item) + { + case 0: + __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery"); + break; + case 1: + { + if (0 != ensure_path_mounted("/data")) + break; + ensure_path_mounted("/sd-ext"); + ensure_path_mounted("/cache"); + if (confirm_selection( "Confirm wipe?", "Yes - Wipe Dalvik Cache")) { + __system("rm -r /data/dalvik-cache"); + __system("rm -r /cache/dalvik-cache"); + __system("rm -r /sd-ext/dalvik-cache"); + } + ensure_path_unmounted("/data"); + ui_print("Dalvik Cache wiped.\n"); + break; + } + case 2: + { + if (confirm_selection( "Confirm wipe?", "Yes - Wipe Battery Stats")) + wipe_battery_stats(); + break; + } + case 3: + handle_failure(1); + break; + case 4: + { + ui_print("Outputting key codes.\n"); + ui_print("Go back to end debugging.\n"); + int key; + int action; + do + { + key = ui_wait_key(); + action = device_handle_key(key, 1); + ui_print("Key: %d\n", key); + } + while (action != GO_BACK); + break; + } + case 5: + { + static char* ext_sizes[] = { "128M", + "256M", + "512M", + "1024M", + "2048M", + "4096M", + NULL }; + + static char* swap_sizes[] = { "0M", + "32M", + "64M", + "128M", + "256M", + NULL }; + + static char* ext_headers[] = { "Ext Size", "", NULL }; + static char* swap_headers[] = { "Swap Size", "", NULL }; + + int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0); + if (ext_size == GO_BACK) + continue; + + int swap_size = get_menu_selection(swap_headers, swap_sizes, 0, 0); + if (swap_size == GO_BACK) + continue; + + char sddevice[256]; + Volume *vol = volume_for_path("/sdcard"); + strcpy(sddevice, vol->device); + // we only want the mmcblk, not the partition + sddevice[strlen("/dev/block/mmcblkX")] = NULL; + char cmd[PATH_MAX]; + setenv("SDPATH", sddevice, 1); + sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]); + ui_print("Partitioning SD Card... please wait...\n"); + if (0 == __system(cmd)) + ui_print("Done!\n"); + else + ui_print("An error occured while partitioning your SD Card. Please see /tmp/recovery.log for more details.\n"); + break; + } + case 6: + { + ensure_path_mounted("/system"); + ensure_path_mounted("/data"); + ui_print("Fixing permissions...\n"); + __system("fix_permissions"); + ui_print("Done!\n"); + break; + } + case 7: + { + static char* ext_sizes[] = { "128M", + "256M", + "512M", + "1024M", + "2048M", + "4096M", + NULL }; + + static char* swap_sizes[] = { "0M", + "32M", + "64M", + "128M", + "256M", + NULL }; + + static char* ext_headers[] = { "Data Size", "", NULL }; + static char* swap_headers[] = { "Swap Size", "", NULL }; + + int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0); + if (ext_size == GO_BACK) + continue; + + int swap_size = 0; + if (swap_size == GO_BACK) + continue; + + char sddevice[256]; + Volume *vol = volume_for_path("/emmc"); + strcpy(sddevice, vol->device); + // we only want the mmcblk, not the partition + sddevice[strlen("/dev/block/mmcblkX")] = NULL; + char cmd[PATH_MAX]; + setenv("SDPATH", sddevice, 1); + sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]); + ui_print("Partitioning Internal SD Card... please wait...\n"); + if (0 == __system(cmd)) + ui_print("Done!\n"); + else + ui_print("An error occured while partitioning your Internal SD Card. Please see /tmp/recovery.log for more details.\n"); + break; + } + } + } +} + +/* +void write_fstab_root(char *root_path, FILE *file) +{ + RootInfo *info = get_root_info_for_path(root_path); + if (info == NULL) { + LOGW("Unable to get root info for %s during fstab generation!", root_path); + return; + } + char device[PATH_MAX]; + int ret = get_root_partition_device(root_path, device); + if (ret == 0) + { + fprintf(file, "%s ", device); + } + else + { + fprintf(file, "%s ", info->device); + } + + fprintf(file, "%s ", info->mount_point); + fprintf(file, "%s %s\n", info->filesystem, info->filesystem_options == NULL ? "rw" : info->filesystem_options); +} + +void create_fstab() +{ + __system("touch /etc/mtab"); + FILE *file = fopen("/etc/fstab", "w"); + if (file == NULL) { + LOGW("Unable to create /etc/fstab!"); + return; + } + write_fstab_root("CACHE:", file); + write_fstab_root("DATA:", file); +#ifdef BOARD_HAS_DATADATA + write_fstab_root("DATADATA:", file); +#endif + write_fstab_root("SYSTEM:", file); + write_fstab_root("SDCARD:", file); + write_fstab_root("SDEXT:", file); + fclose(file); +} +*/ + +void handle_failure(int ret) +{ + if (ret == 0) + return; + if (0 != ensure_path_mounted("/sdcard")) + return; + mkdir("/sdcard/clockworkmod", S_IRWXU); + __system("cp /tmp/recovery.log /sdcard/clockworkmod/recovery.log"); + ui_print("/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.log. Please open ROM Manager to report the issue.\n"); +} + +int format_device(const char* device) { + if (device == NULL) + return -1; + if (device[0] == '/') { + Volume *vol = volume_for_path(device); + if (vol == NULL) + return -1; + return erase_partition(device, vol->fs_type); + } + return erase_raw_partition(device); +} + +int is_path_mounted(const char* path) { + Volume* v = volume_for_path(path); + if (v == NULL) { + return 0; + } + if (strcmp(v->fs_type, "ramdisk") == 0) { + // the ramdisk is always mounted. + return 1; + } + + int result; + result = scan_mounted_volumes(); + if (result < 0) { + LOGE("failed to scan mounted volumes\n"); + return 0; + } + + const MountedVolume* mv = + find_mounted_volume_by_mount_point(v->mount_point); + if (mv) { + // volume is already mounted + return 1; + } + return 0; +} diff --git a/extendedcommands.h b/extendedcommands.h new file mode 100644 index 0000000..650a519 --- /dev/null +++ b/extendedcommands.h @@ -0,0 +1,46 @@ +extern int signature_check_enabled; +extern int script_assert_enabled; + +void +toggle_signature_check(); + +void +toggle_script_asserts(); + +void +show_choose_zip_menu(); + +int +do_nandroid_backup(const char* backup_name); + +int +do_nandroid_restore(); + +void +show_nandroid_restore_menu(); + +void +show_nandroid_menu(); + +void +show_partition_menu(); + +void +show_choose_zip_menu(); + +int +install_zip(const char* packagefilepath); + +int +__system(const char *command); + +void +show_advanced_menu(); + +int +format_unknown_device(const char* root); + +void +wipe_battery_stats(); + +void create_fstab(); diff --git a/firmware.h b/firmware.h new file mode 100644 index 0000000..aeb8f97 --- /dev/null +++ b/firmware.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _RECOVERY_FIRMWARE_H +#define _RECOVERY_FIRMWARE_H + +/* Save a radio or bootloader update image for later installation. + * The type should be one of "hboot" or "radio". + * Takes ownership of type and data. Returns nonzero on error. + */ +int remember_firmware_update(const char *type, const char *data, int length); + +/* Returns true if a firmware update has been saved. */ +int firmware_update_pending(); + +/* If an update was saved, reboot into the bootloader now to install it. + * Returns 0 if no radio image was defined, nonzero on error, + * doesn't return at all on success... + */ +int maybe_install_firmware_update(const char *send_intent); + +#endif diff --git a/flashutils/Android.mk b/flashutils/Android.mk new file mode 100644 index 0000000..324480a --- /dev/null +++ b/flashutils/Android.mk @@ -0,0 +1,98 @@ +LOCAL_PATH := $(call my-dir) + +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_ARCH),arm) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := flashutils.c +LOCAL_MODULE := libflashutils +LOCAL_MODULE_TAGS := eng +LOCAL_C_INCLUDES += bootable/recovery +LOCAL_STATIC_LIBRARIES := libmmcutils libmtdutils libbmlutils +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := flash_image.c +LOCAL_MODULE := flash_image +LOCAL_MODULE_TAGS := eng +#LOCAL_STATIC_LIBRARIES += $(BOARD_FLASH_LIBRARY) +LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils +LOCAL_SHARED_LIBRARIES := libcutils libc +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c +LOCAL_MODULE := dump_image +LOCAL_MODULE_TAGS := eng +LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils +LOCAL_SHARED_LIBRARIES := libcutils libc +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := erase_image.c +LOCAL_MODULE := erase_image +LOCAL_MODULE_TAGS := eng +LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils +LOCAL_SHARED_LIBRARIES := libcutils libc +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := flash_image.c +LOCAL_MODULE := libflash_image +LOCAL_MODULE_TAGS := eng +LOCAL_CFLAGS += -Dmain=flash_image_main +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c +LOCAL_MODULE := libdump_image +LOCAL_MODULE_TAGS := eng +LOCAL_CFLAGS += -Dmain=dump_image_main +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := erase_image.c +LOCAL_MODULE := liberase_image +LOCAL_MODULE_TAGS := eng +LOCAL_CFLAGS += -Dmain=erase_image_main +include $(BUILD_STATIC_LIBRARY) + + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c +LOCAL_MODULE := utility_dump_image +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities +LOCAL_MODULE_STEM := dump_image +LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := flash_image.c +LOCAL_MODULE := utility_flash_image +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities +LOCAL_MODULE_STEM := flash_image +LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := erase_image.c +LOCAL_MODULE := utility_erase_image +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities +LOCAL_MODULE_STEM := erase_image +LOCAL_STATIC_LIBRARIES := libflashutils libmtdutils libmmcutils libbmlutils libcutils libc +LOCAL_FORCE_STATIC_EXECUTABLE := true +include $(BUILD_EXECUTABLE) + +endif # TARGET_ARCH == arm +endif # !TARGET_SIMULATOR diff --git a/flashutils/dump_image.c b/flashutils/dump_image.c new file mode 100644 index 0000000..4db1f74 --- /dev/null +++ b/flashutils/dump_image.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cutils/log.h" +#include "flashutils.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#if 0 + +#define LOG_TAG "dump_image" + +#define BLOCK_SIZE 2048 +#define SPARE_SIZE (BLOCK_SIZE >> 5) + +static int die(const char *msg, ...) { + int err = errno; + va_list args; + va_start(args, msg); + char buf[1024]; + vsnprintf(buf, sizeof(buf), msg, args); + va_end(args); + + if (err != 0) { + strlcat(buf, ": ", sizeof(buf)); + strlcat(buf, strerror(err), sizeof(buf)); + } + + fprintf(stderr, "%s\n", buf); + return 1; +} + +/* Read a flash partition and write it to an image file. */ + +int dump_image(char* partition_name, char* filename, dump_image_callback callback) { + MtdReadContext *in; + const MtdPartition *partition; + char buf[BLOCK_SIZE + SPARE_SIZE]; + size_t partition_size; + size_t read_size; + size_t total; + int fd; + int wrote; + int len; + + if (mtd_scan_partitions() <= 0) + return die("error scanning partitions"); + + partition = mtd_find_partition_by_name(partition_name); + if (partition == NULL) + return die("can't find %s partition", partition_name); + + if (mtd_partition_info(partition, &partition_size, NULL, NULL)) { + return die("can't get info of partition %s", partition_name); + } + + if (!strcmp(filename, "-")) { + fd = fileno(stdout); + } + else { + fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666); + } + + if (fd < 0) + return die("error opening %s", filename); + + in = mtd_read_partition(partition); + if (in == NULL) { + close(fd); + unlink(filename); + return die("error opening %s: %s\n", partition_name, strerror(errno)); + } + + total = 0; + while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) { + wrote = write(fd, buf, len); + if (wrote != len) { + close(fd); + unlink(filename); + return die("error writing %s", filename); + } + total += BLOCK_SIZE; + if (callback != NULL) + callback(total, partition_size); + } + + mtd_read_close(in); + + if (close(fd)) { + unlink(filename); + return die("error closing %s", filename); + } + return 0; +} + +int main(int argc, char **argv) +{ + ssize_t (*read_func) (MtdReadContext *, char *, size_t); + MtdReadContext *in; + const MtdPartition *partition; + char buf[BLOCK_SIZE + SPARE_SIZE]; + size_t partition_size; + size_t read_size; + size_t total; + int fd; + int wrote; + int len; + + if (argc != 3) { + fprintf(stderr, "usage: %s partition file.img\n", argv[0]); + return 2; + } + + return dump_image(argv[1], argv[2], NULL); +} + +#endif + +int main(int argc, char **argv) +{ + if (argc != 3) { + fprintf(stderr, "usage: %s partition file.img\n", argv[0]); + return 2; + } + + return backup_raw_partition(argv[1], argv[2]); +} diff --git a/flashutils/erase_image.c b/flashutils/erase_image.c new file mode 100644 index 0000000..c495255 --- /dev/null +++ b/flashutils/erase_image.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * Portions Copyright (C) 2010 Magnus Eriksson + * + * 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 +#include +#include +#include +#include +#include + +#include + +#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 \n", argv[0]); + return 2; + } + + return erase_image(argv[1]); +} + +#endif + + +int main(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "usage: %s partition\n", argv[0]); + return 2; + } + + return erase_raw_partition(argv[1]); +} diff --git a/mtdutils/flash_image.c b/flashutils/flash_image.c similarity index 93% rename from mtdutils/flash_image.c rename to flashutils/flash_image.c index c776876..3966c42 100644 --- a/mtdutils/flash_image.c +++ b/flashutils/flash_image.c @@ -22,8 +22,8 @@ #include #include "cutils/log.h" -#include "mtdutils.h" +#if 0 #define LOG_TAG "flash_image" #define HEADER_SIZE 2048 // size of header to compare for equality @@ -138,3 +138,17 @@ int main(int argc, char **argv) { if (mtd_write_close(out)) die("error closing %s", argv[1]); return 0; } +#endif + +int main(int argc, char **argv) +{ + if (argc != 3) { + fprintf(stderr, "usage: %s partition file.img\n", argv[0]); + return 2; + } + + int ret = restore_raw_partition(argv[1], argv[2]); + if (ret != 0) + fprintf(stderr, "failed with error: %d\n", ret); + return ret; +} diff --git a/flashutils/flashutils.c b/flashutils/flashutils.c new file mode 100644 index 0000000..b71d4fa --- /dev/null +++ b/flashutils/flashutils.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include + +#include "flashutils/flashutils.h" + +int the_flash_type = UNKNOWN; + +int device_flash_type() +{ + if (the_flash_type == UNKNOWN) { + if (access("/dev/block/bml1", F_OK) == 0) { + the_flash_type = BML; + } else if (access("/proc/emmc", F_OK) == 0) { + the_flash_type = MMC; + } else if (access("/proc/mtd", F_OK) == 0) { + the_flash_type = MTD; + } else { + the_flash_type = UNSUPPORTED; + } + } + return the_flash_type; +} + +char* get_default_filesystem() +{ + return device_flash_type() == MMC ? "ext3" : "yaffs2"; +} + +// This was pulled from bionic: The default system command always looks +// for shell in /system/bin/sh. This is bad. +#define _PATH_BSHELL "/sbin/sh" + +extern char **environ; +int +__system(const char *command) +{ + pid_t pid; + sig_t intsave, quitsave; + sigset_t mask, omask; + int pstat; + char *argp[] = {"sh", "-c", NULL, NULL}; + + if (!command) /* just checking... */ + return(1); + + argp[2] = (char *)command; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &omask); + switch (pid = vfork()) { + case -1: /* error */ + sigprocmask(SIG_SETMASK, &omask, NULL); + return(-1); + case 0: /* child */ + sigprocmask(SIG_SETMASK, &omask, NULL); + execve(_PATH_BSHELL, argp, environ); + _exit(127); + } + + intsave = (sig_t) bsd_signal(SIGINT, SIG_IGN); + quitsave = (sig_t) bsd_signal(SIGQUIT, SIG_IGN); + pid = waitpid(pid, (int *)&pstat, 0); + sigprocmask(SIG_SETMASK, &omask, NULL); + (void)bsd_signal(SIGINT, intsave); + (void)bsd_signal(SIGQUIT, quitsave); + return (pid == -1 ? -1 : pstat); +} + +int restore_raw_partition(const char *partition, const char *filename) +{ + int type = device_flash_type(); + switch (type) { + case MTD: + return cmd_mtd_restore_raw_partition(partition, filename); + case MMC: + return cmd_mmc_restore_raw_partition(partition, filename); + case BML: + return cmd_bml_restore_raw_partition(partition, filename); + default: + return -1; + } +} + +int backup_raw_partition(const char *partition, const char *filename) +{ + int type = device_flash_type(); + switch (type) { + case MTD: + return cmd_mtd_backup_raw_partition(partition, filename); + case MMC: + return cmd_mmc_backup_raw_partition(partition, filename); + case BML: + return cmd_bml_backup_raw_partition(partition, filename); + default: + return -1; + } +} + +int erase_raw_partition(const char *partition) +{ + int type = device_flash_type(); + switch (type) { + case MTD: + return cmd_mtd_erase_raw_partition(partition); + case MMC: + return cmd_mmc_erase_raw_partition(partition); + case BML: + return cmd_bml_erase_raw_partition(partition); + default: + return -1; + } +} + +int erase_partition(const char *partition, const char *filesystem) +{ + int type = device_flash_type(); + switch (type) { + case MTD: + return cmd_mtd_erase_partition(partition, filesystem); + case MMC: + return cmd_mmc_erase_partition(partition, filesystem); + case BML: + return cmd_bml_erase_partition(partition, filesystem); + default: + return -1; + } +} + +int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only) +{ + int type = device_flash_type(); + switch (type) { + case MTD: + return cmd_mtd_mount_partition(partition, mount_point, filesystem, read_only); + case MMC: + return cmd_mmc_mount_partition(partition, mount_point, filesystem, read_only); + case BML: + return cmd_bml_mount_partition(partition, mount_point, filesystem, read_only); + default: + return -1; + } +} + +int get_partition_device(const char *partition, char *device) +{ + int type = device_flash_type(); + switch (type) { + case MTD: + return cmd_mtd_get_partition_device(partition, device); + case MMC: + return cmd_mmc_get_partition_device(partition, device); + case BML: + return cmd_bml_get_partition_device(partition, device); + default: + return -1; + } +} diff --git a/flashutils/flashutils.h b/flashutils/flashutils.h new file mode 100644 index 0000000..d5dadcb --- /dev/null +++ b/flashutils/flashutils.h @@ -0,0 +1,51 @@ +#ifndef FLASHUTILS_H +#define FLASHUTILS_H + +int restore_raw_partition(const char *partition, const char *filename); +int backup_raw_partition(const char *partition, const char *filename); +int erase_raw_partition(const char *partition); +int erase_partition(const char *partition, const char *filesystem); +int mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only); +int get_partition_device(const char *partition, char *device); + +#define FLASH_MTD 0 +#define FLASH_MMC 1 +#define FLASH_BML 2 + +int is_mtd_device(); +char* get_default_filesystem(); + +int __system(const char *command); + +extern int cmd_mtd_restore_raw_partition(const char *partition, const char *filename); +extern int cmd_mtd_backup_raw_partition(const char *partition, const char *filename); +extern int cmd_mtd_erase_raw_partition(const char *partition); +extern int cmd_mtd_erase_partition(const char *partition, const char *filesystem); +extern int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only); +extern int cmd_mtd_get_partition_device(const char *partition, char *device); + +extern int cmd_mmc_restore_raw_partition(const char *partition, const char *filename); +extern int cmd_mmc_backup_raw_partition(const char *partition, const char *filename); +extern int cmd_mmc_erase_raw_partition(const char *partition); +extern int cmd_mmc_erase_partition(const char *partition, const char *filesystem); +extern int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only); +extern int cmd_mmc_get_partition_device(const char *partition, char *device); + +extern int cmd_bml_restore_raw_partition(const char *partition, const char *filename); +extern int cmd_bml_backup_raw_partition(const char *partition, const char *filename); +extern int cmd_bml_erase_raw_partition(const char *partition); +extern int cmd_bml_erase_partition(const char *partition, const char *filesystem); +extern int cmd_bml_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only); +extern int cmd_bml_get_partition_device(const char *partition, char *device); + +extern int device_flash_type(); + +enum flash_type { + UNSUPPORTED = -1, + UNKNOWN = 0, + MTD = 1, + MMC = 2, + BML = 3 +}; + +#endif \ No newline at end of file diff --git a/install.c b/install.c index 5bb3a78..2d8a4cd 100644 --- a/install.c +++ b/install.c @@ -28,14 +28,74 @@ #include "minui/minui.h" #include "minzip/SysUtil.h" #include "minzip/Zip.h" -#include "mtdutils/mounts.h" +#include "mounts.h" #include "mtdutils/mtdutils.h" #include "roots.h" #include "verifier.h" +#include "firmware.h" + +#include "extendedcommands.h" + + #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" +// The update binary ask us to install a firmware file on reboot. Set +// that up. Takes ownership of type and filename. +static int +handle_firmware_update(char* type, char* filename, ZipArchive* zip) { + unsigned int data_size; + const ZipEntry* entry = NULL; + + if (strncmp(filename, "PACKAGE:", 8) == 0) { + entry = mzFindZipEntry(zip, filename+8); + if (entry == NULL) { + LOGE("Failed to find \"%s\" in package", filename+8); + return INSTALL_ERROR; + } + data_size = entry->uncompLen; + } else { + struct stat st_data; + if (stat(filename, &st_data) < 0) { + LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + data_size = st_data.st_size; + } + + LOGI("type is %s; size is %d; file is %s\n", + type, data_size, filename); + + char* data = malloc(data_size); + if (data == NULL) { + LOGI("Can't allocate %d bytes for firmware data\n", data_size); + return INSTALL_ERROR; + } + + if (entry) { + if (mzReadZipEntry(zip, entry, data, data_size) == false) { + LOGE("Failed to read \"%s\" from package", filename+8); + return INSTALL_ERROR; + } + } else { + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + LOGE("Failed to open %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + if (fread(data, 1, data_size, f) != data_size) { + LOGE("Failed to read firmware data: %s\n", strerror(errno)); + return INSTALL_ERROR; + } + fclose(f); + } + + free(filename); + + return INSTALL_SUCCESS; +} + // If the package contains an update binary, extract it and run it. static int try_update_binary(const char *path, ZipArchive *zip) { @@ -43,7 +103,7 @@ try_update_binary(const char *path, ZipArchive *zip) { mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); if (binary_entry == NULL) { mzCloseZipArchive(zip); - return INSTALL_CORRUPT; + return INSTALL_UPDATE_BINARY_MISSING; } char* binary = "/tmp/update_binary"; @@ -117,6 +177,9 @@ try_update_binary(const char *path, ZipArchive *zip) { } close(pipefd[1]); + char* firmware_type = NULL; + char* firmware_filename = NULL; + char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r"); while (fgets(buffer, sizeof(buffer), from_child) != NULL) { @@ -136,6 +199,18 @@ try_update_binary(const char *path, ZipArchive *zip) { char* fraction_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); ui_set_progress(fraction); + } else if (strcmp(command, "firmware") == 0) { + char* type = strtok(NULL, " \n"); + char* filename = strtok(NULL, " \n"); + + if (type != NULL && filename != NULL) { + if (firmware_type != NULL) { + LOGE("ignoring attempt to do multiple firmware updates"); + } else { + firmware_type = strdup(type); + firmware_filename = strdup(filename); + } + } } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { @@ -156,6 +231,11 @@ try_update_binary(const char *path, ZipArchive *zip) { return INSTALL_ERROR; } + if (firmware_type != NULL) { + return handle_firmware_update(firmware_type, firmware_filename, zip); + } else { + return INSTALL_SUCCESS; + } return INSTALL_SUCCESS; } @@ -248,27 +328,30 @@ install_package(const char *path) ui_print("Opening update package...\n"); - int numKeys; - RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); - if (loadedKeys == NULL) { - LOGE("Failed to load keys\n"); - return INSTALL_CORRUPT; - } - LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); - - // Give verification half the progress bar... - ui_print("Verifying update package...\n"); - ui_show_progress( - VERIFICATION_PROGRESS_FRACTION, - VERIFICATION_PROGRESS_TIME); - int err; - err = verify_file(path, loadedKeys, numKeys); - free(loadedKeys); - LOGI("verify_file returned %d\n", err); - if (err != VERIFY_SUCCESS) { - LOGE("signature verification failed\n"); - return INSTALL_CORRUPT; + + if (signature_check_enabled) { + int numKeys; + RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); + if (loadedKeys == NULL) { + LOGE("Failed to load keys\n"); + return INSTALL_CORRUPT; + } + LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); + + // Give verification half the progress bar... + ui_print("Verifying update package...\n"); + ui_show_progress( + VERIFICATION_PROGRESS_FRACTION, + VERIFICATION_PROGRESS_TIME); + + err = verify_file(path, loadedKeys, numKeys); + free(loadedKeys); + LOGI("verify_file returned %d\n", err); + if (err != VERIFY_SUCCESS) { + LOGE("signature verification failed\n"); + return INSTALL_CORRUPT; + } } /* Try to open the package. diff --git a/install.h b/install.h index a7ebc09..ec97d39 100644 --- a/install.h +++ b/install.h @@ -19,7 +19,7 @@ #include "common.h" -enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT }; +enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_UPDATE_SCRIPT_MISSING, INSTALL_UPDATE_BINARY_MISSING }; int install_package(const char *root_path); #endif // RECOVERY_INSTALL_H_ diff --git a/killrecovery.sh b/killrecovery.sh new file mode 100755 index 0000000..352cb4b --- /dev/null +++ b/killrecovery.sh @@ -0,0 +1,22 @@ +#!/sbin/sh +mkdir -p /sd-ext +rm /cache/recovery/command +rm /cache/update.zip +touch /tmp/.ignorebootmessage +kill $(ps | grep /sbin/adbd) +kill $(ps | grep /sbin/recovery) + +# On the Galaxy S, the recovery comes test signed, but the +# recovery is not automatically restarted. +if [ -f /init.smdkc110.rc ] +then + /sbin/recovery & +fi + +# Droid X +if [ -f /init.mapphone_cdma.rc ] +then + /sbin/recovery & +fi + +exit 1 diff --git a/minui/Android.mk b/minui/Android.mk index 91dd939..10f0f3f 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -7,6 +7,10 @@ LOCAL_C_INCLUDES +=\ external/libpng\ external/zlib +ifneq ($(BOARD_LDPI_RECOVERY),) + LOCAL_CFLAGS += -DBOARD_LDPI_RECOVERY='"$(BOARD_LDPI_RECOVERY)"' +endif + LOCAL_MODULE := libminui include $(BUILD_STATIC_LIBRARY) diff --git a/minui/events.c b/minui/events.c index 3aed2a8..efdca95 100644 --- a/minui/events.c +++ b/minui/events.c @@ -19,16 +19,166 @@ #include #include #include +#include #include +#include "../common.h" + #include "minui.h" #define MAX_DEVICES 16 +#define VIBRATOR_TIMEOUT_FILE "/sys/class/timed_output/vibrator/enable" +#define VIBRATOR_TIME_MS 50 + +#define PRESS_THRESHHOLD 10 + +#define ABS_MT_POSITION_X 0x35 +#define ABS_MT_POSITION_Y 0x36 +#define ABS_MT_TOUCH_MAJOR 0x30 +#define SYN_MT_REPORT 2 + +struct virtualkey { + int scancode; + int centerx, centery; + int width, height; +}; + +struct position { + int x, y; + int pressed; + struct input_absinfo xi, yi; +}; + +struct ev { + struct pollfd *fd; + + struct virtualkey *vks; + int vk_count; + + struct position p, mt_p; + int sent, mt_idx; +}; + static struct pollfd ev_fds[MAX_DEVICES]; +static struct ev evs[MAX_DEVICES]; static unsigned ev_count = 0; +static inline int ABS(int x) { + return x<0?-x:x; +} + +int vibrate(int timeout_ms) +{ + char str[20]; + int fd; + int ret; + + fd = open(VIBRATOR_TIMEOUT_FILE, O_WRONLY); + if (fd < 0) + return -1; + + ret = snprintf(str, sizeof(str), "%d", timeout_ms); + ret = write(fd, str, ret); + close(fd); + + if (ret < 0) + return -1; + + return 0; +} + +/* Returns empty tokens */ +static char *vk_strtok_r(char *str, const char *delim, char **save_str) +{ + if(!str) { + if(!*save_str) return NULL; + str = (*save_str) + 1; + } + *save_str = strpbrk(str, delim); + if(*save_str) **save_str = '\0'; + return str; +} + +static int vk_init(struct ev *e) +{ + char vk_path[PATH_MAX] = "/sys/board_properties/virtualkeys."; + char vks[2048], *ts; + ssize_t len; + int vk_fd; + int i; + + e->vk_count = 0; + + len = strlen(vk_path); + len = ioctl(e->fd->fd, EVIOCGNAME(sizeof(vk_path) - len), vk_path + len); + if (len <= 0) + return -1; + + vk_fd = open(vk_path, O_RDONLY); + if (vk_fd < 0) + return -1; + + len = read(vk_fd, vks, sizeof(vks)-1); + close(vk_fd); + if (len <= 0) + return -1; + + vks[len] = '\0'; + + /* Parse a line like: + keytype:keycode:centerx:centery:width:height:keytype2:keycode2:centerx2:... + */ + for (ts = vks, e->vk_count = 1; *ts; ++ts) { + if (*ts == ':') + ++e->vk_count; + } + + if (e->vk_count % 6) { + LOGW("minui: %s is %d %% 6\n", vk_path, e->vk_count % 6); + } + e->vk_count /= 6; + if (e->vk_count <= 0) + return -1; + + e->sent = 0; + e->mt_idx = 0; + + ioctl(e->fd->fd, EVIOCGABS(ABS_X), &e->p.xi); + ioctl(e->fd->fd, EVIOCGABS(ABS_Y), &e->p.yi); + e->p.pressed = 0; + + ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_X), &e->mt_p.xi); + ioctl(e->fd->fd, EVIOCGABS(ABS_MT_POSITION_Y), &e->mt_p.yi); + e->mt_p.pressed = 0; + + e->vks = malloc(sizeof(*e->vks) * e->vk_count); + + for (i = 0; i < e->vk_count; ++i) { + char *token[6]; + int j; + + for (j = 0; j < 6; ++j) { + token[j] = vk_strtok_r((i||j)?NULL:vks, ":", &ts); + } + + if (strcmp(token[0], "0x01") != 0) { + /* Java does string compare, so we do too. */ + LOGW("minui: %s: ignoring unknown virtual key type %s\n", vk_path, token[0]); + continue; + } + + e->vks[i].scancode = strtol(token[1], NULL, 0); + e->vks[i].centerx = strtol(token[2], NULL, 0); + e->vks[i].centery = strtol(token[3], NULL, 0); + e->vks[i].width = strtol(token[4], NULL, 0); + e->vks[i].height = strtol(token[5], NULL, 0); + } + + return 0; +} + int ev_init(void) { DIR *dir; @@ -45,6 +195,11 @@ int ev_init(void) ev_fds[ev_count].fd = fd; ev_fds[ev_count].events = POLLIN; + evs[ev_count].fd = &ev_fds[ev_count]; + + /* Load virtualkeys if there are any */ + vk_init(&evs[ev_count]); + ev_count++; if(ev_count == MAX_DEVICES) break; } @@ -55,11 +210,135 @@ int ev_init(void) void ev_exit(void) { - while (ev_count > 0) { - close(ev_fds[--ev_count].fd); + while (ev_count-- > 0) { + if (evs[ev_count].vk_count) { + free(evs[ev_count].vks); + evs[ev_count].vk_count = 0; + } + close(ev_fds[ev_count].fd); } } +static int vk_inside_display(__s32 value, struct input_absinfo *info, int screen_size) +{ + int screen_pos; + + if (info->minimum == info->maximum) + return 0; + + screen_pos = (value - info->minimum) * (screen_size - 1) / (info->maximum - info->minimum); + return (screen_pos >= 0 && screen_pos < screen_size); +} + +static int vk_tp_to_screen(struct position *p, int *x, int *y) +{ + if (p->xi.minimum == p->xi.maximum || p->yi.minimum == p->yi.maximum) + return 0; + + *x = (p->x - p->xi.minimum) * (gr_fb_width() - 1) / (p->xi.maximum - p->xi.minimum); + *y = (p->y - p->yi.minimum) * (gr_fb_height() - 1) / (p->yi.maximum - p->yi.minimum); + + if (*x >= 0 && *x < gr_fb_width() && + *y >= 0 && *y < gr_fb_height()) { + return 0; + } + + return 1; +} + +/* Translate a virtual key in to a real key event, if needed */ +/* Returns non-zero when the event should be consumed */ +static int vk_modify(struct ev *e, struct input_event *ev) +{ + int i; + int x, y; + + if (ev->type == EV_KEY) { + if (ev->code == BTN_TOUCH) + e->p.pressed = ev->value; + return 0; + } + + if (ev->type == EV_ABS) { + switch (ev->code) { + case ABS_X: + e->p.x = ev->value; + return !vk_inside_display(e->p.x, &e->p.xi, gr_fb_width()); + case ABS_Y: + e->p.y = ev->value; + return !vk_inside_display(e->p.y, &e->p.yi, gr_fb_height()); + case ABS_MT_POSITION_X: + if (e->mt_idx) return 1; + e->mt_p.x = ev->value; + return !vk_inside_display(e->mt_p.x, &e->mt_p.xi, gr_fb_width()); + case ABS_MT_POSITION_Y: + if (e->mt_idx) return 1; + e->mt_p.y = ev->value; + return !vk_inside_display(e->mt_p.y, &e->mt_p.yi, gr_fb_height()); + case ABS_MT_TOUCH_MAJOR: + if (e->mt_idx) return 1; + if (e->sent) + e->mt_p.pressed = (ev->value > 0); + else + e->mt_p.pressed = (ev->value > PRESS_THRESHHOLD); + return 0; + } + + return 0; + } + + if (ev->type != EV_SYN) + return 0; + + if (ev->code == SYN_MT_REPORT) { + /* Ignore the rest of the points */ + ++e->mt_idx; + return 1; + } + if (ev->code != SYN_REPORT) + return 0; + + /* Report complete */ + + e->mt_idx = 0; + + if (!e->p.pressed && !e->mt_p.pressed) { + /* No touch */ + e->sent = 0; + return 0; + } + + if (!(e->p.pressed && vk_tp_to_screen(&e->p, &x, &y)) && + !(e->mt_p.pressed && vk_tp_to_screen(&e->mt_p, &x, &y))) { + /* No touch inside vk area */ + return 0; + } + + if (e->sent) { + /* We've already sent a fake key for this touch */ + return 1; + } + + /* The screen is being touched on the vk area */ + e->sent = 1; + + for (i = 0; i < e->vk_count; ++i) { + int xd = ABS(e->vks[i].centerx - x); + int yd = ABS(e->vks[i].centery - y); + if (xd < e->vks[i].width/2 && yd < e->vks[i].height/2) { + /* Fake a key event */ + ev->type = EV_KEY; + ev->code = e->vks[i].scancode; + ev->value = 1; + + vibrate(VIBRATOR_TIME_MS); + return 0; + } + } + + return 1; +} + int ev_get(struct input_event *ev, unsigned dont_wait) { int r; @@ -72,7 +351,10 @@ int ev_get(struct input_event *ev, unsigned dont_wait) for(n = 0; n < ev_count; n++) { if(ev_fds[n].revents & POLLIN) { r = read(ev_fds[n].fd, ev, sizeof(*ev)); - if(r == sizeof(*ev)) return 0; + if(r == sizeof(*ev)) { + if (!vk_modify(&evs[n], ev)) + return 0; + } } } } diff --git a/minui/font_7x16.h b/minui/font_7x16.h new file mode 100644 index 0000000..31c94fc --- /dev/null +++ b/minui/font_7x16.h @@ -0,0 +1,15 @@ +struct { + unsigned width; + unsigned height; + unsigned cwidth; + unsigned cheight; + unsigned char rundata[]; +} font = { + .width = 668, + .height = 16, + .cwidth = 7, + .cheight = 16, + .rundata = { +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7e,0x82,0x03,0x82,0x7f,0x7f,0x5f,0x82,0x0b,0x82,0x14,0x81,0x0b,0x81,0x11,0x81,0x0c,0x82,0x09,0x81,0x08,0x81,0x07,0x81,0x03,0x81,0x06,0x83,0x68,0x83,0x04,0x81,0x04,0x83,0x17,0x81,0x05,0x81,0x01,0x81,0x0c,0x81,0x04,0x82,0x07,0x83,0x04,0x81,0x07,0x81,0x05,0x81,0x06,0x81,0x25,0x81,0x02,0x84,0x02,0x83,0x05,0x84,0x03,0x84,0x05,0x82,0x02,0x85,0x04,0x83,0x02,0x86,0x02,0x84,0x03,0x84,0x27,0x83,0x0b,0x82,0x03,0x85,0x04,0x83,0x02,0x84,0x03,0x86,0x01,0x86,0x03,0x83,0x02,0x81,0x04,0x81,0x01,0x85,0x04,0x83,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x82,0x03,0x81,0x02,0x84,0x02,0x85,0x03,0x84,0x02,0x85,0x03,0x84,0x01,0x87,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x05,0x81,0x01,0x81,0x04,0x82,0x05,0x81,0x01,0x86,0x03,0x81,0x04,0x81,0x08,0x81,0x05,0x82,0x0e,0x81,0x0a,0x81,0x11,0x81,0x0b,0x81,0x0b,0x81,0x14,0x81,0x08,0x81,0x37,0x81,0x30,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x05,0x81,0x01,0x81,0x05,0x81,0x01,0x81,0x03,0x83,0x02,0x81,0x02,0x81,0x05,0x81,0x07,0x81,0x07,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x22,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x04,0x82,0x02,0x81,0x07,0x81,0x03,0x81,0x05,0x82,0x01,0x81,0x04,0x81,0x01,0x82,0x02,0x81,0x26,0x81,0x03,0x81,0x03,0x83,0x04,0x82,0x03,0x81,0x04,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x82,0x02,0x82,0x01,0x82,0x03,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x82,0x03,0x81,0x05,0x81,0x07,0x81,0x04,0x81,0x02,0x81,0x18,0x81,0x11,0x81,0x0b,0x81,0x0b,0x81,0x14,0x81,0x08,0x81,0x37,0x81,0x30,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x05,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x05,0x81,0x07,0x81,0x06,0x81,0x07,0x81,0x04,0x83,0x05,0x81,0x1d,0x81,0x02,0x81,0x04,0x81,0x03,0x81,0x09,0x81,0x06,0x81,0x03,0x81,0x01,0x81,0x02,0x81,0x06,0x81,0x0a,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x14,0x81,0x08,0x81,0x0b,0x81,0x02,0x81,0x02,0x82,0x03,0x82,0x03,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x82,0x02,0x82,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x08,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x07,0x81,0x04,0x81,0x05,0x81,0x07,0x81,0x03,0x81,0x04,0x81,0x11,0x83,0x03,0x84,0x04,0x83,0x04,0x84,0x03,0x83,0x03,0x85,0x03,0x84,0x02,0x81,0x01,0x82,0x03,0x83,0x05,0x83,0x03,0x81,0x03,0x81,0x04,0x81,0x04,0x85,0x02,0x81,0x01,0x82,0x04,0x83,0x03,0x84,0x04,0x84,0x03,0x84,0x03,0x83,0x03,0x85,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x05,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x85,0x04,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x86,0x01,0x81,0x01,0x81,0x04,0x82,0x02,0x81,0x03,0x82,0x0d,0x81,0x07,0x81,0x04,0x83,0x05,0x81,0x1c,0x81,0x03,0x81,0x04,0x81,0x03,0x81,0x09,0x81,0x06,0x81,0x02,0x82,0x01,0x81,0x02,0x85,0x02,0x81,0x01,0x83,0x06,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x06,0x81,0x06,0x83,0x0a,0x83,0x06,0x82,0x02,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x01,0x82,0x07,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x04,0x81,0x01,0x81,0x06,0x81,0x05,0x81,0x06,0x81,0x06,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x82,0x05,0x81,0x03,0x81,0x02,0x82,0x02,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x82,0x02,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x02,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x82,0x02,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x82,0x02,0x81,0x01,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x05,0x81,0x02,0x81,0x01,0x81,0x03,0x81,0x03,0x81,0x06,0x81,0x04,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0c,0x81,0x01,0x81,0x03,0x83,0x06,0x82,0x04,0x82,0x0d,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x1c,0x81,0x03,0x81,0x02,0x81,0x01,0x81,0x03,0x81,0x08,0x81,0x04,0x83,0x03,0x81,0x02,0x81,0x06,0x82,0x01,0x82,0x02,0x82,0x04,0x81,0x04,0x84,0x02,0x81,0x03,0x82,0x03,0x81,0x06,0x81,0x04,0x82,0x05,0x86,0x05,0x82,0x03,0x82,0x03,0x81,0x02,0x83,0x02,0x81,0x02,0x81,0x02,0x85,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x86,0x01,0x86,0x01,0x81,0x03,0x82,0x01,0x86,0x03,0x81,0x08,0x81,0x02,0x83,0x04,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x85,0x02,0x81,0x04,0x81,0x01,0x85,0x03,0x84,0x04,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x05,0x81,0x06,0x82,0x05,0x81,0x06,0x81,0x06,0x81,0x1d,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x05,0x81,0x08,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x03,0x82,0x07,0x81,0x07,0x82,0x02,0x83,0x10,0x81,0x0c,0x81,0x01,0x81,0x05,0x83,0x02,0x82,0x01,0x82,0x02,0x81,0x02,0x81,0x01,0x81,0x0a,0x81,0x07,0x81,0x05,0x81,0x03,0x87,0x09,0x83,0x0c,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x07,0x81,0x08,0x81,0x01,0x81,0x03,0x81,0x07,0x81,0x01,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x02,0x83,0x01,0x81,0x0f,0x82,0x10,0x82,0x03,0x81,0x04,0x81,0x01,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x81,0x03,0x81,0x06,0x81,0x01,0x82,0x01,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x03,0x81,0x07,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x02,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x01,0x81,0x03,0x82,0x05,0x81,0x06,0x81,0x06,0x81,0x07,0x81,0x05,0x81,0x1a,0x84,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x85,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x82,0x07,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x06,0x83,0x05,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x05,0x81,0x01,0x81,0x05,0x81,0x06,0x81,0x06,0x81,0x06,0x81,0x07,0x83,0x18,0x86,0x04,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x01,0x81,0x02,0x83,0x0a,0x81,0x07,0x81,0x0c,0x81,0x1b,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x06,0x81,0x09,0x81,0x01,0x86,0x06,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x06,0x81,0x11,0x83,0x02,0x86,0x02,0x83,0x0a,0x81,0x01,0x81,0x02,0x81,0x02,0x84,0x02,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x08,0x81,0x02,0x81,0x02,0x82,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x02,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x06,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x82,0x03,0x82,0x01,0x82,0x03,0x81,0x02,0x81,0x04,0x81,0x05,0x81,0x07,0x81,0x07,0x81,0x05,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x03,0x81,0x02,0x81,0x08,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x09,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x03,0x81,0x01,0x81,0x03,0x82,0x01,0x82,0x03,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x07,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x01,0x82,0x02,0x81,0x0c,0x81,0x05,0x81,0x0d,0x81,0x06,0x81,0x0d,0x81,0x05,0x81,0x06,0x81,0x02,0x81,0x04,0x81,0x05,0x81,0x05,0x81,0x04,0x81,0x05,0x81,0x02,0x81,0x03,0x82,0x02,0x81,0x02,0x82,0x03,0x81,0x04,0x81,0x04,0x81,0x01,0x81,0x03,0x81,0x04,0x81,0x06,0x81,0x09,0x81,0x08,0x81,0x08,0x81,0x04,0x81,0x02,0x83,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x02,0x81,0x03,0x81,0x01,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x07,0x81,0x03,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x06,0x81,0x04,0x81,0x01,0x81,0x03,0x82,0x02,0x81,0x02,0x81,0x02,0x81,0x07,0x81,0x02,0x82,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x82,0x03,0x81,0x03,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x04,0x82,0x07,0x81,0x08,0x81,0x04,0x81,0x19,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x82,0x05,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x04,0x81,0x07,0x81,0x03,0x81,0x02,0x81,0x05,0x81,0x04,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x02,0x81,0x03,0x81,0x03,0x81,0x05,0x81,0x03,0x81,0x04,0x81,0x04,0x81,0x03,0x81,0x04,0x81,0x05,0x81,0x01,0x81,0x04,0x81,0x01,0x81,0x04,0x82,0x04,0x81,0x08,0x81,0x06,0x81,0x06,0x81,0x17,0x81,0x0b,0x81,0x01,0x81,0x05,0x83,0x06,0x82,0x03,0x83,0x01,0x81,0x0b,0x81,0x05,0x81,0x0d,0x81,0x06,0x81,0x0d,0x81,0x05,0x81,0x06,0x84,0x02,0x85,0x02,0x86,0x02,0x84,0x06,0x81,0x03,0x84,0x03,0x84,0x03,0x81,0x06,0x84,0x03,0x83,0x05,0x81,0x06,0x81,0x1b,0x81,0x04,0x82,0x05,0x81,0x04,0x81,0x01,0x85,0x04,0x83,0x02,0x84,0x03,0x86,0x01,0x81,0x08,0x83,0x02,0x81,0x04,0x81,0x01,0x85,0x03,0x83,0x03,0x81,0x04,0x81,0x01,0x86,0x01,0x81,0x04,0x81,0x01,0x81,0x03,0x82,0x02,0x84,0x02,0x81,0x07,0x84,0x02,0x81,0x05,0x81,0x01,0x84,0x04,0x81,0x05,0x84,0x04,0x82,0x03,0x81,0x03,0x81,0x02,0x81,0x04,0x81,0x03,0x81,0x04,0x86,0x03,0x81,0x08,0x81,0x04,0x81,0x1a,0x84,0x02,0x84,0x04,0x83,0x04,0x84,0x03,0x83,0x05,0x81,0x05,0x84,0x02,0x81,0x03,0x81,0x02,0x85,0x05,0x81,0x03,0x81,0x03,0x81,0x05,0x82,0x02,0x81,0x01,0x81,0x01,0x81,0x02,0x81,0x03,0x81,0x03,0x83,0x03,0x84,0x04,0x84,0x03,0x81,0x06,0x83,0x05,0x83,0x03,0x84,0x04,0x81,0x05,0x81,0x01,0x81,0x03,0x81,0x03,0x81,0x04,0x81,0x04,0x85,0x04,0x81,0x06,0x81,0x06,0x81,0x2c,0x81,0x1c,0x82,0x03,0x82,0x13,0x81,0x13,0x81,0x54,0x81,0x22,0x81,0x79,0x81,0x43,0x82,0x08,0x81,0x02,0x82,0x47,0x81,0x13,0x81,0x26,0x81,0x0a,0x81,0x35,0x81,0x0d,0x83,0x04,0x81,0x04,0x83,0x2c,0x81,0x7f,0x44,0x83,0x76,0x81,0x7f,0x17,0x81,0x02,0x81,0x13,0x81,0x26,0x81,0x0a,0x81,0x34,0x81,0x15,0x81,0x7f,0x7f,0x7f,0x50,0x87,0x34,0x82,0x12,0x82,0x27,0x81,0x0a,0x81,0x33,0x82,0x7f,0x7f,0x7f,0x7f,0x7f,0x4b,0x00,0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x00,0x49,0x44,0x41,0x54,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x50,0x4c,0x54,0x45,0x00,0x00,0x00,0x00,0x62,0x4b,0x47,0x44,0x00,0x00,0x00,0x00,0x63,0x48,0x52,0x4d,0x00,0x00,0x00,0x00,0x67,0x41,0x4d,0x41,0x00,0x00,0x00,0x00,0x68,0x49,0x53,0x54,0x00,0x00,0x00,0x00,0x69,0x43,0x43,0x50,0x00,0x00,0x00,0x00,0x69,0x54,0x58,0x74,0x00,0x00,0x00,0x00,0x6f,0x46,0x46,0x73,0x00,0x00,0x00,0x00,0x70,0x43,0x41,0x4c,0x00,0x00,0x00,0x00,0x73,0x43,0x41,0x4c,0x00,0x00,0x00,0x00,0x70,0x48,0x59,0x73,0x00,0x00,0x00,0x00,0x73,0x42,0x49,0x54,0x00,0x00,0x00,0x00,0x73,0x50,0x4c,0x54,0x00,0x00,0x00,0x00,0x73,0x52,0x47,0x42,0x00,0x00,0x00,0x00,0x74,0x45,0x58,0x74,0x00,0x00,0x00,0x00,0x74,0x49,0x4d,0x45,0x00,0x00,0x00,0x00,0x74,0x52,0x4e,0x53,0x00,0x00,0x00,0x00,0x7a,0x54,0x58,0x74,0x00, + } +}; diff --git a/minui/graphics.c b/minui/graphics.c index 4127c40..a96342f 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -29,7 +29,12 @@ #include -#include "font_10x18.h" +#ifndef BOARD_LDPI_RECOVERY + #include "font_10x18.h" +#else + #include "font_7x16.h" +#endif + #include "minui.h" typedef struct { @@ -85,7 +90,7 @@ static int get_framebuffer(GGLSurface *fb) fb->version = sizeof(*fb); fb->width = vi.xres; fb->height = vi.yres; - fb->stride = vi.xres; + fb->stride = fi.line_length/2; /* stride is the number of pixels until the data of next row, >= xres */; fb->data = bits; fb->format = GGL_PIXEL_FORMAT_RGB_565; memset(fb->data, 0, vi.yres * vi.xres * 2); @@ -95,8 +100,8 @@ static int get_framebuffer(GGLSurface *fb) fb->version = sizeof(*fb); fb->width = vi.xres; fb->height = vi.yres; - fb->stride = vi.xres; - fb->data = (void*) (((unsigned) bits) + vi.yres * vi.xres * 2); + fb->stride = fi.line_length/2; + fb->data = (void*) (((unsigned) bits) + vi.yres * fi.line_length / 2); fb->format = GGL_PIXEL_FORMAT_RGB_565; memset(fb->data, 0, vi.yres * vi.xres * 2); diff --git a/minui/resources.c b/minui/resources.c index 3d2c727..e055e68 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -125,7 +125,7 @@ int res_create_surface(const char* name, gr_surface* pSurface) { int y; if (channels == 3) { - for (y = 0; y < height; ++y) { + for (y = 0; y < (int)height; ++y) { unsigned char* pRow = pData + y * stride; png_read_row(png_ptr, pRow, NULL); @@ -144,7 +144,7 @@ int res_create_surface(const char* name, gr_surface* pSurface) { } } } else { - for (y = 0; y < height; ++y) { + for (y = 0; y < (int)height; ++y) { unsigned char* pRow = pData + y * stride; png_read_row(png_ptr, pRow, NULL); } diff --git a/mmcutils/Android.mk b/mmcutils/Android.mk new file mode 100644 index 0000000..f1fe294 --- /dev/null +++ b/mmcutils/Android.mk @@ -0,0 +1,16 @@ +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_ARCH),arm) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + mmcutils.c + +LOCAL_MODULE := libmmcutils +LOCAL_MODULE_TAGS := eng + +include $(BUILD_STATIC_LIBRARY) + +endif # TARGET_ARCH == arm +endif # !TARGET_SIMULATOR diff --git a/mmcutils/mmcutils.c b/mmcutils/mmcutils.c new file mode 100644 index 0000000..44fdcf2 --- /dev/null +++ b/mmcutils/mmcutils.c @@ -0,0 +1,591 @@ +/* + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for _IOW, _IOR, mount() + +#include "mmcutils.h" + +unsigned ext3_count = 0; +char *ext3_partitions[] = {"system", "userdata", "cache", "NONE"}; + +unsigned vfat_count = 0; +char *vfat_partitions[] = {"modem", "NONE"}; + +struct MmcPartition { + char *device_index; + char *filesystem; + char *name; + unsigned dstatus; + unsigned dtype ; + unsigned dfirstsec; + unsigned dsize; +}; + +typedef struct { + MmcPartition *partitions; + int partitions_allocd; + int partition_count; +} MmcState; + +static MmcState g_mmc_state = { + NULL, // partitions + 0, // partitions_allocd + -1 // partition_count +}; + +#define MMC_DEVICENAME "/dev/block/mmcblk0" + +static void +mmc_partition_name (MmcPartition *mbr, unsigned int type) { + switch(type) + { + char name[64]; + case MMC_BOOT_TYPE: + sprintf(name,"boot"); + mbr->name = strdup(name); + break; + case MMC_RECOVERY_TYPE: + sprintf(name,"recovery"); + mbr->name = strdup(name); + break; + case MMC_EXT3_TYPE: + if (strcmp("NONE", ext3_partitions[ext3_count])) { + strcpy((char *)name,(const char *)ext3_partitions[ext3_count]); + mbr->name = strdup(name); + ext3_count++; + } + mbr->filesystem = strdup("ext3"); + break; + case MMC_VFAT_TYPE: + if (strcmp("NONE", vfat_partitions[vfat_count])) { + strcpy((char *)name,(const char *)vfat_partitions[vfat_count]); + mbr->name = strdup(name); + vfat_count++; + } + mbr->filesystem = strdup("vfat"); + break; + }; +} + +static int +mmc_read_mbr (const char *device, MmcPartition *mbr) { + FILE *fd; + unsigned char buffer[512]; + int idx, i; + unsigned mmc_partition_count = 0; + unsigned int dtype; + unsigned int dfirstsec; + unsigned int EBR_first_sec; + unsigned int EBR_current_sec; + int ret = -1; + + fd = fopen(device, "r"); + if(fd == NULL) + { + printf("Can't open device: \"%s\"\n", device); + goto ERROR2; + } + if ((fread(buffer, 512, 1, fd)) != 1) + { + printf("Can't read device: \"%s\"\n", device); + goto ERROR1; + } + /* Check to see if signature exists */ + if ((buffer[TABLE_SIGNATURE] != 0x55) || \ + (buffer[TABLE_SIGNATURE + 1] != 0xAA)) + { + printf("Incorrect mbr signatures!\n"); + goto ERROR1; + } + idx = TABLE_ENTRY_0; + for (i = 0; i < 4; i++) + { + char device_index[128]; + + mbr[mmc_partition_count].dstatus = \ + buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS]; + mbr[mmc_partition_count].dtype = \ + buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE]; + mbr[mmc_partition_count].dfirstsec = \ + GET_LWORD_FROM_BYTE(&buffer[idx + \ + i * TABLE_ENTRY_SIZE + \ + OFFSET_FIRST_SEC]); + mbr[mmc_partition_count].dsize = \ + GET_LWORD_FROM_BYTE(&buffer[idx + \ + i * TABLE_ENTRY_SIZE + \ + OFFSET_SIZE]); + dtype = mbr[mmc_partition_count].dtype; + dfirstsec = mbr[mmc_partition_count].dfirstsec; + mmc_partition_name(&mbr[mmc_partition_count], \ + mbr[mmc_partition_count].dtype); + + sprintf(device_index, "%sp%d", device, (mmc_partition_count+1)); + mbr[mmc_partition_count].device_index = strdup(device_index); + + mmc_partition_count++; + if (mmc_partition_count == MAX_PARTITIONS) + goto SUCCESS; + } + + /* See if the last partition is EBR, if not, parsing is done */ + if (dtype != 0x05) + { + goto SUCCESS; + } + + EBR_first_sec = dfirstsec; + EBR_current_sec = dfirstsec; + + fseek (fd, (EBR_first_sec * 512), SEEK_SET); + if ((fread(buffer, 512, 1, fd)) != 1) + goto ERROR1; + + /* Loop to parse the EBR */ + for (i = 0;; i++) + { + char device_index[128]; + + if ((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE + 1] != 0xAA)) + { + break; + } + mbr[mmc_partition_count].dstatus = \ + buffer[TABLE_ENTRY_0 + OFFSET_STATUS]; + mbr[mmc_partition_count].dtype = \ + buffer[TABLE_ENTRY_0 + OFFSET_TYPE]; + mbr[mmc_partition_count].dfirstsec = \ + GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \ + OFFSET_FIRST_SEC]) + \ + EBR_current_sec; + mbr[mmc_partition_count].dsize = \ + GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \ + OFFSET_SIZE]); + mmc_partition_name(&mbr[mmc_partition_count], \ + mbr[mmc_partition_count].dtype); + + sprintf(device_index, "%sp%d", device, (mmc_partition_count+1)); + mbr[mmc_partition_count].device_index = strdup(device_index); + + mmc_partition_count++; + if (mmc_partition_count == MAX_PARTITIONS) + goto SUCCESS; + + dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]); + if(dfirstsec == 0) + { + /* Getting to the end of the EBR tables */ + break; + } + /* More EBR to follow - read in the next EBR sector */ + fseek (fd, ((EBR_first_sec + dfirstsec) * 512), SEEK_SET); + if ((fread(buffer, 512, 1, fd)) != 1) + goto ERROR1; + + EBR_current_sec = EBR_first_sec + dfirstsec; + } + +SUCCESS: + ret = mmc_partition_count; +ERROR1: + fclose(fd); +ERROR2: + return ret; +} + +int +mmc_scan_partitions() { + int i; + ssize_t nbytes; + + if (g_mmc_state.partitions == NULL) { + const int nump = MAX_PARTITIONS; + MmcPartition *partitions = malloc(nump * sizeof(*partitions)); + if (partitions == NULL) { + errno = ENOMEM; + return -1; + } + g_mmc_state.partitions = partitions; + g_mmc_state.partitions_allocd = nump; + memset(partitions, 0, nump * sizeof(*partitions)); + } + g_mmc_state.partition_count = 0; + ext3_count = 0; + vfat_count = 0; + + /* Initialize all of the entries to make things easier later. + * (Lets us handle sparsely-numbered partitions, which + * may not even be possible.) + */ + for (i = 0; i < g_mmc_state.partitions_allocd; i++) { + MmcPartition *p = &g_mmc_state.partitions[i]; + if (p->device_index != NULL) { + free(p->device_index); + p->device_index = NULL; + } + if (p->name != NULL) { + free(p->name); + p->name = NULL; + } + if (p->filesystem != NULL) { + free(p->filesystem); + p->filesystem = NULL; + } + } + + g_mmc_state.partition_count = mmc_read_mbr(MMC_DEVICENAME, g_mmc_state.partitions); + if(g_mmc_state.partition_count == -1) + { + printf("Error in reading mbr!\n"); + // keep "partitions" around so we can free the names on a rescan. + g_mmc_state.partition_count = -1; + } + return g_mmc_state.partition_count; +} + +const MmcPartition * +mmc_find_partition_by_name(const char *name) +{ + if (g_mmc_state.partitions != NULL) { + int i; + for (i = 0; i < g_mmc_state.partitions_allocd; i++) { + MmcPartition *p = &g_mmc_state.partitions[i]; + if (p->device_index !=NULL && p->name != NULL) { + if (strcmp(p->name, name) == 0) { + return p; + } + } + } + } + return NULL; +} + +#define MKE2FS_BIN "/sbin/mke2fs" +#define TUNE2FS_BIN "/sbin/tune2fs" +#define E2FSCK_BIN "/sbin/e2fsck" + +static int +run_exec_process ( char **argv) { + pid_t pid; + int status; + pid = fork(); + if (pid == 0) { + execv(argv[0], argv); + fprintf(stderr, "E:Can't run (%s)\n",strerror(errno)); + _exit(-1); + } + + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + return 1; + } + return 0; +} + +int +mmc_format_ext3 (MmcPartition *partition) { + char device[128]; + strcpy(device, partition->device_index); + // Run mke2fs + char *const mke2fs[] = {MKE2FS_BIN, "-j", device, NULL}; + if(run_exec_process(mke2fs)) + return -1; + + // Run tune2fs + char *const tune2fs[] = {TUNE2FS_BIN, "-j", "-C", "1", device, NULL}; + if(run_exec_process(tune2fs)) + return -1; + + // Run e2fsck + char *const e2fsck[] = {E2FSCK_BIN, "-fy", device, NULL}; + if(run_exec_process(e2fsck)) + return -1; + + return 0; +} + +int +mmc_mount_partition(const MmcPartition *partition, const char *mount_point, + int read_only) +{ + const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME; + char devname[128]; + int rv = -1; + strcpy(devname, partition->device_index); + if (partition->filesystem == NULL) { + printf("Null filesystem!\n"); + return rv; + } + if (!read_only) { + rv = mount(devname, mount_point, partition->filesystem, flags, NULL); + } + if (read_only || rv < 0) { + rv = mount(devname, mount_point, partition->filesystem, flags | MS_RDONLY, 0); + if (rv < 0) { + printf("Failed to mount %s on %s: %s\n", + devname, mount_point, strerror(errno)); + } else { + printf("Mount %s on %s read-only\n", devname, mount_point); + } + } + return rv; +} + +int +mmc_raw_copy (const MmcPartition *partition, char *in_file) { + int ch; + FILE *in; + FILE *out; + int val = 0; + char buf[512]; + unsigned sz = 0; + unsigned i; + int ret = -1; + char *out_file = partition->device_index; + + in = fopen ( in_file, "r" ); + if (in == NULL) + goto ERROR3; + + out = fopen ( out_file, "w" ); + if (out == NULL) + goto ERROR2; + + fseek(in, 0L, SEEK_END); + sz = ftell(in); + fseek(in, 0L, SEEK_SET); + + if (sz % 512) + { + while ( ( ch = fgetc ( in ) ) != EOF ) + fputc ( ch, out ); + } + else + { + for (i=0; i< (sz/512); i++) + { + if ((fread(buf, 512, 1, in)) != 1) + goto ERROR1; + if ((fwrite(buf, 512, 1, out)) != 1) + goto ERROR1; + } + } + + fsync(out); + ret = 0; +ERROR1: + fclose ( out ); +ERROR2: + fclose ( in ); +ERROR3: + return ret; + +} + + +// TODO: refactor this to not be a giant copy paste mess +int +mmc_raw_dump (const MmcPartition *partition, char *out_file) { + int ch; + FILE *in; + FILE *out; + int val = 0; + char buf[512]; + unsigned sz = 0; + unsigned i; + int ret = -1; + char *in_file = partition->device_index; + + in = fopen ( in_file, "r" ); + if (in == NULL) + goto ERROR3; + + out = fopen ( out_file, "w" ); + if (out == NULL) + goto ERROR2; + + fseek(in, 0L, SEEK_END); + sz = ftell(in); + fseek(in, 0L, SEEK_SET); + + if (sz % 512) + { + while ( ( ch = fgetc ( in ) ) != EOF ) + fputc ( ch, out ); + } + else + { + for (i=0; i< (sz/512); i++) + { + if ((fread(buf, 512, 1, in)) != 1) + goto ERROR1; + if ((fwrite(buf, 512, 1, out)) != 1) + goto ERROR1; + } + } + + fsync(out); + ret = 0; +ERROR1: + fclose ( out ); +ERROR2: + fclose ( in ); +ERROR3: + return ret; + +} + + +int +mmc_raw_read (const MmcPartition *partition, char *data, int data_size) { + int ch; + FILE *in; + int val = 0; + char buf[512]; + unsigned sz = 0; + unsigned i; + int ret = -1; + char *in_file = partition->device_index; + + in = fopen ( in_file, "r" ); + if (in == NULL) + goto ERROR3; + + fseek(in, 0L, SEEK_END); + sz = ftell(in); + fseek(in, 0L, SEEK_SET); + + fread(data, data_size, 1, in); + + ret = 0; +ERROR1: +ERROR2: + fclose ( in ); +ERROR3: + return ret; + +} + +int +mmc_raw_write (const MmcPartition *partition, char *data, int data_size) { + int ch; + FILE *out; + int val = 0; + char buf[512]; + unsigned sz = 0; + unsigned i; + int ret = -1; + char *out_file = partition->device_index; + + out = fopen ( out_file, "w" ); + if (out == NULL) + goto ERROR3; + + fwrite(data, data_size, 1, out); + + ret = 0; +ERROR1: +ERROR2: + fclose ( out ); +ERROR3: + return ret; + +} + +int cmd_mmc_restore_raw_partition(const char *partition, const char *filename) +{ + mmc_scan_partitions(); + const MmcPartition *p; + p = mmc_find_partition_by_name(partition); + if (p == NULL) + return -1; + return mmc_raw_copy(p, filename); +} + +int cmd_mmc_backup_raw_partition(const char *partition, const char *filename) +{ + mmc_scan_partitions(); + const MmcPartition *p; + p = mmc_find_partition_by_name(partition); + if (p == NULL) + return -1; + return mmc_raw_dump(p, filename); +} + +int cmd_mmc_erase_raw_partition(const char *partition) +{ + mmc_scan_partitions(); + const MmcPartition *p; + p = mmc_find_partition_by_name(partition); + if (p == NULL) + return -1; + + // TODO: implement raw wipe + return 0; +} + +int cmd_mmc_erase_partition(const char *partition, const char *filesystem) +{ + mmc_scan_partitions(); + const MmcPartition *p; + p = mmc_find_partition_by_name(partition); + if (p == NULL) + return -1; + return mmc_format_ext3 (p); +} + +int cmd_mmc_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only) +{ + mmc_scan_partitions(); + const MmcPartition *p; + p = mmc_find_partition_by_name(partition); + if (p == NULL) + return -1; + return mmc_mount_partition(p, mount_point, read_only); +} + +int cmd_mmc_get_partition_device(const char *partition, char *device) +{ + mmc_scan_partitions(); + const MmcPartition *p; + p = mmc_find_partition_by_name(partition); + if (p == NULL) + return -1; + strcpy(device, p->device_index); + return 0; +} diff --git a/mmcutils/mmcutils.h b/mmcutils/mmcutils.h new file mode 100644 index 0000000..64e5813 --- /dev/null +++ b/mmcutils/mmcutils.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MMCUTILS_H_ +#define MMCUTILS_H_ + +/* Some useful define used to access the MBR/EBR table */ +#define BLOCK_SIZE 0x200 +#define TABLE_ENTRY_0 0x1BE +#define TABLE_ENTRY_1 0x1CE +#define TABLE_ENTRY_2 0x1DE +#define TABLE_ENTRY_3 0x1EE +#define TABLE_SIGNATURE 0x1FE +#define TABLE_ENTRY_SIZE 0x010 + +#define OFFSET_STATUS 0x00 +#define OFFSET_TYPE 0x04 +#define OFFSET_FIRST_SEC 0x08 +#define OFFSET_SIZE 0x0C +#define COPYBUFF_SIZE (1024 * 16) +#define BINARY_IN_TABLE_SIZE (16 * 512) +#define MAX_FILE_ENTRIES 20 + +#define MMC_BOOT_TYPE 0x48 +#define MMC_SYSTEM_TYPE 0x82 +#define MMC_USERDATA_TYPE 0x83 +#define MMC_RECOVERY_TYPE 0x71 + +#define MMC_RCA 2 + +#define MAX_PARTITIONS 64 + +#define GET_LWORD_FROM_BYTE(x) ((unsigned)*(x) | \ + ((unsigned)*((x)+1) << 8) | \ + ((unsigned)*((x)+2) << 16) | \ + ((unsigned)*((x)+3) << 24)) + +#define PUT_LWORD_TO_BYTE(x, y) do{*(x) = (y) & 0xff; \ + *((x)+1) = ((y) >> 8) & 0xff; \ + *((x)+2) = ((y) >> 16) & 0xff; \ + *((x)+3) = ((y) >> 24) & 0xff; }while(0) + +#define GET_PAR_NUM_FROM_POS(x) ((((x) & 0x0000FF00) >> 8) + ((x) & 0x000000FF)) + +#define MMC_BOOT_TYPE 0x48 +#define MMC_EXT3_TYPE 0x83 +#define MMC_VFAT_TYPE 0xC +typedef struct MmcPartition MmcPartition; + +/* Functions */ +int mmc_scan_partitions(); +const MmcPartition *mmc_find_partition_by_name(const char *name); +int mmc_format_ext3 (MmcPartition *partition); +int mmc_mount_partition(const MmcPartition *partition, const char *mount_point, \ + int read_only); +int mmc_raw_copy (const MmcPartition *partition, char *in_file); +int mmc_raw_read (const MmcPartition *partition, char *data, int data_size); +int mmc_raw_write (const MmcPartition *partition, char *data, int data_size); + +#endif // MMCUTILS_H_ + + diff --git a/mtdutils/mounts.c b/mounts.c similarity index 100% rename from mtdutils/mounts.c rename to mounts.c diff --git a/mtdutils/mounts.h b/mounts.h similarity index 100% rename from mtdutils/mounts.h rename to mounts.h diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index 57ab579..90e97fd 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -5,20 +5,12 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - mtdutils.c \ - mounts.c + mtdutils.c LOCAL_MODULE := libmtdutils include $(BUILD_STATIC_LIBRARY) -include $(CLEAR_VARS) -LOCAL_SRC_FILES := flash_image.c -LOCAL_MODULE := flash_image -LOCAL_MODULE_TAGS := eng -LOCAL_STATIC_LIBRARIES := libmtdutils -LOCAL_SHARED_LIBRARIES := libcutils libc -include $(BUILD_EXECUTABLE) endif # TARGET_ARCH == arm endif # !TARGET_SIMULATOR diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 198f498..c8dbba4 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -28,13 +28,6 @@ #include "mtdutils.h" -struct MtdPartition { - int device_index; - unsigned int size; - unsigned int erase_size; - char *name; -}; - struct MtdReadContext { const MtdPartition *partition; char *buffer; @@ -345,7 +338,7 @@ ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) read += ctx->partition->erase_size; } - if (read >= len) { + if (read >= (int)len) { return read; } @@ -569,3 +562,295 @@ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) { } return pos; } + +#define BLOCK_SIZE 2048 +#define SPARE_SIZE (BLOCK_SIZE >> 5) +#define HEADER_SIZE 2048 + +int cmd_mtd_restore_raw_partition(const char *partition_name, const char *filename) +{ + const MtdPartition *ptn; + MtdWriteContext *write; + void *data; + unsigned sz; + + if (mtd_scan_partitions() <= 0) + { + printf("error scanning partitions"); + return -1; + } + const MtdPartition *partition = mtd_find_partition_by_name(partition_name); + if (partition == NULL) + { + printf("can't find %s partition", partition_name); + return -1; + } + + // If the first part of the file matches the partition, skip writing + + int fd = open(filename, O_RDONLY); + if (fd < 0) + { + printf("error opening %s", filename); + return -1; + } + + char header[HEADER_SIZE]; + int headerlen = read(fd, header, sizeof(header)); + if (headerlen <= 0) + { + printf("error reading %s header", filename); + return -1; + } + + MtdReadContext *in = mtd_read_partition(partition); + if (in == NULL) { + printf("error opening %s: %s\n", partition, strerror(errno)); + // just assume it needs re-writing + } else { + char check[HEADER_SIZE]; + int checklen = mtd_read_data(in, check, sizeof(check)); + if (checklen <= 0) { + printf("error reading %s: %s\n", partition_name, strerror(errno)); + // just assume it needs re-writing + } else if (checklen == headerlen && !memcmp(header, check, headerlen)) { + printf("header is the same, not flashing %s\n", partition_name); + return 0; + } + mtd_read_close(in); + } + + // Skip the header (we'll come back to it), write everything else + printf("flashing %s from %s\n", partition_name, filename); + + MtdWriteContext *out = mtd_write_partition(partition); + if (out == NULL) + { + printf("error writing %s", partition_name); + return -1; + } + + char buf[HEADER_SIZE]; + memset(buf, 0, headerlen); + int wrote = mtd_write_data(out, buf, headerlen); + if (wrote != headerlen) + { + printf("error writing %s", partition_name); + return -1; + } + + int len; + while ((len = read(fd, buf, sizeof(buf))) > 0) { + wrote = mtd_write_data(out, buf, len); + if (wrote != len) + { + printf("error writing %s", partition_name); + return -1; + } + } + if (len < 0) + { + printf("error reading %s", filename); + return -1; + } + + if (mtd_write_close(out)) + { + printf("error closing %s", partition_name); + return -1; + } + + // Now come back and write the header last + + out = mtd_write_partition(partition); + if (out == NULL) + { + printf("error re-opening %s", partition_name); + return -1; + } + + wrote = mtd_write_data(out, header, headerlen); + if (wrote != headerlen) + { + printf("error re-writing %s", partition_name); + return -1; + } + + // Need to write a complete block, so write the rest of the first block + size_t block_size; + if (mtd_partition_info(partition, NULL, &block_size, NULL)) + { + printf("error getting %s block size", partition_name); + return -1; + } + + if (lseek(fd, headerlen, SEEK_SET) != headerlen) + { + printf("error rewinding %s", filename); + return -1; + } + + int left = block_size - headerlen; + while (left < 0) left += block_size; + while (left > 0) { + len = read(fd, buf, left > (int)sizeof(buf) ? (int)sizeof(buf) : left); + if (len <= 0){ + printf("error reading %s", filename); + return -1; + } + if (mtd_write_data(out, buf, len) != len) + { + printf("error writing %s", partition_name); + return -1; + } + + left -= len; + } + + if (mtd_write_close(out)) + { + printf("error closing %s", partition_name); + return -1; + } + return 0; +} + + +int cmd_mtd_backup_raw_partition(const char *partition_name, const char *filename) +{ + MtdReadContext *in; + const MtdPartition *partition; + char buf[BLOCK_SIZE + SPARE_SIZE]; + size_t partition_size; + size_t read_size; + size_t total; + int fd; + int wrote; + int len; + + if (mtd_scan_partitions() <= 0) + { + printf("error scanning partitions"); + return -1; + } + + partition = mtd_find_partition_by_name(partition_name); + if (partition == NULL) + { + printf("can't find %s partition", partition_name); + return -1; + } + + if (mtd_partition_info(partition, &partition_size, NULL, NULL)) { + printf("can't get info of partition %s", partition_name); + return -1; + } + + if (!strcmp(filename, "-")) { + fd = fileno(stdout); + } + else { + fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666); + } + + if (fd < 0) + { + printf("error opening %s", filename); + return -1; + } + + in = mtd_read_partition(partition); + if (in == NULL) { + close(fd); + unlink(filename); + printf("error opening %s: %s\n", partition_name, strerror(errno)); + return -1; + } + + total = 0; + while ((len = mtd_read_data(in, buf, BLOCK_SIZE)) > 0) { + wrote = write(fd, buf, len); + if (wrote != len) { + close(fd); + unlink(filename); + printf("error writing %s", filename); + return -1; + } + total += BLOCK_SIZE; + } + + mtd_read_close(in); + + if (close(fd)) { + unlink(filename); + printf("error closing %s", filename); + return -1; + } + return 0; +} + +int cmd_mtd_erase_raw_partition(const char *partition_name) +{ + MtdWriteContext *out; + size_t erased; + size_t total_size; + size_t erase_size; + + if (mtd_scan_partitions() <= 0) + { + printf("error scanning partitions"); + return -1; + } + const MtdPartition *p = mtd_find_partition_by_name(partition_name); + if (p == NULL) + { + printf("can't find %s partition", partition_name); + return -1; + } + + out = mtd_write_partition(p); + if (out == NULL) + { + printf("could not estabilish write context for %s", partition_name); + return -1; + } + + // do the actual erase, -1 = full partition erase + erased = mtd_erase_blocks(out, -1); + + // erased = bytes erased, if zero, something borked + if (!erased) + { + printf("error erasing %s", partition_name); + return -1; + } + + return 0; +} + +int cmd_mtd_erase_partition(const char *partition, const char *filesystem) +{ + return cmd_mtd_erase_raw_partition(partition); +} + + +int cmd_mtd_mount_partition(const char *partition, const char *mount_point, const char *filesystem, int read_only) +{ + mtd_scan_partitions(); + const MtdPartition *p; + p = mtd_find_partition_by_name(partition); + if (p == NULL) { + return -1; + } + return mtd_mount_partition(p, mount_point, filesystem, read_only); +} + +int cmd_mtd_get_partition_device(const char *partition, char *device) +{ + mtd_scan_partitions(); + MtdPartition *p = mtd_find_partition_by_name(partition); + if (p == NULL) + return -1; + sprintf(device, "/dev/block/mtdblock%d", p->device_index); + return 0; +} diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h index 45d3ebc..c57d45d 100644 --- a/mtdutils/mtdutils.h +++ b/mtdutils/mtdutils.h @@ -53,4 +53,11 @@ off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos); int mtd_write_close(MtdWriteContext *); +struct MtdPartition { + int device_index; + unsigned int size; + unsigned int erase_size; + char *name; +}; + #endif // MTDUTILS_H_ diff --git a/nandroid-md5.sh b/nandroid-md5.sh new file mode 100644 index 0000000..0db53ee --- /dev/null +++ b/nandroid-md5.sh @@ -0,0 +1,4 @@ +#!/sbin/sh +cd $1 +md5sum *img > nandroid.md5 +return $? \ No newline at end of file diff --git a/nandroid.c b/nandroid.c new file mode 100644 index 0000000..4f7724a --- /dev/null +++ b/nandroid.c @@ -0,0 +1,361 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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 + +#include "extendedcommands.h" +#include "nandroid.h" + +int print_and_error(const char* message) { + ui_print("%s", message); + return 1; +} + +int yaffs_files_total = 0; +int yaffs_files_count = 0; +void yaffs_callback(char* filename) +{ + char* justfile = basename(filename); + if (strlen(justfile) < 30) + ui_print("%s", justfile); + yaffs_files_count++; + if (yaffs_files_total != 0) + ui_set_progress((float)yaffs_files_count / (float)yaffs_files_total); + ui_reset_text_col(); +} + +void compute_directory_stats(char* directory) +{ + char tmp[PATH_MAX]; + sprintf(tmp, "find %s | wc -l > /tmp/dircount", directory); + __system(tmp); + char count_text[100]; + FILE* f = fopen("/tmp/dircount", "r"); + fread(count_text, 1, sizeof(count_text), f); + fclose(f); + yaffs_files_count = 0; + yaffs_files_total = atoi(count_text); + ui_reset_progress(); + ui_show_progress(1, 0); +} + +int nandroid_backup_partition_extended(const char* backup_path, const char* mount_point, int umount_when_finished) { + int ret = 0; + char* name = basename(mount_point); + + struct stat file_info; + mkyaffs2image_callback callback = NULL; + if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) { + callback = yaffs_callback; + } + + ui_print("Backing up %s...\n", name); + if (0 != (ret = ensure_path_mounted(mount_point) != 0)) { + ui_print("Can't mount %s!\n", mount_point); + return ret; + } + compute_directory_stats(mount_point); + char tmp[PATH_MAX]; + sprintf(tmp, "%s/%s.img", backup_path, name); + ret = mkyaffs2image(mount_point, tmp, 0, callback); + if (umount_when_finished) { + ensure_path_unmounted(mount_point); + } + if (0 != ret) { + ui_print("Error while making a yaffs2 image of %s!\n", mount_point); + return ret; + } + return 0; +} + +int nandroid_backup_partition(const char* backup_path, char* root) { + return nandroid_backup_partition_extended(backup_path, root, 1); +} + +int nandroid_backup(const char* backup_path) +{ + ui_set_background(BACKGROUND_ICON_INSTALLING); + + if (ensure_path_mounted("/sdcard") != 0) + return print_and_error("Can't mount /sdcard\n"); + + int ret; + struct statfs s; + if (0 != (ret = statfs("/sdcard", &s))) + return print_and_error("Unable to stat /sdcard\n"); + uint64_t bavail = s.f_bavail; + uint64_t bsize = s.f_bsize; + uint64_t sdcard_free = bavail * bsize; + uint64_t sdcard_free_mb = sdcard_free / (uint64_t)(1024 * 1024); + ui_print("SD Card space free: %lluMB\n", sdcard_free_mb); + if (sdcard_free_mb < 150) + ui_print("There may not be enough free space to complete backup... continuing...\n"); + + char tmp[PATH_MAX]; + sprintf(tmp, "mkdir -p %s", backup_path); + __system(tmp); + +#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES + ui_print("Backing up boot...\n"); + sprintf(tmp, "%s/%s", backup_path, "boot.img"); + ret = backup_raw_partition("boot", tmp); + if (0 != ret) + return print_and_error("Error while dumping boot image!\n"); + + ui_print("Backing up recovery...\n"); + sprintf(tmp, "%s/%s", backup_path, "recovery.img"); + ret = backup_raw_partition("recovery", tmp); + if (0 != ret) + return print_and_error("Error while dumping recovery image!\n"); +#endif + + if (0 != (ret = nandroid_backup_partition(backup_path, "/system"))) + return ret; + + if (0 != (ret = nandroid_backup_partition(backup_path, "/data"))) + return ret; + +#ifdef BOARD_HAS_DATADATA + if (0 != (ret = nandroid_backup_partition(backup_path, "/datadata"))) + return ret; +#endif + + struct stat st; + if (0 != stat("/sdcard/.android_secure", &st)) + { + ui_print("No /sdcard/.android_secure found. Skipping backup of applications on external storage.\n"); + } + else + { + if (0 != (ret = nandroid_backup_partition_extended(backup_path, "/sdcard/.android_secure", 0))) + return ret; + } + + if (0 != (ret = nandroid_backup_partition_extended(backup_path, "/cache", 0))) + return ret; + + Volume *vol = volume_for_path("/sd-ext"); + if (vol == NULL || 0 != stat(vol->device, &st)) + { + ui_print("No sd-ext found. Skipping backup of sd-ext.\n"); + } + else + { + if (0 != ensure_path_mounted("/sd-ext")) + ui_print("Could not mount sd-ext. sd-ext backup may not be supported on this device. Skipping backup of sd-ext.\n"); + else if (0 != (ret = nandroid_backup_partition(backup_path, "SDEXT:"))) + return ret; + } + + ui_print("Generating md5 sum...\n"); + sprintf(tmp, "nandroid-md5.sh %s", backup_path); + if (0 != (ret = __system(tmp))) { + ui_print("Error while generating md5 sum!\n"); + return ret; + } + + sync(); + ui_set_background(BACKGROUND_ICON_NONE); + ui_reset_progress(); + ui_print("\nBackup complete!\n"); + return 0; +} + +typedef int (*format_function)(char* root); + +static void ensure_directory(const char* dir) { + char tmp[PATH_MAX]; + sprintf(tmp, "mkdir -p %s", dir); + __system(tmp); +} + +int nandroid_restore_partition_extended(const char* backup_path, const char* mount_point, int umount_when_finished) { + int ret = 0; + char* name = basename(mount_point); + + char tmp[PATH_MAX]; + sprintf(tmp, "%s/%s.img", backup_path, name); + struct stat file_info; + if (0 != (ret = statfs(tmp, &file_info))) { + ui_print("%s.img not found. Skipping restore of %s.\n", name, mount_point); + return 0; + } + + ensure_directory(mount_point); + + unyaffs_callback callback = NULL; + if (0 != stat("/sdcard/clockworkmod/.hidenandroidprogress", &file_info)) { + callback = yaffs_callback; + } + + ui_print("Restoring %s...\n", name); + /* + if (0 != (ret = ensure_root_path_unmounted(root))) { + ui_print("Can't unmount %s!\n", mount_point); + return ret; + } + */ + if (0 != (ret = format_device(mount_point))) { + ui_print("Error while formatting %s!\n", mount_point); + return ret; + } + + if (0 != (ret = ensure_path_mounted(mount_point))) { + ui_print("Can't mount %s!\n", mount_point); + return ret; + } + + if (0 != (ret = unyaffs(tmp, mount_point, callback))) { + ui_print("Error while restoring %s!\n", mount_point); + return ret; + } + + if (umount_when_finished) { + ensure_path_unmounted(mount_point); + } + + return 0; +} + +int nandroid_restore_partition(const char* backup_path, const char* root) { + return nandroid_restore_partition_extended(backup_path, root, 1); +} + +int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext) +{ + ui_set_background(BACKGROUND_ICON_INSTALLING); + ui_show_indeterminate_progress(); + yaffs_files_total = 0; + + if (ensure_path_mounted("/sdcard") != 0) + return print_and_error("Can't mount /sdcard\n"); + + char tmp[PATH_MAX]; + + ui_print("Checking MD5 sums...\n"); + sprintf(tmp, "cd %s && md5sum -c nandroid.md5", backup_path); + if (0 != __system(tmp)) + return print_and_error("MD5 mismatch!\n"); + + int ret; +#ifndef BOARD_RECOVERY_IGNORE_BOOTABLES + if (restore_boot) + { + ui_print("Erasing boot before restore...\n"); + if (0 != (ret = format_device("boot"))) + return print_and_error("Error while formatting BOOT:!\n"); + sprintf(tmp, "%s/boot.img", backup_path); + ui_print("Restoring boot image...\n"); + if (0 != (ret = restore_raw_partition("boot", tmp))) { + ui_print("Error while flashing boot image!"); + return ret; + } + } +#endif + + if (restore_system && 0 != (ret = nandroid_restore_partition(backup_path, "/system"))) + return ret; + + if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "/data"))) + return ret; + +#ifdef BOARD_HAS_DATADATA + if (restore_data && 0 != (ret = nandroid_restore_partition(backup_path, "/datadata"))) + return ret; +#endif + + if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, "/sdcard/.android_secure", 0))) + return ret; + + if (restore_cache && 0 != (ret = nandroid_restore_partition_extended(backup_path, "/cache", 0))) + return ret; + + if (restore_sdext && 0 != (ret = nandroid_restore_partition(backup_path, "/sd-ext"))) + return ret; + + sync(); + ui_set_background(BACKGROUND_ICON_NONE); + ui_reset_progress(); + ui_print("\nRestore complete!\n"); + return 0; +} + +void nandroid_generate_timestamp_path(char* backup_path) +{ + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + if (tmp == NULL) + { + struct timeval tp; + gettimeofday(&tp, NULL); + sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec); + } + else + { + strftime(backup_path, PATH_MAX, "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp); + } +} + +int nandroid_usage() +{ + printf("Usage: nandroid backup\n"); + printf("Usage: nandroid restore \n"); + return 1; +} + +int nandroid_main(int argc, char** argv) +{ + if (argc > 3 || argc < 2) + return nandroid_usage(); + + if (strcmp("backup", argv[1]) == 0) + { + if (argc != 2) + return nandroid_usage(); + + char backup_path[PATH_MAX]; + nandroid_generate_timestamp_path(backup_path); + return nandroid_backup(backup_path); + } + + if (strcmp("restore", argv[1]) == 0) + { + if (argc != 3) + return nandroid_usage(); + return nandroid_restore(argv[2], 1, 1, 1, 1, 1); + } + + return nandroid_usage(); +} diff --git a/nandroid.h b/nandroid.h new file mode 100644 index 0000000..f124e11 --- /dev/null +++ b/nandroid.h @@ -0,0 +1,9 @@ +#ifndef NANDROID_H +#define NANDROID_H + +int nandroid_main(int argc, char** argv); +int nandroid_backup(const char* backup_path); +int nandroid_restore(const char* backup_path, int restore_boot, int restore_system, int restore_data, int restore_cache, int restore_sdext); +void nandroid_generate_timestamp_path(char* backup_path); + +#endif \ No newline at end of file diff --git a/reboot.c b/reboot.c new file mode 100644 index 0000000..04aa9ae --- /dev/null +++ b/reboot.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +int reboot_main(int argc, char *argv[]) +{ + int ret; + int nosync = 0; + int poweroff = 0; + int force = 0; + + opterr = 0; + do { + int c; + + c = getopt(argc, argv, "npf"); + + if (c == EOF) { + break; + } + + switch (c) { + case 'n': + nosync = 1; + break; + case 'p': + poweroff = 1; + break; + case 'f': + force = 1; + break; + case '?': + fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]); + exit(EXIT_FAILURE); + } + } while (1); + + if(argc > optind + 1) { + fprintf(stderr, "%s: too many arguments\n", argv[0]); + exit(EXIT_FAILURE); + } + + if(!nosync) + sync(); + + if(force || argc > optind) { + if(poweroff) + ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL); + else if(argc > optind) + ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, argv[optind]); + else + ret = reboot(RB_AUTOBOOT); + } else { + if(poweroff) { + property_set("ctl.start", "poweroff"); + ret = 0; + } else { + property_set("ctl.start", "reboot"); + ret = 0; + } + } + + if(ret < 0) { + perror("reboot"); + exit(EXIT_FAILURE); + } + fprintf(stderr, "reboot returned\n"); + return 0; +} diff --git a/recovery.c b/recovery.c index 9ad075d..f91edc1 100644 --- a/recovery.c +++ b/recovery.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +30,7 @@ #include #include #include +#include #include "bootloader.h" #include "common.h" @@ -40,6 +42,8 @@ #include "recovery_ui.h" #include "encryptedfs_provisioning.h" +#include "extendedcommands.h" + static const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 's' }, { "update_package", required_argument, NULL, 'u' }, @@ -55,6 +59,8 @@ static const char *INTENT_FILE = "/cache/recovery/intent"; static const char *LOG_FILE = "/cache/recovery/log"; static const char *LAST_LOG_FILE = "/cache/recovery/last_log"; static const char *SDCARD_ROOT = "/sdcard"; +static int allow_display_toggle = 1; +static const char *SDCARD_PACKAGE_FILE = "/sdcard/update.zip"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; @@ -152,6 +158,7 @@ fopen_path(const char *path, const char *mode) { if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1); FILE *fp = fopen(path, mode); + if (fp == NULL && path != COMMAND_FILE) LOGE("Can't open %s\n", path); return fp; } @@ -171,7 +178,9 @@ static void get_args(int *argc, char ***argv) { struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); +#ifndef BOARD_HAS_NO_MISC_PARTITION get_bootloader_message(&boot); // this may fail, leaving a zeroed structure +#endif if (boot.command[0] != 0 && boot.command[0] != 255) { LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command); @@ -181,8 +190,10 @@ get_args(int *argc, char ***argv) { LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status); } + struct stat file_info; + // --- if arguments weren't supplied, look in the bootloader control block - if (*argc <= 1) { + if (*argc <= 1 && 0 != stat("/tmp/.ignorebootmessage", &file_info)) { boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination const char *arg = strtok(boot.recovery, "\n"); if (arg != NULL && !strcmp(arg, "recovery")) { @@ -226,10 +237,13 @@ get_args(int *argc, char ***argv) { strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery)); strlcat(boot.recovery, "\n", sizeof(boot.recovery)); } +#ifndef BOARD_HAS_NO_MISC_PARTITION set_bootloader_message(&boot); +#endif } -static void +#ifndef BOARD_HAS_NO_MISC_PARTITION +void set_sdcard_update_bootloader_message() { struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); @@ -237,6 +251,7 @@ set_sdcard_update_bootloader_message() { strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); set_bootloader_message(&boot); } +#endif // How much of the temp log we have copied to the copy in cache. static long tmplog_offset = 0; @@ -288,10 +303,12 @@ finish_recovery(const char *send_intent) { copy_log_file(LAST_LOG_FILE, false); chmod(LAST_LOG_FILE, 0640); +#ifndef BOARD_HAS_NO_MISC_PARTITION // Reset to mormal system boot so recovery won't cycle indefinitely. struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); set_bootloader_message(&boot); +#endif // Remove the command file, so recovery won't repeat indefinitely. if (ensure_path_mounted(COMMAND_FILE) != 0 || @@ -409,9 +426,8 @@ copy_sideloaded_package(const char* original_path) { } static char** -prepend_title(const char** headers) { - char* title[] = { "Android system recovery <" - EXPAND(RECOVERY_API_VERSION) "e>", +prepend_title(char** headers) { + char* title[] = { EXPAND(RECOVERY_VERSION), "", NULL }; @@ -431,23 +447,30 @@ prepend_title(const char** headers) { return new_headers; } -static int +int get_menu_selection(char** headers, char** items, int menu_only, int initial_selection) { // throw away keys pressed previously, so user doesn't // accidentally trigger menu items. ui_clear_key_queue(); - ui_start_menu(headers, items, initial_selection); + int item_count = ui_start_menu(headers, items, initial_selection); int selected = initial_selection; int chosen_item = -1; - while (chosen_item < 0) { + // Some users with dead enter keys need a way to turn on power to select. + // Jiggering across the wrapping menu is one "secret" way to enable it. + // We can't rely on /cache or /sdcard since they may not be available. + int wrap_count = 0; + + while (chosen_item < 0 && chosen_item != GO_BACK) { int key = ui_wait_key(); int visible = ui_text_visible(); int action = device_handle_key(key, visible); + int old_selected = selected; + if (action < 0) { switch (action) { case HIGHLIGHT_UP: @@ -460,16 +483,40 @@ get_menu_selection(char** headers, char** items, int menu_only, break; case SELECT_ITEM: chosen_item = selected; + if (ui_get_showing_back_button()) { + if (chosen_item == item_count) { + chosen_item = GO_BACK; + } + } break; case NO_ACTION: break; + case GO_BACK: + chosen_item = GO_BACK; + break; } } else if (!menu_only) { chosen_item = action; } + + if (abs(selected - old_selected) > 1) { + wrap_count++; + if (wrap_count == 3) { + wrap_count = 0; + if (ui_get_showing_back_button()) { + ui_print("Back menu button disabled.\n"); + ui_set_showing_back_button(0); + } + else { + ui_print("Back menu button enabled.\n"); + ui_set_showing_back_button(1); + } + } + } } ui_end_menu(); + ui_clear_key_queue(); return chosen_item; } @@ -634,6 +681,10 @@ wipe_data(int confirm) { device_wipe_data(); erase_volume("/data"); erase_volume("/cache"); + erase_volume("/datadata"); + erase_volume("/datadata"); + erase_volume("/sd-ext"); + erase_volume("/sdcard/.android_secure"); ui_print("Data wipe complete.\n"); } @@ -645,7 +696,9 @@ prompt_and_wait() { finish_recovery(NULL); ui_reset_progress(); + allow_display_toggle = 1; int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0); + allow_display_toggle = 0; // device-specific code may take some action here. It may // return one of the core actions handled in the switch @@ -662,16 +715,20 @@ prompt_and_wait() { break; case ITEM_WIPE_CACHE: - ui_print("\n-- Wiping cache...\n"); - erase_volume("/cache"); - ui_print("Cache wipe complete.\n"); - if (!ui_text_visible()) return; + if (confirm_selection("Confirm wipe?", "Yes - Wipe Cache")) + { + ui_print("\n-- Wiping cache...\n"); + erase_volume("/cache"); + ui_print("Cache wipe complete.\n"); + if (!ui_text_visible()) return; + } break; case ITEM_APPLY_SDCARD: - ; - int status = sdcard_directory(SDCARD_ROOT); - if (status >= 0) { + if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip")) + { + ui_print("\n-- Install from sdcard...\n"); + int status = install_package(SDCARD_PACKAGE_FILE); if (status != INSTALL_SUCCESS) { ui_set_background(BACKGROUND_ICON_ERROR); ui_print("Installation aborted.\n"); @@ -682,6 +739,18 @@ prompt_and_wait() { } } break; + case ITEM_INSTALL_ZIP: + show_install_update_menu(); + break; + case ITEM_NANDROID: + show_nandroid_menu(); + break; + case ITEM_PARTITION: + show_partition_menu(); + break; + case ITEM_ADVANCED: + show_advanced_menu(); + break; } } } @@ -693,6 +762,29 @@ print_property(const char *key, const char *name, void *cookie) { int main(int argc, char **argv) { + if (strstr(argv[0], "recovery") == NULL) + { + if (strstr(argv[0], "flash_image") != NULL) + return flash_image_main(argc, argv); + if (strstr(argv[0], "dump_image") != NULL) + return dump_image_main(argc, argv); + if (strstr(argv[0], "erase_image") != NULL) + return erase_image_main(argc, argv); + if (strstr(argv[0], "mkyaffs2image") != NULL) + return mkyaffs2image_main(argc, argv); + if (strstr(argv[0], "unyaffs") != NULL) + return unyaffs_main(argc, argv); + if (strstr(argv[0], "nandroid")) + return nandroid_main(argc, argv); + if (strstr(argv[0], "reboot")) + return reboot_main(argc, argv); + if (strstr(argv[0], "setprop")) + return setprop_main(argc, argv); + return busybox_driver(argc, argv); + } + __system("/sbin/postrecoveryboot.sh"); + + int is_user_initiated_recovery = 0; time_t start = time(NULL); // If these fail, there's not really anywhere to complain... @@ -701,7 +793,7 @@ main(int argc, char **argv) { printf("Starting recovery on %s", ctime(&start)); ui_init(); - ui_set_background(BACKGROUND_ICON_INSTALLING); + ui_print(EXPAND(RECOVERY_VERSION)"\n"); load_volume_table(); get_args(&argc, &argv); @@ -757,7 +849,7 @@ main(int argc, char **argv) { printf("\n"); int status = INSTALL_SUCCESS; - + if (toggle_secure_fs) { if (strcmp(encrypted_fs_mode,"on") == 0) { encrypted_fs_data.mode = MODE_ENCRYPTED_FS_ENABLED; @@ -806,10 +898,31 @@ main(int argc, char **argv) { if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n"); } else { + LOGI("Checking for extendedcommand...\n"); status = INSTALL_ERROR; // No command specified + // we are starting up in user initiated recovery here + // let's set up some default options + signature_check_enabled = 0; + script_assert_enabled = 0; + is_user_initiated_recovery = 1; + ui_set_show_text(1); + + if (extendedcommand_file_exists()) { + LOGI("Running extendedcommand...\n"); + int ret; + if (0 == (ret = run_and_remove_extendedcommand())) { + status = INSTALL_SUCCESS; + ui_set_show_text(0); + } + else { + handle_failure(ret); + } + } else { + LOGI("Skipping execution of extendedcommand, file not found...\n"); + } } - if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR); + if (status != INSTALL_SUCCESS && !is_user_initiated_recovery) ui_set_background(BACKGROUND_ICON_ERROR); if (status != INSTALL_SUCCESS || ui_text_visible()) { prompt_and_wait(); } @@ -821,3 +934,7 @@ main(int argc, char **argv) { reboot(RB_AUTOBOOT); return EXIT_SUCCESS; } + +int get_allow_toggle_display() { + return allow_display_toggle; +} diff --git a/recovery_ui.h b/recovery_ui.h index e451bdf..f34365f 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -64,11 +64,16 @@ int device_wipe_data(); #define HIGHLIGHT_UP -2 #define HIGHLIGHT_DOWN -3 #define SELECT_ITEM -4 +#define GO_BACK -5 #define ITEM_REBOOT 0 #define ITEM_APPLY_SDCARD 1 #define ITEM_WIPE_DATA 2 #define ITEM_WIPE_CACHE 3 +#define ITEM_INSTALL_ZIP 4 +#define ITEM_NANDROID 5 +#define ITEM_PARTITION 6 +#define ITEM_ADVANCED 7 // Header text to display above the main menu. extern char* MENU_HEADERS[]; @@ -76,4 +81,10 @@ extern char* MENU_HEADERS[]; // Text of menu items. extern char* MENU_ITEMS[]; +int +get_menu_selection(char** headers, char** items, int menu_only, int initial_selection); + +void +set_sdcard_update_bootloader_message(); + #endif diff --git a/roots.c b/roots.c index 90b82d7..184bfd0 100644 --- a/roots.c +++ b/roots.c @@ -23,7 +23,7 @@ #include #include "mtdutils/mtdutils.h" -#include "mtdutils/mounts.h" +#include "mounts.h" #include "roots.h" #include "common.h" #include "make_ext4fs.h" diff --git a/setprop.c b/setprop.c new file mode 100644 index 0000000..63ad2b4 --- /dev/null +++ b/setprop.c @@ -0,0 +1,18 @@ +#include + +#include + +int setprop_main(int argc, char *argv[]) +{ + if(argc != 3) { + fprintf(stderr,"usage: setprop \n"); + return 1; + } + + if(property_set(argv[1], argv[2])){ + fprintf(stderr,"could not set property\n"); + return 1; + } + + return 0; +} diff --git a/ui.c b/ui.c index 84a9b85..d973a50 100644 --- a/ui.c +++ b/ui.c @@ -29,11 +29,25 @@ #include "minui/minui.h" #include "recovery_ui.h" +#ifdef BOARD_HAS_NO_SELECT_BUTTON +static int gShowBackButton = 1; +#else +static int gShowBackButton = 0; +#endif + #define MAX_COLS 96 #define MAX_ROWS 32 -#define CHAR_WIDTH 10 -#define CHAR_HEIGHT 18 +#define MENU_MAX_COLS 64 +#define MENU_MAX_ROWS 250 + +#ifndef BOARD_LDPI_RECOVERY + #define CHAR_WIDTH 10 + #define CHAR_HEIGHT 18 +#else + #define CHAR_WIDTH 7 + #define CHAR_HEIGHT 16 +#endif #define PROGRESSBAR_INDETERMINATE_STATES 6 #define PROGRESSBAR_INDETERMINATE_FPS 15 @@ -43,6 +57,7 @@ static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS]; static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES]; static gr_surface gProgressBarEmpty; static gr_surface gProgressBarFill; +static int ui_has_initialized = 0; static const struct { gr_surface* surface; const char *name; } BITMAPS[] = { { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" }, @@ -79,9 +94,10 @@ static int text_cols = 0, text_rows = 0; static int text_col = 0, text_row = 0, text_top = 0; static int show_text = 0; -static char menu[MAX_ROWS][MAX_COLS]; +static char menu[MENU_MAX_ROWS][MENU_MAX_COLS]; static int show_menu = 0; static int menu_top = 0, menu_items = 0, menu_sel = 0; +static int menu_show_start = 0; // this is line which menu display is starting at // Key event input queue static pthread_mutex_t key_queue_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -148,10 +164,15 @@ static void draw_text_line(int row, const char* t) { } } +#define MENU_TEXT_COLOR 7, 133, 74, 255 +#define NORMAL_TEXT_COLOR 200, 200, 200, 255 +#define HEADER_TEXT_COLOR NORMAL_TEXT_COLOR + // Redraw everything on the screen. Does not flip pages. // Should only be called with gUpdateMutex locked. static void draw_screen_locked(void) { + if (!ui_has_initialized) return; draw_background_locked(gCurrentIcon); draw_progress_locked(); @@ -160,29 +181,43 @@ static void draw_screen_locked(void) gr_fill(0, 0, gr_fb_width(), gr_fb_height()); int i = 0; + int j = 0; + int row = 0; // current row that we are drawing on if (show_menu) { - gr_color(64, 96, 255, 255); - gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT, - gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1); + gr_color(MENU_TEXT_COLOR); + gr_fill(0, (menu_top + menu_sel - menu_show_start) * CHAR_HEIGHT, + gr_fb_width(), (menu_top + menu_sel - menu_show_start + 1)*CHAR_HEIGHT+1); - for (; i < menu_top + menu_items; ++i) { + gr_color(HEADER_TEXT_COLOR); + for (i = 0; i < menu_top; ++i) { + draw_text_line(i, menu[i]); + row++; + } + + if (menu_items - menu_show_start + menu_top >= MAX_ROWS) + j = MAX_ROWS - menu_top; + else + j = menu_items - menu_show_start; + + gr_color(MENU_TEXT_COLOR); + for (i = menu_show_start + menu_top; i < (menu_show_start + menu_top + j); ++i) { if (i == menu_top + menu_sel) { gr_color(255, 255, 255, 255); - draw_text_line(i, menu[i]); - gr_color(64, 96, 255, 255); + draw_text_line(i - menu_show_start , menu[i]); + gr_color(MENU_TEXT_COLOR); } else { - draw_text_line(i, menu[i]); + gr_color(MENU_TEXT_COLOR); + draw_text_line(i - menu_show_start, menu[i]); } + row++; } - gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1, - gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1); - ++i; + gr_fill(0, row*CHAR_HEIGHT+CHAR_HEIGHT/2-1, + gr_fb_width(), row*CHAR_HEIGHT+CHAR_HEIGHT/2+1); } - gr_color(255, 255, 0, 255); - - for (; i < text_rows; ++i) { - draw_text_line(i, text[(i+text_top) % text_rows]); + gr_color(NORMAL_TEXT_COLOR); + for (; row < text_rows; ++row) { + draw_text_line(row, text[(row+text_top) % text_rows]); } } } @@ -191,6 +226,7 @@ static void draw_screen_locked(void) // Should only be called with gUpdateMutex locked. static void update_screen_locked(void) { + if (!ui_has_initialized) return; draw_screen_locked(); gr_flip(); } @@ -199,6 +235,7 @@ static void update_screen_locked(void) // Should only be called with gUpdateMutex locked. static void update_progress_locked(void) { + if (!ui_has_initialized) return; if (show_text || !gPagesIdentical) { draw_screen_locked(); // Must redraw the whole screen gPagesIdentical = 1; @@ -308,6 +345,7 @@ static void *input_thread(void *cookie) void ui_init(void) { + ui_has_initialized = 1; gr_init(); ev_init(); @@ -337,6 +375,23 @@ void ui_init(void) pthread_create(&t, NULL, input_thread, NULL); } +char *ui_copy_image(int icon, int *width, int *height, int *bpp) { + pthread_mutex_lock(&gUpdateMutex); + draw_background_locked(gBackgroundIcon[icon]); + *width = gr_fb_width(); + *height = gr_fb_height(); + *bpp = sizeof(gr_pixel) * 8; + int size = *width * *height * sizeof(gr_pixel); + char *ret = malloc(size); + if (ret == NULL) { + LOGE("Can't allocate %d bytes for image\n", size); + } else { + memcpy(ret, gr_fb_data(), size); + } + pthread_mutex_unlock(&gUpdateMutex); + return ret; +} + void ui_set_background(int icon) { pthread_mutex_lock(&gUpdateMutex); @@ -425,7 +480,17 @@ void ui_print(const char *fmt, ...) pthread_mutex_unlock(&gUpdateMutex); } -void ui_start_menu(char** headers, char** items, int initial_selection) { +void ui_reset_text_col() +{ + pthread_mutex_lock(&gUpdateMutex); + text_col = 0; + pthread_mutex_unlock(&gUpdateMutex); +} + +#define MENU_ITEM_HEADER " - " +#define MENU_ITEM_HEADER_LENGTH strlen(MENU_ITEM_HEADER) + +int ui_start_menu(char** headers, char** items, int initial_selection) { int i; pthread_mutex_lock(&gUpdateMutex); if (text_rows > 0 && text_cols > 0) { @@ -435,17 +500,28 @@ void ui_start_menu(char** headers, char** items, int initial_selection) { menu[i][text_cols-1] = '\0'; } menu_top = i; - for (; i < text_rows; ++i) { + for (; i < MENU_MAX_ROWS; ++i) { if (items[i-menu_top] == NULL) break; - strncpy(menu[i], items[i-menu_top], text_cols-1); + strcpy(menu[i], MENU_ITEM_HEADER); + strncpy(menu[i] + MENU_ITEM_HEADER_LENGTH, items[i-menu_top], text_cols-1 - MENU_ITEM_HEADER_LENGTH); menu[i][text_cols-1] = '\0'; } + + if (gShowBackButton) { + strcpy(menu[i], " - +++++Go Back+++++"); + ++i; + } + menu_items = i - menu_top; show_menu = 1; - menu_sel = initial_selection; + menu_sel = menu_show_start = initial_selection; update_screen_locked(); } pthread_mutex_unlock(&gUpdateMutex); + if (gShowBackButton) { + return menu_items - 1; + } + return menu_items; } int ui_menu_select(int sel) { @@ -454,9 +530,21 @@ int ui_menu_select(int sel) { if (show_menu > 0) { old_sel = menu_sel; menu_sel = sel; - if (menu_sel < 0) menu_sel = 0; - if (menu_sel >= menu_items) menu_sel = menu_items-1; + + if (menu_sel < 0) menu_sel = menu_items + menu_sel; + if (menu_sel >= menu_items) menu_sel = menu_sel - menu_items; + + + if (menu_sel < menu_show_start && menu_show_start > 0) { + menu_show_start = menu_sel; + } + + if (menu_sel - menu_show_start + menu_top >= text_rows) { + menu_show_start = menu_sel + menu_top - text_rows + 1; + } + sel = menu_sel; + if (menu_sel != old_sel) update_screen_locked(); } pthread_mutex_unlock(&gUpdateMutex); @@ -513,3 +601,15 @@ void ui_clear_key_queue() { key_queue_len = 0; pthread_mutex_unlock(&key_queue_mutex); } + +void ui_set_show_text(int value) { + show_text = value; +} + +void ui_set_showing_back_button(int showBackButton) { + gShowBackButton = showBackButton; +} + +int ui_get_showing_back_button() { + return gShowBackButton; +} diff --git a/updater/Android.mk b/updater/Android.mk index dcc6a49..5c43e1c 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -4,6 +4,7 @@ LOCAL_PATH := $(call my-dir) updater_src_files := \ install.c \ + ../mounts.c \ updater.c # @@ -24,6 +25,8 @@ LOCAL_C_INCLUDES += system/extras/ext4_utils LOCAL_STATIC_LIBRARIES += libext4_utils libz endif +LOCAL_STATIC_LIBRARIES += libflashutils libmtdutils libmmcutils libbmlutils + LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz LOCAL_STATIC_LIBRARIES += libmincrypt libbz @@ -70,3 +73,9 @@ LOCAL_MODULE := updater LOCAL_FORCE_STATIC_EXECUTABLE := true include $(BUILD_EXECUTABLE) + + +file := $(PRODUCT_OUT)/utilities/update-binary +ALL_PREBUILT += $(file) +$(file) : $(TARGET_OUT)/bin/updater | $(ACP) + $(transform-prebuilt-to-target) diff --git a/updater/install.c b/updater/install.c index a596dc6..478055d 100644 --- a/updater/install.c +++ b/updater/install.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +32,10 @@ #include "edify/expr.h" #include "mincrypt/sha.h" #include "minzip/DirUtil.h" -#include "mtdutils/mounts.h" +#include "mounts.h" +#include "flashutils/flashutils.h" #include "mtdutils/mtdutils.h" +#include "mmcutils/mmcutils.h" #include "updater.h" #include "applypatch/applypatch.h" @@ -78,23 +81,11 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { mkdir(mount_point, 0755); - if (strcmp(partition_type, "MTD") == 0) { - mtd_scan_partitions(); - const MtdPartition* mtd; - mtd = mtd_find_partition_by_name(location); - if (mtd == NULL) { - fprintf(stderr, "%s: no mtd partition named \"%s\"", - name, location); + if (strcmp(partition_type, "MTD") == 0 || strcmp(partition_type, "MMC") == 0) { + if (0 == mount_partition(location, mount_point, get_default_filesystem(), 0)) + result = mount_point; + else result = strdup(""); - goto done; - } - if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) { - fprintf(stderr, "mtd mount of %s failed: %s\n", - location, strerror(errno)); - result = strdup(""); - goto done; - } - result = mount_point; } else { if (mount(location, mount_point, fs_type, MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) { @@ -204,33 +195,11 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } - if (strcmp(partition_type, "MTD") == 0) { - mtd_scan_partitions(); - const MtdPartition* mtd = mtd_find_partition_by_name(location); - if (mtd == NULL) { - fprintf(stderr, "%s: no mtd partition named \"%s\"", - name, location); + if (strcmp(partition_type, "MTD") == 0 || strcmp(partition_type, "MMC") == 0) { + if (0 != erase_partition(location, NULL)) { result = strdup(""); goto done; } - MtdWriteContext* ctx = mtd_write_partition(mtd); - if (ctx == NULL) { - fprintf(stderr, "%s: can't write \"%s\"", name, location); - result = strdup(""); - goto done; - } - if (mtd_erase_blocks(ctx, -1) == -1) { - mtd_write_close(ctx); - fprintf(stderr, "%s: failed to erase \"%s\"", name, location); - result = strdup(""); - goto done; - } - if (mtd_write_close(ctx) != 0) { - fprintf(stderr, "%s: failed to close \"%s\"", name, location); - result = strdup(""); - goto done; - } - result = location; #ifdef USE_EXT4 } else if (strcmp(fs_type, "ext4") == 0) { reset_ext4fs_info(); @@ -247,7 +216,7 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { fprintf(stderr, "%s: unsupported fs_type \"%s\" partition_type \"%s\"", name, fs_type, partition_type); } - + result = location; done: free(fs_type); free(partition_type); @@ -680,57 +649,10 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } - mtd_scan_partitions(); - const MtdPartition* mtd = mtd_find_partition_by_name(partition); - if (mtd == NULL) { - fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition); + if (0 == restore_raw_partition(partition, filename)) + result = strdup(partition); + else result = strdup(""); - goto done; - } - - MtdWriteContext* ctx = mtd_write_partition(mtd); - if (ctx == NULL) { - fprintf(stderr, "%s: can't write mtd partition \"%s\"\n", - name, partition); - result = strdup(""); - goto done; - } - - bool success; - - FILE* f = fopen(filename, "rb"); - if (f == NULL) { - fprintf(stderr, "%s: can't open %s: %s\n", - name, filename, strerror(errno)); - result = strdup(""); - goto done; - } - - success = true; - char* buffer = malloc(BUFSIZ); - int read; - while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) { - int wrote = mtd_write_data(ctx, buffer, read); - success = success && (wrote == read); - if (!success) { - fprintf(stderr, "mtd_write_data to %s failed: %s\n", - partition, strerror(errno)); - } - } - free(buffer); - fclose(f); - - if (mtd_erase_blocks(ctx, -1) == -1) { - fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition); - } - if (mtd_write_close(ctx) != 0) { - fprintf(stderr, "%s: error closing write of %s\n", name, partition); - } - - printf("%s %s partition from %s\n", - success ? "wrote" : "failed to write", partition, filename); - - result = success ? partition : strdup(""); done: if (result != partition) free(partition); diff --git a/utilities/Android.mk b/utilities/Android.mk new file mode 100644 index 0000000..f6b6e64 --- /dev/null +++ b/utilities/Android.mk @@ -0,0 +1,45 @@ +LOCAL_PATH := $(call my-dir) + +ifndef BOARD_HAS_SMALL_RECOVERY + +include $(CLEAR_VARS) +LOCAL_MODULE := e2fsck +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := $(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := fix_permissions +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := $(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := parted +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := $(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := sdparted +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := $(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := tune2fs +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := $(LOCAL_MODULE) +include $(BUILD_PREBUILT) + +endif diff --git a/utilities/e2fsck b/utilities/e2fsck new file mode 100644 index 0000000..2844a1d Binary files /dev/null and b/utilities/e2fsck differ diff --git a/utilities/fix_permissions b/utilities/fix_permissions new file mode 100644 index 0000000..a6db514 --- /dev/null +++ b/utilities/fix_permissions @@ -0,0 +1,484 @@ +#! /system/bin/sh +# +# Warning: if you want to run this script in cm-recovery change the above to #!/sbin/sh +# +# fix_permissions - fixes permissions on Android data directories after upgrade +# shade@chemlab.org +# +# original concept: http://blog.elsdoerfer.name/2009/05/25/android-fix-package-uid-mismatches/ +# implementation by: Cyanogen +# improved by: ankn, smeat, thenefield, farmatito, rikupw, Kastro +# +# v1.1-v1.31r3 - many improvements and concepts from XDA developers. +# v1.34 through v2.00 - A lot of frustration [by Kastro] +# v2.01 - Completely rewrote the script for SPEED, thanks for the input farmatito +# /data/data depth recursion is tweaked; +# fixed single mode; +# functions created for modularity; +# logging can be disabled via CLI for more speed; +# runtime computation added to end (Runtime: mins secs); +# progress (current # of total) added to screen; +# fixed CLI argument parsing, now you can have more than one option!; +# debug cli option; +# verbosity can be disabled via CLI option for less noise;; +# [by Kastro, (XDA: k4str0), twitter;mattcarver] +# v2.02 - ignore com.htc.resources.apk if it exists and minor code cleanups, +# fix help text, implement simulated run (-s) [farmatito] +# v2.03 - fixed chown group ownership output [Kastro] +# v2.04 - replaced /system/sd with $SD_EXT_DIRECTORY [Firerat] +VERSION="2.04" + +# Defaults +DEBUG=0 # Debug off by default +LOGGING=1 # Logging on by default +VERBOSE=1 # Verbose on by default + +# Messages +UID_MSG="Changing user ownership for:" +GID_MSG="Changing group ownership for:" +PERM_MSG="Changing permissions for:" + +# Programs needed +ECHO="busybox echo" +GREP="busybox grep" +EGREP="busybox egrep" +CAT="busybox cat" +CHOWN="busybox chown" +CHMOD="busybox chmod" +MOUNT="busybox mount" +UMOUNT="busybox umount" +CUT="busybox cut" +FIND="busybox find" +LS="busybox ls" +TR="busybox tr" +TEE="busybox tee" +TEST="busybox test" +SED="busybox sed" +RM="busybox rm" +WC="busybox wc" +EXPR="busybox expr" +DATE="busybox date" + +# Initialise vars +CODEPATH="" +UID="" +GID="" +PACKAGE="" +REMOVE=0 +NOSYSTEM=0 +ONLY_ONE="" +SIMULATE=0 +SYSREMOUNT=0 +SYSMOUNT=0 +DATAMOUNT=0 +SYSSDMOUNT=0 +FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" ) +FP_STARTEPOCH=$( $DATE +%s ) +if $TEST "$SD_EXT_DIRECTORY" = ""; then + #check for mount point, /system/sd included in tests for backward compatibility + for MP in /sd-ext /system/sd;do + if $TEST -d $MP; then + SD_EXT_DIRECTORY=$MP + break + fi + done +fi +fp_usage() +{ + $ECHO "Usage $0 [OPTIONS] [APK_PATH]" + $ECHO " -d turn on debug" + $ECHO " -f fix only package APK_PATH" + $ECHO " -l disable logging for this run (faster)" + $ECHO " -r remove stale data directories" + $ECHO " of uninstalled packages while fixing permissions" + $ECHO " -s simulate only" + $ECHO " -u check only non-system directories" + $ECHO " -v disable verbosity for this run (less output)" + $ECHO " -V print version" + $ECHO " -h this help" +} + +fp_parseargs() +{ + # Parse options + while $TEST $# -ne 0; do + case "$1" in + -d) + DEBUG=1 + ;; + -f) + if $TEST $# -lt 2; then + $ECHO "$0: missing argument for option $1" + exit 1 + else + if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then + ONLY_ONE=$2 + shift; + else + $ECHO "$0: missing argument for option $1" + exit 1 + fi + fi + ;; + -r) + REMOVE=1 + ;; + -s) + SIMULATE=1 + ;; + -l) + if $TEST $LOGGING -eq 0; then + LOGGING=1 + else + LOGGING=0 + fi + ;; + -v) + if $TEST $VERBOSE -eq 0; then + VERBOSE=1 + else + VERBOSE=0 + fi + ;; + -u) + NOSYSTEM=1 + ;; + -V) + $ECHO "$0 $VERSION" + exit 0 + ;; + -h) + fp_usage + exit 0 + ;; + -*) + $ECHO "$0: unknown option $1" + $ECHO + fp_usage + exit 1 + ;; + esac + shift; + done +} + +fp_print() +{ + MSG=$@ + if $TEST $LOGGING -eq 1; then + $ECHO $MSG | $TEE -a $LOG_FILE + else + $ECHO $MSG + fi +} + +fp_start() +{ + if $TEST $SIMULATE -eq 0 ; then + if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then + DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 ) + if $TEST $DEBUG -eq 1; then + fp_print "/system mounted on $DEVICE" + fi + if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then + $MOUNT -o remount,rw $DEVICE /system + SYSREMOUNT=1 + fi + else + $MOUNT /system > /dev/null 2>&1 + SYSMOUNT=1 + fi + + if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then + $MOUNT /data > /dev/null 2>&1 + DATAMOUNT=1 + fi + + if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " $SD_EXT_DIRECTORY " "/proc/mounts" ) -eq 0; then + $MOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1 + SYSSDMOUNT=1 + fi + fi + if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then + LOG_FILE="/data/fix_permissions.log" + else + LOG_FILE="/sdcard/fix_permissions.log" + fi + if $TEST ! -e "$LOG_FILE"; then + > $LOG_FILE + fi + + fp_print "$0 $VERSION started at $FP_STARTTIME" +} + +fp_chown_uid() +{ + FP_OLDUID=$1 + FP_UID=$2 + FP_FILE=$3 + + #if user ownership doesn't equal then change them + if $TEST "$FP_OLDUID" != "$FP_UID"; then + if $TEST $VERBOSE -ne 0; then + fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'" + fi + if $TEST $SIMULATE -eq 0; then + $CHOWN $FP_UID "$FP_FILE" + fi + fi +} + +fp_chown_gid() +{ + FP_OLDGID=$1 + FP_GID=$2 + FP_FILE=$3 + + #if group ownership doesn't equal then change them + if $TEST "$FP_OLDGID" != "$FP_GID"; then + if $TEST $VERBOSE -ne 0; then + fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'" + fi + if $TEST $SIMULATE -eq 0; then + $CHOWN :$FP_GID "$FP_FILE" + fi + fi +} + +fp_chmod() +{ + FP_OLDPER=$1 + FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 ) + FP_PERSTR=$2 + FP_PERNUM=$3 + FP_FILE=$4 + + #if the permissions are not equal + if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then + if $TEST $VERBOSE -ne 0; then + fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)" + fi + #change the permissions + if $TEST $SIMULATE -eq 0; then + $CHMOD $FP_PERNUM "$FP_FILE" + fi + fi +} + +fp_all() +{ + FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^ /dev/null 2>&1 + fi + + if $TEST $SYSSDMOUNT -eq 1; then + $UMOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1 + fi + + if $TEST $SYSMOUNT -eq 1; then + $UMOUNT /system > /dev/null 2>&1 + fi + + if $TEST $DATAMOUNT -eq 1; then + $UMOUNT /data > /dev/null 2>&1 + fi + + FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" ) + FP_ENDEPOCH=$( $DATE +%s ) + + date_diff $FP_STARTEPOCH $FP_ENDEPOCH + + fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)" +} + +#MAIN SCRIPT + +fp_parseargs $@ +fp_start +if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then + fp_single "$ONLY_ONE" +else + fp_all +fi +fp_end diff --git a/utilities/parted b/utilities/parted new file mode 100644 index 0000000..bb3d432 Binary files /dev/null and b/utilities/parted differ diff --git a/utilities/sdparted b/utilities/sdparted new file mode 100644 index 0000000..d2a46a4 --- /dev/null +++ b/utilities/sdparted @@ -0,0 +1,637 @@ +#!/sbin/sh + +# do logging, if not excluded with -x +LOGFILE="/data/sdparted.log" +[ "$1" != "-x" ] && echo "$0" "$@" >> "$LOGFILE" && "$0" -x "$@" 2>&1 | tee -a "$LOGFILE" && exit +shift + +ShowError() { echo ; echo " err: $1" ; echo ; exit 1 ; } + +ShowMessage() { echo ; echo " msg: $1" ; } + +ShowHelp() { + +cat <. + default=total sdcard size - (ext + swap) + + --extsize|-es SIZE[MG] set the size of the ext partition to . + default=$EXTSIZE + + --swapsize|-ss SIZE[MG] set the size of the swap partition to . + if set to 0, no swap partition will be created. + default=$SWAPSIZE + + --extfs|-efs TYPE set the filesystem of ext partition to . + valid types=ext2, ext3, ext4 + default=$EXTFS + + + --upgradefs|-ufs TYPE upgrades existing ext partition to . + 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 . + this operation will NOT wipe your sdcard and + cannot be used with any partition creation options. + valid types=ext2 + + + --interactive|-i interactive mode + + --help|-h display this help + + --printonly|-po display sdcard information + + --silent|-s do not prompt user, not even initial warning. + + +examples: + $SCRIPTNAME creates swap=$SWAPSIZE ext2=$EXTSIZE fat32=remaining free space + $SCRIPTNAME -efs ext4 creates swap=$SWAPSIZE ext4=$EXTSIZE fat32=remaining free space + $SCRIPTNAME -fs 1.5G -efs ext3 creates swap=$SWAPSIZE ext3=$EXTSIZE fat32=1536 + $SCRIPTNAME -es 256M -ss 0 creates no swap ext2=256 fat32=remaining free space + $SCRIPTNAME -ufs ext4 upgrades ext partition to ext4 + +DONEHELP + +} + +UserAbort() { + + WHILEEXIT= + + while [ -z "$WHILEEXIT" ] + do + echo -n "do you want to continue? (Y/n) " + read response + echo + [ "$response" = "Y" ] || [ "$response" = "n" ] || [ "$response" = "N" ] && WHILEEXIT="$response" + done + + echo "$response" > /dev/null 2>&1 >>"$LOGFILE" + + [ "$response" != "Y" ] + +} + +UnmountAll () { + + # unmount all partitions so we can work with $SDPATH + # i'm assuming no more than 3 partitions + # maybe make a little more elegant later + echo -n "unmounting all partitions..." + umount "$FATPATH" > /dev/null 2>&1 >>"$LOGFILE" + umount "$EXTPATH" > /dev/null 2>&1 >>"$LOGFILE" + umount "$SWAPPATH" > /dev/null 2>&1 >>"$LOGFILE" + echo "done" + echo + +} + + +CheckReqs() { + + echo -n "checking script requirements..." + # check for valid sdcard + [ -e $SDPATH ] || ShowError "$SDPATH does not exist!" + + # look for necessary programs + [ -e $CMPARTED ] || ShowError "$CMPARTED does not exist!" + [ -e $CMTUNE2FS ] || ShowError "$CMTUNE2FS does not exist!" + [ -e $CME2FSCK ] || ShowError "$CME2FSCK does not exist!" + + # verify cm-v1.4 + PARTEDREV=`"$CMPARTED" "$SDPATH" version | grep Parted | cut -d" " -f3` + [ "$PARTEDREV" == "1.8.8.1.179-aef3" ] || ShowError "you are not using parted v1.8.8.1.179-aef3!" + echo "done" + echo + +} + +CheckTableType() { + + TABLETYPE=`"$CMPARTED" "$SDPATH" print | grep Table: | cut -d" " -f3` + + [ "$TABLETYPE" == "loop" -o "$TABLETYPE" == "msdos" ] && TTISOK=1 || TTISOK=0 + [ "$TABLETYPE" == "loop" ] && TTISLOOP=1 || TTISLOOP=0 + [ "$TABLETYPE" == "msdos" ] && TTISMSDOS=1 || TTISMOSDOS=0 + +} + +ValidateExtArg() { + + FUNC_RET="nonzero" + + # validating argument + [ "$1" != "ext2" ] && [ "$1" != "ext3" ] && [ "$1" != "ext4" ] && FUNC_RET= + + [ -z "$FUNC_RET" ] && [ -z "$IMODE" ] && ShowError "$1 is not a valid filesystem." + [ -z "$FUNC_RET" ] && [ -n "$IMODE" ] && ShowMessage "$1 is not a valid filesystem." + + # return valid argument + [ -n "$FUNC_RET" ] && FUNC_RET="$1" + +} + +ValidateSizeArg() { + + # check for zero-length arg to protect expr length + [ -z "$1" ] && ShowError "zero-length argument passed to size-validator" + + SIZEMB= + ARGLEN=`expr length $1` + SIZELEN=$(($ARGLEN-1)) + SIZEARG=`expr substr $1 1 $SIZELEN` + SIZEUNIT=`expr substr $1 $ARGLEN 1` + + # check if SIZEARG is an integer + if [ $SIZEARG -eq $SIZEARG 2> /dev/null ] ; then + # look for G + [ "$SIZEUNIT" == "G" ] && SIZEMB=$(($SIZEARG * 1024)) + # look for M + [ "$SIZEUNIT" == "M" ] && SIZEMB=$SIZEARG + # no units on arg AND prevents using bogus size units + [ -z "$SIZEMB" ] && [ $SIZEUNIT -eq $SIZEUNIT 2> /dev/null ] && SIZEMB=$1 + # check if SIZEARG is a floating point number, GB only + elif [ `expr index "$SIZEARG" .` != 0 ] && [ "$SIZEUNIT" == "G" ] ; then + INT=`echo "$SIZEARG" | cut -d"." -f1` + FRAC=`echo "$SIZEARG" | cut -d"." -f2` + SIGDIGITS=`expr length $FRAC` + + [ -z "$INT" ] && INT=0 + INTMB=$(($INT * 1024)) + FRACMB=$((($FRAC * 1024) / (10**$SIGDIGITS))) + SIZEMB=$(($INTMB + $FRACMB)) + # it's not a valid size + else + [ -z "$IMODE" ] && ShowError "$1 is not a valid size" + fi + + [ -z "$SIZEMB" ] && [ -n "$IMODE" ] && ShowMessage "$1 is not a valid size" + + # return valid argument in MB + FUNC_RET=$SIZEMB + +} + +CalculatePartitions() { + + # get size of sdcard in MB & do some math + SDSIZEMB=`"$CMPARTED" "$SDPATH" unit MB print | grep $SDPATH | cut -d" " -f3` + SDSIZE=${SDSIZEMB%MB} + [ -n "$FATSIZE" ] || FATSIZE=$(($SDSIZE - $EXTSIZE - $SWAPSIZE)) + EXTEND=$(($FATSIZE + $EXTSIZE)) + SWAPEND=$(($EXTEND + $SWAPSIZE)) + + # check for fatsize of 0 + [ $FATSIZE -le 0 ] && ShowError "must have a fat32 partition greater than 0MB" + + # check for zero-length sdsize... + # indicative of parted not reporting length + # correctly b/c of error on sdcard + [ -z "$SDSIZE" ] && ShowError "zero-length argument passed to partition-calculator" + + # make sure we're not being asked to do the impossible + [ $(($FATSIZE + $EXTSIZE + $SWAPSIZE)) -gt $SDSIZE ] && [ -z "$IMODE" ] && ShowError "sum of requested partitions is greater than sdcard size" + +} + + +UpgradeDowngradeOnly() { + + if [ -n "$UEXTFSONLY" ] ; then + echo + [ -n "$CREATEPART" ] && ShowError "cannot use upgrade option when creating partitions, use -efs instead" + [ -n "$DEXTFSONLY" ] && ShowError "cannot upgrade AND downgrade, it just doesn't make sense" + echo "you have chosen to upgrade $EXTPATH to $UEXTFSONLY." + echo "this action will NOT delete any data from sdcard." + echo + [ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user" + echo + UpgradeExt "$UEXTFSONLY" + ShowCardInfo + elif [ -n "$DEXTFSONLY" ] ; then + echo + [ -n "$CREATEPART" ] && ShowError "cannot use downgrade option when creating partitions." + [ -n "$UEXTFSONLY" ] && ShowError "cannot downgrade AND upgrade, it just doesn't make sense." + echo "you have chosen to downgrade $EXTPATH to $DEXTFSONLY." + echo "this action will NOT delete any data from sdcard." + echo + [ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user" + echo + DowngradeExt "$DEXTFSONLY" + ShowCardInfo + fi + +} + +PrepareSdCard() { + + echo + if [ $TTISOK -eq 0 ] ; then + echo "partition 1 may not be aligned to cylinder boundaries." + echo "to continue, this must be corrected." + elif [ $TTISLOOP -gt 0 ] ; then + echo "your sdcard's partition table type is $TABLETYPE." + echo "to continue, partition table must be set to 'msdos'." + elif [ $TTISMSDOS -gt 0 ] ; then + # just a reminder..in a later version, + # i may implement resizing of partitions, + # so this will be unnecessary. but until then... + echo "to continue, all existing partitions must be removed." + else + # this is not good, and should never happen + # if it does, there is a serious problem + ShowError "sdcard failed table type check." + fi + + echo + echo "this action will remove all data from your sdcard." + echo + [ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user" + + [ $TTISOK -eq 0 ] && echo -n "correcting cylinder boundaries..." + [ $TTISLOOP -gt 0 ] && echo -n "setting partition table to msdos..." + [ $TTISMSDOS -gt 0 ] && echo -n "removing all partitions..." + + "$CMPARTED" -s "$SDPATH" mklabel msdos 2>&1 >>"$LOGFILE" + echo "done" + echo + +} + +ShowActions() { + + echo + echo "total size of sdcard=$SDSIZEMB" + echo + echo "the following actions will be performed:" + echo " -create $FATSIZE""MB fat32 partition" + [ $EXTSIZE -gt 0 ] && echo " -create $EXTSIZE""MB ext2 partition" + [ $SWAPSIZE -gt 0 ] && echo " -create $SWAPSIZE""MB swap partition" + [ "$EXTFS" != "ext2" ] && echo " -ext2 partition will be upgraded to $EXTFS" + echo + [ -z "$SILENTRUN" ] && UserAbort && ShowError "script canceled by user" + echo + +} + +ShowCardInfo() { + + CheckTableType + + echo + echo "retrieving current sdcard information..." + + if [ $TTISOK -gt 0 ] ; then + echo + parted "$SDPATH" print + echo + echo "script log is located @ /data/sdparted.log" + exit 0 + else + echo + echo "partition 1 may not be aligned to cylinder boundaries." + ShowError "cannot complete print operation." + fi + echo + +} + + +PartitionSdCard() { + + echo "performing selected actions..." + echo + + if [ $FATSIZE -gt 0 ] ; then + echo -n "creating fat32 partition..." + "$CMPARTED" -s "$SDPATH" mkpartfs primary fat32 0 "$FATSIZE"MB 2>&1 >>"$LOGFILE" + echo "done" + fi + + if [ $EXTSIZE -gt 0 ] ; then + echo -n "creating ext2 partition..." + "$CMPARTED" -s "$SDPATH" mkpartfs primary ext2 "$FATSIZE"MB "$EXTEND"MB 2>&1 >>"$LOGFILE" + echo "done" + fi + + if [ $SWAPSIZE -gt 0 ] ; then + echo -n "creating swap partition..." + "$CMPARTED" -s "$SDPATH" mkpartfs primary linux-swap "$EXTEND"MB "$SWAPEND"MB 2>&1 >>"$LOGFILE" + echo "done" + fi + echo + +} + +UpgradeExt() { + + # check for no upgrade + [ "$1" == "ext2" ] && return + # check for ext partition + [ ! -e "$EXTPATH" ] && ShowError "$EXTPATH does not exist" + + # have to use -m switch for this check b/c parted incorrectly + # reports all ext partitions as ext2 when running print + 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 + 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 < /dev/null 2>&1 >>"$LOGFILE" + + ValidateSizeArg "$SWAPRESP" + SWAPTEST="$FUNC_RET" + [ -n "$SWAPTEST" ] && [ $SWAPTEST -gt $SDSIZE ] && ShowMessage "$SWAPRESP > available space($(($SDSIZE))M)." && SWAPTEST= + done + + SWAPSIZE=$SWAPTEST + +} + +GetExtSize() { + + EXTTEST= + + while [ -z "$EXTTEST" ] + do + echo + echo -n "ext partition size [default=$EXTSIZE]: " + read EXTRESP + + [ -z "$EXTRESP" ] && EXTRESP="$EXTSIZE" + echo "$EXTRESP" > /dev/null 2>&1 >>"$LOGFILE" + + ValidateSizeArg "$EXTRESP" + EXTTEST="$FUNC_RET" + + [ -n "$EXTTEST" ] && [ $EXTTEST -gt $(($SDSIZE - $SWAPSIZE)) ] && ShowMessage "$EXTRESP > available space($(($SDSIZE - $SWAPSIZE))M)." && EXTTEST= + done + + EXTSIZE=$EXTTEST + +} + +GetExtType() { + + FSTEST= + + while [ -z "$FSTEST" ] + do + echo + echo -n "ext partition type [default=$EXTFS]: " + read FSRESP + + [ -z "$FSRESP" ] && FSRESP="$EXTFS" + echo "$FSRESP" > /dev/null 2>&1 >>"$LOGFILE" + + ValidateExtArg "$FSRESP" + FSTEST="$FUNC_RET" + done + + EXTFS="$FSTEST" + +} + +GetFatSize() { + + FATTEST= + + while [ -z "$FATTEST" ] + do + echo + echo -n "fat partition size [default=$FATSIZE]: " + read FATRESP + + [ -z "$FATRESP" ] && FATRESP="$FATSIZE" + echo "$FATRESP" > /dev/null 2>&1 >>"$LOGFILE" + + ValidateSizeArg "$FATRESP" + FATTEST="$FUNC_RET" + + [ -n "$FATTEST" ] && [ $FATTEST -gt $FATSIZE ] && ShowMessage "$FATRESP > available space($(($SDSIZE - $SWAPSIZE - $EXTSIZE))M)." && FATTEST= + [ -n "$FATTEST" ] && [ $FATTEST -le 0 ] && ShowMessage "must have a fat32 partition greater than 0MB" && FATTEST= + done + + FATSIZE=$FATTEST + +} + + +SCRIPTNAME="sdparted" +SCRIPTREV="0.6" +MYNAME="51dusty" + +IMODE= +SILENTRUN= +CREATEPART= +FUNC_RET= + +UEXTFSONLY= +DEXTFSONLY= + +TTISOK= +TTISLOOP= +TTISMSDOS= + +SDSIZE= +SDSIZEMB= +if [ -z "$SDPATH" ] +then + SDPATH="/dev/block/mmcblk0" +else + echo Found SDPATH=$SDPATH +fi + +FATSIZE= +FATTYPE="fat32" +FATPATH=$SDPATH"p1" + +EXTSIZE=512 +EXTFS="ext2" +EXTPATH=$SDPATH"p2" +EXTEND= + +SWAPSIZE=32 +SWAPTYPE="linux-swap" +SWAPPATH=$SDPATH"p3" +SWAPEND= + +CMPARTED="/sbin/parted" +CMTUNE2FS="/sbin/tune2fs" +CME2FSCK="/sbin/e2fsck" + +# give the output some breathing room +echo "$SCRIPTREV" >> "$LOGFILE" +echo + +# check for arguments +while [ $# -gt 0 ] ; do + case "$1" in + + -h|--help) ShowHelp ; exit 0 ;; + + -fs|--fatsize) shift ; ValidateSizeArg "$1" ; FATSIZE="$FUNC_RET" ; CREATEPART="$1" ;; + -es|--extsize) shift ; ValidateSizeArg "$1" ; EXTSIZE="$FUNC_RET" ; CREATEPART="$1" ;; + -ss|--swapsize) shift ; ValidateSizeArg "$1" ; SWAPSIZE="$FUNC_RET" ; CREATEPART="$1" ;; + -efs|--extfs) shift ; ValidateExtArg "$1" ; EXTFS="$FUNC_RET" ; CREATEPART="$1" ;; + + -ufs|--upgradefs) shift ; ValidateExtArg "$1" ; UEXTFSONLY="$FUNC_RET" ;; + -dfs|--downgradefs) shift ; ValidateExtArg "$1" ; DEXTFSONLY="$FUNC_RET" ;; + + -i|--interactive) IMODE="$1" ;; + + -s|--silent) SILENTRUN="$1" ;; + + -po|--printonly) ShowCardInfo ;; + + *) ShowHelp ; ShowError "unknown argument '$1'" ;; + + esac + shift +done + +# can't do silent when in interactive mode +[ -n "$IMODE" ] && SILENTRUN= + +# make sure sdcard exists and all needed files are here +CheckReqs + +# unmount all +UnmountAll + +# upgrade only? downgrade only? +UpgradeDowngradeOnly + +# check table +CheckTableType + +# prep card +PrepareSdCard + +# check for interactive mode +[ -n "$IMODE" ] && Interactive + +# do some math +CalculatePartitions + +# last chance to cancel +ShowActions + +# partition card +PartitionSdCard + +# upgrade fs if necessary +UpgradeExt "$EXTFS" + +# say goodbye and show print output +ShowCardInfo diff --git a/utilities/tune2fs b/utilities/tune2fs new file mode 100755 index 0000000..4bd02f6 Binary files /dev/null and b/utilities/tune2fs differ