summaryrefslogtreecommitdiffstats
path: root/recovery.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'recovery.cpp')
-rw-r--r--recovery.cpp282
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;
}