summaryrefslogtreecommitdiffstats
path: root/recovery.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'recovery.cpp')
-rw-r--r--recovery.cpp263
1 files changed, 90 insertions, 173 deletions
diff --git a/recovery.cpp b/recovery.cpp
index 8f2183d30..7f17b16ef 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -44,6 +44,8 @@
#include "adb_install.h"
extern "C" {
#include "minadbd/adb.h"
+#include "fuse_sideload.h"
+#include "fuse_sdcard_provider.h"
}
struct selabel_handle *sehandle;
@@ -58,6 +60,7 @@ static const struct option OPTIONS[] = {
{ "locale", required_argument, NULL, 'l' },
{ "stages", required_argument, NULL, 'g' },
{ "shutdown_after", no_argument, NULL, 'p' },
+ { "reason", required_argument, NULL, 'r' },
{ NULL, 0, NULL, 0 },
};
@@ -73,12 +76,12 @@ static const char *CACHE_ROOT = "/cache";
static const char *SDCARD_ROOT = "/sdcard";
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
-static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
RecoveryUI* ui = NULL;
char* locale = NULL;
char recovery_version[PROPERTY_VALUE_MAX+1];
char* stage = NULL;
+char* reason = NULL;
/*
* The recovery tool communicates with the main system through /cache files.
@@ -439,96 +442,6 @@ erase_volume(const char *volume) {
return result;
}
-static char*
-copy_sideloaded_package(const char* original_path) {
- if (ensure_path_mounted(original_path) != 0) {
- LOGE("Can't mount %s\n", original_path);
- return NULL;
- }
-
- if (ensure_path_mounted(SIDELOAD_TEMP_DIR) != 0) {
- LOGE("Can't mount %s\n", SIDELOAD_TEMP_DIR);
- return NULL;
- }
-
- if (mkdir(SIDELOAD_TEMP_DIR, 0700) != 0) {
- if (errno != EEXIST) {
- LOGE("Can't mkdir %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
- return NULL;
- }
- }
-
- // verify that SIDELOAD_TEMP_DIR is exactly what we expect: a
- // directory, owned by root, readable and writable only by root.
- struct stat st;
- if (stat(SIDELOAD_TEMP_DIR, &st) != 0) {
- LOGE("failed to stat %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
- return NULL;
- }
- if (!S_ISDIR(st.st_mode)) {
- LOGE("%s isn't a directory\n", SIDELOAD_TEMP_DIR);
- return NULL;
- }
- if ((st.st_mode & 0777) != 0700) {
- LOGE("%s has perms %o\n", SIDELOAD_TEMP_DIR, st.st_mode);
- return NULL;
- }
- if (st.st_uid != 0) {
- LOGE("%s owned by %lu; not root\n", SIDELOAD_TEMP_DIR, st.st_uid);
- return NULL;
- }
-
- char copy_path[PATH_MAX];
- strcpy(copy_path, SIDELOAD_TEMP_DIR);
- strcat(copy_path, "/package.zip");
-
- char* buffer = (char*)malloc(BUFSIZ);
- if (buffer == NULL) {
- LOGE("Failed to allocate buffer\n");
- return NULL;
- }
-
- size_t read;
- FILE* fin = fopen(original_path, "rb");
- if (fin == NULL) {
- LOGE("Failed to open %s (%s)\n", original_path, strerror(errno));
- return NULL;
- }
- FILE* fout = fopen(copy_path, "wb");
- if (fout == NULL) {
- LOGE("Failed to open %s (%s)\n", copy_path, strerror(errno));
- return NULL;
- }
-
- while ((read = fread(buffer, 1, BUFSIZ, fin)) > 0) {
- if (fwrite(buffer, 1, read, fout) != read) {
- LOGE("Short write of %s (%s)\n", copy_path, strerror(errno));
- return NULL;
- }
- }
-
- free(buffer);
-
- if (fclose(fout) != 0) {
- LOGE("Failed to close %s (%s)\n", copy_path, strerror(errno));
- return NULL;
- }
-
- if (fclose(fin) != 0) {
- LOGE("Failed to close %s (%s)\n", original_path, strerror(errno));
- return NULL;
- }
-
- // "adb push" is happy to overwrite read-only files when it's
- // running as root, but we'll try anyway.
- if (chmod(copy_path, 0400) != 0) {
- LOGE("Failed to chmod %s (%s)\n", copy_path, strerror(errno));
- return NULL;
- }
-
- return strdup(copy_path);
-}
-
static const char**
prepend_title(const char* const* headers) {
// count the number of lines in our title, plus the
@@ -604,9 +517,9 @@ static int compare_string(const void* a, const void* b) {
return strcmp(*(const char**)a, *(const char**)b);
}
-static int
-update_directory(const char* path, const char* unmount_when_done,
- int* wipe_cache, Device* device) {
+// Returns a malloc'd path, or NULL.
+static char*
+browse_directory(const char* path, Device* device) {
ensure_path_mounted(path);
const char* MENU_HEADERS[] = { "Choose a package to install:",
@@ -618,10 +531,7 @@ update_directory(const char* path, const char* unmount_when_done,
d = opendir(path);
if (d == NULL) {
LOGE("error opening %s: %s\n", path, strerror(errno));
- if (unmount_when_done != NULL) {
- ensure_path_unmounted(unmount_when_done);
- }
- return 0;
+ return NULL;
}
const char** headers = prepend_title(MENU_HEADERS);
@@ -677,58 +587,41 @@ update_directory(const char* path, const char* unmount_when_done,
z_size += d_size;
zips[z_size] = NULL;
- int result;
+ char* result;
int chosen_item = 0;
- do {
+ while (true) {
chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device);
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 update_directory)
- result = -1;
+ result = NULL;
break;
- } else if (item[item_len-1] == '/') {
+ }
+
+ char new_path[PATH_MAX];
+ strlcpy(new_path, path, PATH_MAX);
+ strlcat(new_path, "/", PATH_MAX);
+ strlcat(new_path, item, PATH_MAX);
+
+ if (item[item_len-1] == '/') {
// recurse down into a subdirectory
- char new_path[PATH_MAX];
- strlcpy(new_path, path, PATH_MAX);
- strlcat(new_path, "/", PATH_MAX);
- strlcat(new_path, item, PATH_MAX);
new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/'
- result = update_directory(new_path, unmount_when_done, wipe_cache, device);
- if (result >= 0) break;
+ result = browse_directory(new_path, device);
+ if (result) 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, path, PATH_MAX);
- strlcat(new_path, "/", PATH_MAX);
- strlcat(new_path, item, PATH_MAX);
-
- ui->Print("\n-- Install %s ...\n", path);
- set_sdcard_update_bootloader_message();
- char* copy = copy_sideloaded_package(new_path);
- if (unmount_when_done != NULL) {
- ensure_path_unmounted(unmount_when_done);
- }
- if (copy) {
- result = install_package(copy, wipe_cache, TEMPORARY_INSTALL_FILE);
- free(copy);
- } else {
- result = INSTALL_ERROR;
- }
+ // selected a zip file: return the malloc'd path to the caller.
+ result = strdup(new_path);
break;
}
- } while (true);
+ }
int i;
for (i = 0; i < z_size; ++i) free(zips[i]);
free(zips);
free(headers);
- if (unmount_when_done != NULL) {
- ensure_path_unmounted(unmount_when_done);
- }
return result;
}
@@ -768,10 +661,14 @@ wipe_data(int confirm, Device* device) {
device->WipeData();
erase_volume("/data");
erase_volume("/cache");
+ erase_persistent_partition();
ui->Print("Data wipe complete.\n");
}
-static void
+// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION
+// means to take the default, which is to reboot or shutdown depending
+// on if the --shutdown_after flag was passed to recovery.
+static Device::BuiltinAction
prompt_and_wait(Device* device, int status) {
const char* const* headers = prepend_title(device->GetMenuHeaders());
@@ -795,27 +692,48 @@ prompt_and_wait(Device* device, int status) {
// device-specific code may take some action here. It may
// return one of the core actions handled in the switch
// statement below.
- chosen_item = device->InvokeMenuItem(chosen_item);
+ Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
+
+ int wipe_cache = 0;
+ switch (chosen_action) {
+ case Device::NO_ACTION:
+ break;
- int wipe_cache;
- switch (chosen_item) {
case Device::REBOOT:
- return;
+ case Device::SHUTDOWN:
+ case Device::REBOOT_BOOTLOADER:
+ return chosen_action;
case Device::WIPE_DATA:
wipe_data(ui->IsTextVisible(), device);
- if (!ui->IsTextVisible()) return;
+ if (!ui->IsTextVisible()) return Device::NO_ACTION;
break;
case Device::WIPE_CACHE:
ui->Print("\n-- Wiping cache...\n");
erase_volume("/cache");
ui->Print("Cache wipe complete.\n");
- if (!ui->IsTextVisible()) return;
+ if (!ui->IsTextVisible()) return Device::NO_ACTION;
break;
- case Device::APPLY_EXT:
- status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device);
+ case Device::APPLY_EXT: {
+ ensure_path_mounted(SDCARD_ROOT);
+ char* path = browse_directory(SDCARD_ROOT, device);
+ if (path == NULL) {
+ ui->Print("\n-- No package file selected.\n", path);
+ break;
+ }
+
+ ui->Print("\n-- Install %s ...\n", path);
+ set_sdcard_update_bootloader_message();
+ void* token = start_sdcard_fuse(path);
+
+ int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, &wipe_cache,
+ TEMPORARY_INSTALL_FILE, false);
+
+ finish_sdcard_fuse(token);
+ ensure_path_unmounted(SDCARD_ROOT);
+
if (status == INSTALL_SUCCESS && wipe_cache) {
ui->Print("\n-- Wiping cache (at package request)...\n");
if (erase_volume("/cache")) {
@@ -824,39 +742,22 @@ prompt_and_wait(Device* device, int status) {
ui->Print("Cache wipe complete.\n");
}
}
+
if (status >= 0) {
if (status != INSTALL_SUCCESS) {
ui->SetBackground(RecoveryUI::ERROR);
ui->Print("Installation aborted.\n");
} else if (!ui->IsTextVisible()) {
- return; // reboot if logs aren't visible
+ return Device::NO_ACTION; // reboot if logs aren't visible
} else {
ui->Print("\nInstall from sdcard complete.\n");
}
}
break;
+ }
case Device::APPLY_CACHE:
- // Don't unmount cache at the end of this.
- status = update_directory(CACHE_ROOT, NULL, &wipe_cache, device);
- if (status == INSTALL_SUCCESS && wipe_cache) {
- ui->Print("\n-- Wiping cache (at package request)...\n");
- if (erase_volume("/cache")) {
- ui->Print("Cache wipe failed.\n");
- } else {
- ui->Print("Cache wipe complete.\n");
- }
- }
- if (status >= 0) {
- if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
- ui->Print("Installation aborted.\n");
- } else if (!ui->IsTextVisible()) {
- return; // reboot if logs aren't visible
- } else {
- ui->Print("\nInstall from cache complete.\n");
- }
- }
+ ui->Print("\nAPPLY_CACHE is deprecated.\n");
break;
case Device::APPLY_ADB_SIDELOAD:
@@ -867,7 +768,7 @@ prompt_and_wait(Device* device, int status) {
ui->Print("Installation aborted.\n");
copy_logs();
} else if (!ui->IsTextVisible()) {
- return; // reboot if logs aren't visible
+ return Device::NO_ACTION; // reboot if logs aren't visible
} else {
ui->Print("\nInstall from ADB complete.\n");
}
@@ -939,7 +840,7 @@ main(int argc, char **argv) {
return 0;
}
- printf("Starting recovery on %s", ctime(&start));
+ printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
load_volume_table();
ensure_path_mounted(LAST_LOG_FILE);
@@ -971,6 +872,7 @@ main(int argc, char **argv) {
break;
}
case 'p': shutdown_after = true; break;
+ case 'r': reason = optarg; break;
case '?':
LOGE("Invalid command argument\n");
continue;
@@ -981,7 +883,8 @@ main(int argc, char **argv) {
load_locale_from_cache();
}
printf("locale is [%s]\n", locale);
- printf("stage is [%s]\n", stage, stage);
+ printf("stage is [%s]\n", stage);
+ printf("reason is [%s]\n", reason);
Device* device = make_device();
ui = device->GetUI();
@@ -1039,7 +942,7 @@ main(int argc, char **argv) {
int status = INSTALL_SUCCESS;
if (update_package != NULL) {
- status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);
+ status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);
if (status == INSTALL_SUCCESS && wipe_cache) {
if (erase_volume("/cache")) {
LOGE("Cache wipe (requested by package) failed.");
@@ -1061,6 +964,7 @@ main(int argc, char **argv) {
if (device->WipeData()) status = INSTALL_ERROR;
if (erase_volume("/data")) status = INSTALL_ERROR;
if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
+ if (erase_persistent_partition() == -1 ) status = INSTALL_ERROR;
if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
} else if (wipe_cache) {
if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
@@ -1074,18 +978,31 @@ main(int argc, char **argv) {
copy_logs();
ui->SetBackground(RecoveryUI::ERROR);
}
+ Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
- prompt_and_wait(device, status);
+ Device::BuiltinAction temp = prompt_and_wait(device, status);
+ if (temp != Device::NO_ACTION) after = temp;
}
- // Otherwise, get ready to boot the main system...
+ // Save logs and clean up before rebooting or shutting down.
finish_recovery(send_intent);
- if (shutdown_after) {
- ui->Print("Shutting down...\n");
- property_set(ANDROID_RB_PROPERTY, "shutdown,");
- } else {
- ui->Print("Rebooting...\n");
- property_set(ANDROID_RB_PROPERTY, "reboot,");
+
+ switch (after) {
+ case Device::SHUTDOWN:
+ ui->Print("Shutting down...\n");
+ property_set(ANDROID_RB_PROPERTY, "shutdown,");
+ break;
+
+ case Device::REBOOT_BOOTLOADER:
+ ui->Print("Rebooting to bootloader...\n");
+ property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
+ break;
+
+ default:
+ ui->Print("Rebooting...\n");
+ property_set(ANDROID_RB_PROPERTY, "reboot,");
+ break;
}
+ sleep(5); // should reboot before this finishes
return EXIT_SUCCESS;
}