diff options
Diffstat (limited to 'recovery.cpp')
-rw-r--r-- | recovery.cpp | 282 |
1 files changed, 234 insertions, 48 deletions
diff --git a/recovery.cpp b/recovery.cpp index b7a545898..ee2fb43fc 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -28,27 +28,30 @@ #include <sys/klog.h> #include <sys/stat.h> #include <sys/types.h> +#include <sys/wait.h> #include <time.h> #include <unistd.h> -#include <base/file.h> -#include <base/stringprintf.h> +#include <chrono> +#include <adb.h> +#include <android-base/file.h> +#include <android-base/stringprintf.h> +#include <cutils/android_reboot.h> +#include <cutils/properties.h> + +#include "adb_install.h" #include "bootloader.h" #include "common.h" -#include "cutils/properties.h" -#include "cutils/android_reboot.h" +#include "device.h" +#include "fuse_sdcard_provider.h" +#include "fuse_sideload.h" #include "install.h" #include "minui/minui.h" #include "minzip/DirUtil.h" #include "roots.h" #include "ui.h" #include "screen_ui.h" -#include "device.h" -#include "adb_install.h" -#include "adb.h" -#include "fuse_sideload.h" -#include "fuse_sdcard_provider.h" struct selabel_handle *sehandle; @@ -74,7 +77,10 @@ static const char *INTENT_FILE = "/cache/recovery/intent"; static const char *LOG_FILE = "/cache/recovery/log"; static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install"; static const char *LOCALE_FILE = "/cache/recovery/last_locale"; +static const char *CONVERT_FBE_DIR = "/cache/recovery/convert_fbe"; +static const char *CONVERT_FBE_FILE = "/cache/recovery/convert_fbe/convert_fbe"; static const char *CACHE_ROOT = "/cache"; +static const char *DATA_ROOT = "/data"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; @@ -151,8 +157,7 @@ static const int MAX_ARG_LENGTH = 4096; static const int MAX_ARGS = 100; // open a given path, mounting partitions as necessary -FILE* -fopen_path(const char *path, const char *mode) { +FILE* fopen_path(const char *path, const char *mode) { if (ensure_path_mounted(path) != 0) { LOGE("Can't mount %s\n", path); return NULL; @@ -166,23 +171,102 @@ fopen_path(const char *path, const char *mode) { return fp; } +// close a file, log an error if the error indicator is set +static void check_and_fclose(FILE *fp, const char *name) { + fflush(fp); + if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno)); + fclose(fp); +} + bool is_ro_debuggable() { char value[PROPERTY_VALUE_MAX+1]; return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1'); } static void redirect_stdio(const char* filename) { - // If these fail, there's not really anywhere to complain... - freopen(filename, "a", stdout); setbuf(stdout, NULL); - freopen(filename, "a", stderr); setbuf(stderr, NULL); -} + int pipefd[2]; + if (pipe(pipefd) == -1) { + LOGE("pipe failed: %s\n", strerror(errno)); -// close a file, log an error if the error indicator is set -static void -check_and_fclose(FILE *fp, const char *name) { - fflush(fp); - if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno)); - fclose(fp); + // Fall back to traditional logging mode without timestamps. + // If these fail, there's not really anywhere to complain... + freopen(filename, "a", stdout); setbuf(stdout, NULL); + freopen(filename, "a", stderr); setbuf(stderr, NULL); + + return; + } + + pid_t pid = fork(); + if (pid == -1) { + LOGE("fork failed: %s\n", strerror(errno)); + + // Fall back to traditional logging mode without timestamps. + // If these fail, there's not really anywhere to complain... + freopen(filename, "a", stdout); setbuf(stdout, NULL); + freopen(filename, "a", stderr); setbuf(stderr, NULL); + + return; + } + + if (pid == 0) { + /// Close the unused write end. + close(pipefd[1]); + + auto start = std::chrono::steady_clock::now(); + + // Child logger to actually write to the log file. + FILE* log_fp = fopen(filename, "a"); + if (log_fp == nullptr) { + LOGE("fopen \"%s\" failed: %s\n", filename, strerror(errno)); + close(pipefd[0]); + _exit(1); + } + + FILE* pipe_fp = fdopen(pipefd[0], "r"); + if (pipe_fp == nullptr) { + LOGE("fdopen failed: %s\n", strerror(errno)); + check_and_fclose(log_fp, filename); + close(pipefd[0]); + _exit(1); + } + + char* line = nullptr; + size_t len = 0; + while (getline(&line, &len, pipe_fp) != -1) { + auto now = std::chrono::steady_clock::now(); + double duration = std::chrono::duration_cast<std::chrono::duration<double>>( + now - start).count(); + if (line[0] == '\n') { + fprintf(log_fp, "[%12.6lf]\n", duration); + } else { + fprintf(log_fp, "[%12.6lf] %s", duration, line); + } + fflush(log_fp); + } + + LOGE("getline failed: %s\n", strerror(errno)); + + free(line); + check_and_fclose(log_fp, filename); + close(pipefd[0]); + _exit(1); + } else { + // Redirect stdout/stderr to the logger process. + // Close the unused read end. + close(pipefd[0]); + + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); + + if (dup2(pipefd[1], STDOUT_FILENO) == -1) { + LOGE("dup2 stdout failed: %s\n", strerror(errno)); + } + if (dup2(pipefd[1], STDERR_FILENO) == -1) { + LOGE("dup2 stderr failed: %s\n", strerror(errno)); + } + + close(pipefd[1]); + } } // command line args come from, in decreasing precedence: @@ -326,14 +410,18 @@ static void rotate_logs(int max) { ensure_path_mounted(LAST_KMSG_FILE); for (int i = max-1; i >= 0; --i) { - std::string old_log = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d", - LAST_LOG_FILE, i); + std::string old_log = android::base::StringPrintf("%s", LAST_LOG_FILE); + if (i > 0) { + old_log += "." + std::to_string(i); + } std::string new_log = android::base::StringPrintf("%s.%d", LAST_LOG_FILE, i+1); // Ignore errors if old_log doesn't exist. rename(old_log.c_str(), new_log.c_str()); - std::string old_kmsg = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d", - LAST_KMSG_FILE, i); + std::string old_kmsg = android::base::StringPrintf("%s", LAST_KMSG_FILE); + if (i > 0) { + old_kmsg += "." + std::to_string(i); + } std::string new_kmsg = android::base::StringPrintf("%s.%d", LAST_KMSG_FILE, i+1); rename(old_kmsg.c_str(), new_kmsg.c_str()); } @@ -419,6 +507,7 @@ typedef struct _saved_log_file { static bool erase_volume(const char* volume) { bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); + bool is_data = (strcmp(volume, DATA_ROOT) == 0); ui->SetBackground(RecoveryUI::ERASING); ui->SetProgressType(RecoveryUI::INDETERMINATE); @@ -473,7 +562,25 @@ static bool erase_volume(const char* volume) { ui->Print("Formatting %s...\n", volume); ensure_path_unmounted(volume); - int result = format_volume(volume); + + int result; + + if (is_data && reason && strcmp(reason, "convert_fbe") == 0) { + // Create convert_fbe breadcrumb file to signal to init + // to convert to file based encryption, not full disk encryption + mkdir(CONVERT_FBE_DIR, 0700); + FILE* f = fopen(CONVERT_FBE_FILE, "wb"); + if (!f) { + ui->Print("Failed to convert to file encryption\n"); + return true; + } + fclose(f); + result = format_volume(volume, CONVERT_FBE_DIR); + remove(CONVERT_FBE_FILE); + rmdir(CONVERT_FBE_DIR); + } else { + result = format_volume(volume); + } if (is_cache) { while (head) { @@ -706,7 +813,10 @@ static void choose_recovery_file(Device* device) { // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x for (int i = 0; i < KEEP_LOG_COUNT; i++) { char* log_file; - if (asprintf(&log_file, (i == 0) ? "%s" : "%s.%d", LAST_LOG_FILE, i) == -1) { + int ret; + ret = (i == 0) ? asprintf(&log_file, "%s", LAST_LOG_FILE) : + asprintf(&log_file, "%s.%d", LAST_LOG_FILE, i); + if (ret == -1) { // memory allocation failure - return early. Should never happen. return; } @@ -717,7 +827,9 @@ static void choose_recovery_file(Device* device) { } char* kmsg_file; - if (asprintf(&kmsg_file, (i == 0) ? "%s" : "%s.%d", LAST_KMSG_FILE, i) == -1) { + ret = (i == 0) ? asprintf(&kmsg_file, "%s", LAST_KMSG_FILE) : + asprintf(&kmsg_file, "%s.%d", LAST_KMSG_FILE, i); + if (ret == -1) { // memory allocation failure - return early. Should never happen. return; } @@ -736,10 +848,7 @@ static void choose_recovery_file(Device* device) { int chosen_item = get_menu_selection(headers, entries, 1, 0, device); if (strcmp(entries[chosen_item], "Back") == 0) break; - // TODO: do we need to redirect? ShowFile could just avoid writing to stdio. - redirect_stdio("/dev/null"); ui->ShowFile(entries[chosen_item]); - redirect_stdio(TEMPORARY_LOG_FILE); } for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) { @@ -747,6 +856,10 @@ static void choose_recovery_file(Device* device) { } } +// How long (in seconds) we wait for the fuse-provided package file to +// appear, before timing out. +#define SDCARD_INSTALL_TIMEOUT 10 + static int apply_from_sdcard(Device* device, bool* wipe_cache) { modified_flash = true; @@ -758,19 +871,68 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) { char* path = browse_directory(SDCARD_ROOT, device); if (path == NULL) { ui->Print("\n-- No package file selected.\n"); + ensure_path_unmounted(SDCARD_ROOT); return INSTALL_ERROR; } 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, + // We used to use fuse in a thread as opposed to a process. Since accessing + // through fuse involves going from kernel to userspace to kernel, it leads + // to deadlock when a page fault occurs. (Bug: 26313124) + pid_t child; + if ((child = fork()) == 0) { + bool status = start_sdcard_fuse(path); + + _exit(status ? EXIT_SUCCESS : EXIT_FAILURE); + } + + // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child + // process is ready. + int result = INSTALL_ERROR; + int status; + bool waited = false; + for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) { + if (waitpid(child, &status, WNOHANG) == -1) { + result = INSTALL_ERROR; + waited = true; + break; + } + + struct stat sb; + if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) { + if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) { + sleep(1); + continue; + } else { + LOGE("Timed out waiting for the fuse-provided package.\n"); + result = INSTALL_ERROR; + kill(child, SIGKILL); + break; + } + } + + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, TEMPORARY_INSTALL_FILE, false); + break; + } + + if (!waited) { + // Calling stat() on this magic filename signals the fuse + // filesystem to shut down. + struct stat sb; + stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb); + + waitpid(child, &status, 0); + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOGE("Error exit from the fuse process: %d\n", WEXITSTATUS(status)); + } - finish_sdcard_fuse(token); ensure_path_unmounted(SDCARD_ROOT); - return status; + return result; } // Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION @@ -853,9 +1015,24 @@ prompt_and_wait(Device* device, int status) { break; case Device::MOUNT_SYSTEM: - if (ensure_path_mounted("/system") != -1) { - ui->Print("Mounted /system.\n"); + char system_root_image[PROPERTY_VALUE_MAX]; + property_get("ro.build.system_root_image", system_root_image, ""); + + // For a system image built with the root directory (i.e. + // system_root_image == "true"), we mount it to /system_root, and symlink /system + // to /system_root/system to make adb shell work (the symlink is created through + // the build system). + // Bug: 22855115 + if (strcmp(system_root_image, "true") == 0) { + if (ensure_path_mounted_at("/", "/system_root") != -1) { + ui->Print("Mounted /system.\n"); + } + } else { + if (ensure_path_mounted("/system") != -1) { + ui->Print("Mounted /system.\n"); + } } + break; } } @@ -905,10 +1082,6 @@ ui_print(const char* format, ...) { int main(int argc, char **argv) { - time_t start = time(NULL); - - redirect_stdio(TEMPORARY_LOG_FILE); - // If this binary is started with the single argument "--adbd", // instead of being the normal recovery binary, it turns into kind // of a stripped-down version of adbd that only supports the @@ -917,10 +1090,16 @@ main(int argc, char **argv) { // only way recovery should be run with this argument is when it // starts a copy of itself from the apply_from_adb() function. if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { - adb_main(0, DEFAULT_ADB_PORT); + adb_server_main(0, DEFAULT_ADB_PORT, -1); return 0; } + time_t start = time(NULL); + + // redirect_stdio should be called only in non-sideload mode. Otherwise + // we may have two logger instances with different timestamps. + redirect_stdio(TEMPORARY_LOG_FILE); + printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table(); @@ -1011,11 +1190,15 @@ main(int argc, char **argv) { if (strncmp(update_package, "CACHE:", 6) == 0) { int len = strlen(update_package) + 10; char* modified_path = (char*)malloc(len); - strlcpy(modified_path, "/cache/", len); - strlcat(modified_path, update_package+6, len); - printf("(replacing path \"%s\" with \"%s\")\n", - update_package, modified_path); - update_package = modified_path; + if (modified_path) { + strlcpy(modified_path, "/cache/", len); + strlcat(modified_path, update_package+6, len); + printf("(replacing path \"%s\" with \"%s\")\n", + update_package, modified_path); + update_package = modified_path; + } + else + printf("modified_path allocation failed\n"); } } printf("\n"); @@ -1114,6 +1297,9 @@ main(int argc, char **argv) { property_set(ANDROID_RB_PROPERTY, "reboot,"); break; } - sleep(5); // should reboot before this finishes + while (true) { + pause(); + } + // Should be unreachable. return EXIT_SUCCESS; } |