summaryrefslogtreecommitdiffstats
path: root/recovery.c
diff options
context:
space:
mode:
Diffstat (limited to 'recovery.c')
-rw-r--r--recovery.c190
1 files changed, 158 insertions, 32 deletions
diff --git a/recovery.c b/recovery.c
index 04bf657..40e79d7 100644
--- a/recovery.c
+++ b/recovery.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
+#include <dirent.h>
#include "bootloader.h"
#include "common.h"
@@ -51,7 +52,7 @@ static const struct option OPTIONS[] = {
static const char *COMMAND_FILE = "CACHE:recovery/command";
static const char *INTENT_FILE = "CACHE:recovery/intent";
static const char *LOG_FILE = "CACHE:recovery/log";
-static const char *SDCARD_PACKAGE_FILE = "SDCARD:update.zip";
+static const char *EXT_ROOT = "EXT:";
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *SIDELOAD_TEMP_DIR = "TMP:sideload";
@@ -405,7 +406,7 @@ copy_sideloaded_package(const char* original_root_path) {
}
static char**
-prepend_title(char** headers) {
+prepend_title(const char** headers) {
char* title[] = { "Android system recovery <"
EXPAND(RECOVERY_API_VERSION) "e>",
"",
@@ -428,13 +429,14 @@ prepend_title(char** headers) {
}
static int
-get_menu_selection(char** headers, char** items, int menu_only) {
+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);
- int selected = 0;
+ ui_start_menu(headers, items, initial_selection);
+ int selected = initial_selection;
int chosen_item = -1;
while (chosen_item < 0) {
@@ -468,6 +470,132 @@ get_menu_selection(char** headers, char** items, int menu_only) {
return chosen_item;
}
+static int compare_string(const void* a, const void* b) {
+ return strcmp(*(const char**)a, *(const char**)b);
+}
+
+static int
+sdcard_directory(const char* root_path) {
+ // Mount the sdcard when the package selection menu is enabled so
+ // you can "adb push" packages to the sdcard and immediately
+ // install them.
+ if (ensure_root_path_mounted(EXT_ROOT) < 0) {
+ ui_print("Failed to mount external storage.\n");
+ return INSTALL_ERROR;
+ }
+
+ const char* MENU_HEADERS[] = { "Choose a package to install:",
+ root_path,
+ "",
+ NULL };
+ DIR* d;
+ struct dirent* de;
+ char path[PATH_MAX];
+ d = opendir(translate_root_path(root_path, path, sizeof(path)));
+ if (d == NULL) {
+ LOGE("error opening %s: %s\n", path, strerror(errno));
+ ensure_root_path_unmounted(EXT_ROOT);
+ return 0;
+ }
+
+ char** headers = prepend_title(MENU_HEADERS);
+
+ int d_size = 0;
+ int d_alloc = 10;
+ char** dirs = malloc(d_alloc * sizeof(char*));
+ int z_size = 1;
+ int z_alloc = 10;
+ char** zips = malloc(z_alloc * sizeof(char*));
+ zips[0] = strdup("../");
+
+ while ((de = readdir(d)) != NULL) {
+ int name_len = strlen(de->d_name);
+
+ if (de->d_type == DT_DIR) {
+ // skip "." and ".." entries
+ if (name_len == 1 && de->d_name[0] == '.') continue;
+ if (name_len == 2 && de->d_name[0] == '.' &&
+ de->d_name[1] == '.') continue;
+
+ if (d_size >= d_alloc) {
+ d_alloc *= 2;
+ dirs = realloc(dirs, d_alloc * sizeof(char*));
+ }
+ dirs[d_size] = malloc(name_len + 2);
+ strcpy(dirs[d_size], de->d_name);
+ dirs[d_size][name_len] = '/';
+ dirs[d_size][name_len+1] = '\0';
+ ++d_size;
+ } else if (de->d_type == DT_REG &&
+ name_len >= 4 &&
+ strncasecmp(de->d_name + (name_len-4), ".zip", 4) == 0) {
+ if (z_size >= z_alloc) {
+ z_alloc *= 2;
+ zips = realloc(zips, z_alloc * sizeof(char*));
+ }
+ zips[z_size++] = strdup(de->d_name);
+ }
+ }
+ closedir(d);
+
+ qsort(dirs, d_size, sizeof(char*), compare_string);
+ qsort(zips, z_size, sizeof(char*), compare_string);
+
+ // append dirs to the zips list
+ if (d_size + z_size + 1 > z_alloc) {
+ z_alloc = d_size + z_size + 1;
+ zips = realloc(zips, z_alloc * sizeof(char*));
+ }
+ memcpy(zips + z_size, dirs, d_size * sizeof(char*));
+ free(dirs);
+ z_size += d_size;
+ zips[z_size] = NULL;
+
+ int result = INSTALL_CORRUPT;
+ int chosen_item = 0;
+ do {
+ chosen_item = get_menu_selection(headers, zips, 1, chosen_item);
+
+ char* item = zips[chosen_item];
+ int item_len = strlen(item);
+ if (chosen_item == 0) { // item 0 is always "../"
+ // go up but continue browsing (if the caller is sdcard_directory)
+ result = -1;
+ break;
+ } else if (item[item_len-1] == '/') {
+ // recurse down into a subdirectory
+ char new_path[PATH_MAX];
+ strlcpy(new_path, root_path, PATH_MAX);
+ strlcat(new_path, item, PATH_MAX);
+ result = sdcard_directory(new_path);
+ if (result >= 0) break;
+ } else {
+ // selected a zip file: attempt to install it, and return
+ // the status to the caller.
+ char new_path[PATH_MAX];
+ strlcpy(new_path, root_path, PATH_MAX);
+ strlcat(new_path, item, PATH_MAX);
+
+ ui_print("\n-- Install %s ...\n", new_path);
+ char* copy = copy_sideloaded_package(new_path);
+ if (copy != NULL) {
+ set_sdcard_update_bootloader_message();
+ result = install_package(copy);
+ free(copy);
+ }
+ break;
+ }
+ } while (true);
+
+ int i;
+ for (i = 0; i < z_size; ++i) free(zips[i]);
+ free(zips);
+ free(headers);
+
+ ensure_root_path_unmounted(EXT_ROOT);
+ return result;
+}
+
static void
wipe_data(int confirm) {
if (confirm) {
@@ -478,7 +606,7 @@ wipe_data(int confirm) {
" THIS CAN NOT BE UNDONE.",
"",
NULL };
- title_headers = prepend_title(headers);
+ title_headers = prepend_title((const char**)headers);
}
char* items[] = { " No",
@@ -494,7 +622,7 @@ wipe_data(int confirm) {
" No",
NULL };
- int chosen_item = get_menu_selection(title_headers, items, 1);
+ int chosen_item = get_menu_selection(title_headers, items, 1, 0);
if (chosen_item != 7) {
return;
}
@@ -509,13 +637,13 @@ wipe_data(int confirm) {
static void
prompt_and_wait() {
- char** headers = prepend_title(MENU_HEADERS);
+ char** headers = prepend_title((const char**)MENU_HEADERS);
for (;;) {
finish_recovery(NULL);
ui_reset_progress();
- int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0);
+ int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0);
// device-specific code may take some action here. It may
// return one of the core actions handled in the switch
@@ -538,22 +666,18 @@ prompt_and_wait() {
if (!ui_text_visible()) return;
break;
- case ITEM_APPLY_SDCARD:
- ui_print("\n-- Install from sdcard...\n");
- int status = INSTALL_CORRUPT;
- char* copy = copy_sideloaded_package(SDCARD_PACKAGE_FILE);
- if (copy != NULL) {
- set_sdcard_update_bootloader_message();
- status = install_package(copy);
- free(copy);
- }
- if (status != INSTALL_SUCCESS) {
- ui_set_background(BACKGROUND_ICON_ERROR);
- ui_print("Installation aborted.\n");
- } else if (!ui_text_visible()) {
- return; // reboot if logs aren't visible
- } else {
- ui_print("\nInstall from sdcard complete.\n");
+ case ITEM_APPLY_EXT:
+ ;
+ int status = sdcard_directory(EXT_ROOT);
+ if (status >= 0) {
+ if (status != INSTALL_SUCCESS) {
+ ui_set_background(BACKGROUND_ICON_ERROR);
+ ui_print("Installation aborted.\n");
+ } else if (!ui_text_visible()) {
+ return; // reboot if logs aren't visible
+ } else {
+ ui_print("\nInstall from sdcard complete.\n");
+ }
}
break;
}
@@ -562,7 +686,7 @@ prompt_and_wait() {
static void
print_property(const char *key, const char *name, void *cookie) {
- fprintf(stderr, "%s=%s\n", key, name);
+ printf("%s=%s\n", key, name);
}
int
@@ -572,7 +696,7 @@ main(int argc, char **argv) {
// If these fail, there's not really anywhere to complain...
freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
- fprintf(stderr, "Starting recovery on %s", ctime(&start));
+ printf("Starting recovery on %s", ctime(&start));
ui_init();
get_args(&argc, &argv);
@@ -602,14 +726,14 @@ main(int argc, char **argv) {
device_recovery_start();
- fprintf(stderr, "Command:");
+ printf("Command:");
for (arg = 0; arg < argc; arg++) {
- fprintf(stderr, " \"%s\"", argv[arg]);
+ printf(" \"%s\"", argv[arg]);
}
- fprintf(stderr, "\n\n");
+ printf("\n\n");
property_list(print_property, NULL);
- fprintf(stderr, "\n");
+ printf("\n");
int status = INSTALL_SUCCESS;
@@ -665,7 +789,9 @@ main(int argc, char **argv) {
}
if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
- if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait();
+ if (status != INSTALL_SUCCESS || ui_text_visible()) {
+ prompt_and_wait();
+ }
// Otherwise, get ready to boot the main system...
finish_recovery(send_intent);