summaryrefslogtreecommitdiffstats
path: root/recovery.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--recovery.cpp311
1 files changed, 258 insertions, 53 deletions
diff --git a/recovery.cpp b/recovery.cpp
index 10f84140d..ccb2d223b 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -19,7 +19,9 @@
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
+#include <inttypes.h>
#include <limits.h>
+#include <linux/fs.h>
#include <linux/input.h>
#include <stdarg.h>
#include <stdio.h>
@@ -33,12 +35,16 @@
#include <unistd.h>
#include <chrono>
+#include <string>
+#include <vector>
#include <adb.h>
#include <android/log.h> /* Android Log Priority Tags */
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
#include <cutils/properties.h>
#include <log/logger.h> /* Android Log packet format */
@@ -47,7 +53,6 @@
#include <healthd/BatteryMonitor.h>
#include "adb_install.h"
-#include "bootloader.h"
#include "common.h"
#include "device.h"
#include "error_code.h"
@@ -56,8 +61,10 @@
#include "install.h"
#include "minui/minui.h"
#include "minzip/DirUtil.h"
+#include "minzip/Zip.h"
#include "roots.h"
#include "ui.h"
+#include "unique_fd.h"
#include "screen_ui.h"
struct selabel_handle *sehandle;
@@ -77,9 +84,17 @@ static const struct option OPTIONS[] = {
{ "shutdown_after", no_argument, NULL, 'p' },
{ "reason", required_argument, NULL, 'r' },
{ "security", no_argument, NULL, 'e'},
+ { "wipe_ab", no_argument, NULL, 0 },
+ { "wipe_package_size", required_argument, NULL, 0 },
{ NULL, 0, NULL, 0 },
};
+// More bootreasons can be found in "system/core/bootstat/bootstat.cpp".
+static const std::vector<std::string> bootreason_blacklist {
+ "kernel_panic",
+ "Panic",
+};
+
static const char *CACHE_LOG_DIR = "/cache/recovery";
static const char *COMMAND_FILE = "/cache/recovery/command";
static const char *INTENT_FILE = "/cache/recovery/intent";
@@ -104,6 +119,7 @@ static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
// So we should check battery with a slightly lower limitation.
static const int BATTERY_OK_PERCENTAGE = 20;
static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
+constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe";
RecoveryUI* ui = NULL;
static const char* locale = "en_US";
@@ -293,9 +309,13 @@ static void redirect_stdio(const char* filename) {
// - the contents of COMMAND_FILE (one per line)
static void
get_args(int *argc, char ***argv) {
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
+ bootloader_message boot = {};
+ std::string err;
+ if (!read_bootloader_message(&boot, &err)) {
+ LOGE("%s\n", err.c_str());
+ // If fails, leave a zeroed bootloader_message.
+ memset(&boot, 0, sizeof(boot));
+ }
stage = strndup(boot.stage, sizeof(boot.stage));
if (boot.command[0] != 0 && boot.command[0] != 255) {
@@ -357,16 +377,20 @@ get_args(int *argc, char ***argv) {
strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
- set_bootloader_message(&boot);
+ if (!write_bootloader_message(boot, &err)) {
+ LOGE("%s\n", err.c_str());
+ }
}
static void
set_sdcard_update_bootloader_message() {
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
+ bootloader_message boot = {};
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
- set_bootloader_message(&boot);
+ std::string err;
+ if (!write_bootloader_message(boot, &err)) {
+ LOGE("%s\n", err.c_str());
+ }
}
// Read from kernel log into buffer and write out to file.
@@ -527,9 +551,11 @@ finish_recovery(const char *send_intent) {
copy_logs();
// Reset to normal system boot so recovery won't cycle indefinitely.
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- set_bootloader_message(&boot);
+ bootloader_message boot = {};
+ std::string err;
+ if (!write_bootloader_message(boot, &err)) {
+ LOGE("%s\n", err.c_str());
+ }
// Remove the command file, so recovery won't repeat indefinitely.
if (has_cache) {
@@ -854,46 +880,184 @@ static bool wipe_cache(bool should_confirm, Device* device) {
return success;
}
-static void choose_recovery_file(Device* device) {
- if (!has_cache) {
- ui->Print("No /cache partition found.\n");
- return;
+// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported.
+// Otherwise, it goes with BLKDISCARD (if device supports BLKDISCARDZEROES) or
+// BLKZEROOUT.
+static bool secure_wipe_partition(const std::string& partition) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY)));
+ if (fd.get() == -1) {
+ LOGE("failed to open \"%s\": %s\n", partition.c_str(), strerror(errno));
+ return false;
+ }
+
+ uint64_t range[2] = {0, 0};
+ if (ioctl(fd.get(), BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) {
+ LOGE("failed to get partition size: %s\n", strerror(errno));
+ return false;
+ }
+ printf("Secure-wiping \"%s\" from %" PRIu64 " to %" PRIu64 ".\n",
+ partition.c_str(), range[0], range[1]);
+
+ printf("Trying BLKSECDISCARD...\t");
+ if (ioctl(fd.get(), BLKSECDISCARD, &range) == -1) {
+ printf("failed: %s\n", strerror(errno));
+
+ // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT.
+ unsigned int zeroes;
+ if (ioctl(fd.get(), BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) {
+ printf("Trying BLKDISCARD...\t");
+ if (ioctl(fd.get(), BLKDISCARD, &range) == -1) {
+ printf("failed: %s\n", strerror(errno));
+ return false;
+ }
+ } else {
+ printf("Trying BLKZEROOUT...\t");
+ if (ioctl(fd.get(), BLKZEROOUT, &range) == -1) {
+ printf("failed: %s\n", strerror(errno));
+ return false;
+ }
+ }
+ }
+
+ printf("done\n");
+ return true;
+}
+
+// Check if the wipe package matches expectation:
+// 1. verify the package.
+// 2. check metadata (ota-type, pre-device and serial number if having one).
+static bool check_wipe_package(size_t wipe_package_size) {
+ if (wipe_package_size == 0) {
+ LOGE("wipe_package_size is zero.\n");
+ return false;
+ }
+ std::string wipe_package;
+ std::string err_str;
+ if (!read_wipe_package(&wipe_package, wipe_package_size, &err_str)) {
+ LOGE("Failed to read wipe package: %s\n", err_str.c_str());
+ return false;
+ }
+ if (!verify_package(reinterpret_cast<const unsigned char*>(wipe_package.data()),
+ wipe_package.size())) {
+ LOGE("Failed to verify package.\n");
+ return false;
+ }
+
+ // Extract metadata
+ ZipArchive zip;
+ int err = mzOpenZipArchive(reinterpret_cast<unsigned char*>(&wipe_package[0]),
+ wipe_package.size(), &zip);
+ if (err != 0) {
+ LOGE("Can't open wipe package: %s\n", err != -1 ? strerror(err) : "bad");
+ return false;
+ }
+ std::string metadata;
+ if (!read_metadata_from_package(&zip, &metadata)) {
+ mzCloseZipArchive(&zip);
+ return false;
+ }
+ mzCloseZipArchive(&zip);
+
+ // Check metadata
+ std::vector<std::string> lines = android::base::Split(metadata, "\n");
+ bool ota_type_matched = false;
+ bool device_type_matched = false;
+ bool has_serial_number = false;
+ bool serial_number_matched = false;
+ for (const auto& line : lines) {
+ if (line == "ota-type=BRICK") {
+ ota_type_matched = true;
+ } else if (android::base::StartsWith(line, "pre-device=")) {
+ std::string device_type = line.substr(strlen("pre-device="));
+ char real_device_type[PROPERTY_VALUE_MAX];
+ property_get("ro.build.product", real_device_type, "");
+ device_type_matched = (device_type == real_device_type);
+ } else if (android::base::StartsWith(line, "serialno=")) {
+ std::string serial_no = line.substr(strlen("serialno="));
+ char real_serial_no[PROPERTY_VALUE_MAX];
+ property_get("ro.serialno", real_serial_no, "");
+ has_serial_number = true;
+ serial_number_matched = (serial_no == real_serial_no);
+ }
+ }
+ return ota_type_matched && device_type_matched && (!has_serial_number || serial_number_matched);
+}
+
+// Wipe the current A/B device, with a secure wipe of all the partitions in
+// RECOVERY_WIPE.
+static bool wipe_ab_device(size_t wipe_package_size) {
+ ui->SetBackground(RecoveryUI::ERASING);
+ ui->SetProgressType(RecoveryUI::INDETERMINATE);
+
+ if (!check_wipe_package(wipe_package_size)) {
+ LOGE("Failed to verify wipe package\n");
+ return false;
+ }
+ std::string partition_list;
+ if (!android::base::ReadFileToString(RECOVERY_WIPE, &partition_list)) {
+ LOGE("failed to read \"%s\".\n", RECOVERY_WIPE);
+ return false;
}
+ std::vector<std::string> lines = android::base::Split(partition_list, "\n");
+ for (const std::string& line : lines) {
+ std::string partition = android::base::Trim(line);
+ // Ignore '#' comment or empty lines.
+ if (android::base::StartsWith(partition, "#") || partition.empty()) {
+ continue;
+ }
+
+ // Proceed anyway even if it fails to wipe some partition.
+ secure_wipe_partition(partition);
+ }
+ return true;
+}
+
+static void choose_recovery_file(Device* device) {
// "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry
char* entries[1 + KEEP_LOG_COUNT * 2 + 1];
memset(entries, 0, sizeof(entries));
unsigned int n = 0;
- // Add LAST_LOG_FILE + LAST_LOG_FILE.x
- // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x
- for (int i = 0; i < KEEP_LOG_COUNT; i++) {
- char* log_file;
- 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;
- }
- if ((ensure_path_mounted(log_file) != 0) || (access(log_file, R_OK) == -1)) {
- free(log_file);
- } else {
- entries[n++] = log_file;
- }
+ if (has_cache) {
+ // Add LAST_LOG_FILE + LAST_LOG_FILE.x
+ // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x
+ for (int i = 0; i < KEEP_LOG_COUNT; i++) {
+ char* log_file;
+ 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;
+ }
+ if ((ensure_path_mounted(log_file) != 0) || (access(log_file, R_OK) == -1)) {
+ free(log_file);
+ } else {
+ entries[n++] = log_file;
+ }
- char* kmsg_file;
- 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;
+ char* kmsg_file;
+ 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;
+ }
+ if ((ensure_path_mounted(kmsg_file) != 0) || (access(kmsg_file, R_OK) == -1)) {
+ free(kmsg_file);
+ } else {
+ entries[n++] = kmsg_file;
+ }
}
- if ((ensure_path_mounted(kmsg_file) != 0) || (access(kmsg_file, R_OK) == -1)) {
- free(kmsg_file);
- } else {
- entries[n++] = kmsg_file;
+ } else {
+ // If cache partition is not found, view /tmp/recovery.log instead.
+ ui->Print("No /cache partition found.\n");
+ if (access(TEMPORARY_LOG_FILE, R_OK) == -1) {
+ return;
+ } else{
+ entries[n++] = strdup(TEMPORARY_LOG_FILE);
}
}
@@ -1227,7 +1391,7 @@ static bool is_battery_ok() {
}
static void set_retry_bootloader_message(int retry_count, int argc, char** argv) {
- struct bootloader_message boot {};
+ bootloader_message boot = {};
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
@@ -1246,7 +1410,34 @@ static void set_retry_bootloader_message(int retry_count, int argc, char** argv)
snprintf(buffer, sizeof(buffer), "--retry_count=%d\n", retry_count+1);
strlcat(boot.recovery, buffer, sizeof(boot.recovery));
}
- set_bootloader_message(&boot);
+ std::string err;
+ if (!write_bootloader_message(boot, &err)) {
+ LOGE("%s\n", err.c_str());
+ }
+}
+
+static bool bootreason_in_blacklist() {
+ char bootreason[PROPERTY_VALUE_MAX];
+ if (property_get("ro.boot.bootreason", bootreason, nullptr) > 0) {
+ for (const auto& str : bootreason_blacklist) {
+ if (strcasecmp(str.c_str(), bootreason) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void log_failure_code(ErrorCode code, const char *update_package) {
+ FILE* install_log = fopen_path(TEMPORARY_INSTALL_FILE, "w");
+ if (install_log != nullptr) {
+ fprintf(install_log, "%s\n", update_package);
+ fprintf(install_log, "0\n");
+ fprintf(install_log, "error: %d\n", code);
+ fclose(install_log);
+ } else {
+ LOGE("failed to open last_install: %s\n", strerror(errno));
+ }
}
static ssize_t logbasename(
@@ -1341,6 +1532,8 @@ int main(int argc, char **argv) {
const char *update_package = NULL;
bool should_wipe_data = false;
bool should_wipe_cache = false;
+ bool should_wipe_ab = false;
+ size_t wipe_package_size = 0;
bool show_text = false;
bool sideload = false;
bool sideload_auto_reboot = false;
@@ -1350,7 +1543,8 @@ int main(int argc, char **argv) {
bool security_update = false;
int arg;
- while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
+ int option_index;
+ while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
switch (arg) {
case 'i': send_intent = optarg; break;
case 'n': android::base::ParseInt(optarg, &retry_count, 0); break;
@@ -1373,6 +1567,16 @@ int main(int argc, char **argv) {
case 'p': shutdown_after = true; break;
case 'r': reason = optarg; break;
case 'e': security_update = true; break;
+ case 0: {
+ if (strcmp(OPTIONS[option_index].name, "wipe_ab") == 0) {
+ should_wipe_ab = true;
+ break;
+ } else if (strcmp(OPTIONS[option_index].name, "wipe_package_size") == 0) {
+ android::base::ParseUint(optarg, &wipe_package_size);
+ break;
+ }
+ break;
+ }
case '?':
LOGE("Invalid command argument\n");
continue;
@@ -1459,15 +1663,12 @@ int main(int argc, char **argv) {
BATTERY_OK_PERCENTAGE);
// Log the error code to last_install when installation skips due to
// low battery.
- FILE* install_log = fopen_path(LAST_INSTALL_FILE, "w");
- if (install_log != nullptr) {
- fprintf(install_log, "%s\n", update_package);
- fprintf(install_log, "0\n");
- fprintf(install_log, "error: %d\n", kLowBattery);
- fclose(install_log);
- } else {
- LOGE("failed to open last_install: %s\n", strerror(errno));
- }
+ log_failure_code(kLowBattery, update_package);
+ status = INSTALL_SKIPPED;
+ } else if (bootreason_in_blacklist()) {
+ // Skip update-on-reboot when bootreason is kernel_panic or similar
+ ui->Print("bootreason is in the blacklist; skip OTA installation\n");
+ log_failure_code(kBootreasonInBlacklist, update_package);
status = INSTALL_SKIPPED;
} else {
status = install_package(update_package, &should_wipe_cache,
@@ -1511,6 +1712,10 @@ int main(int argc, char **argv) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
}
+ } else if (should_wipe_ab) {
+ if (!wipe_ab_device(wipe_package_size)) {
+ status = INSTALL_ERROR;
+ }
} else if (sideload) {
// 'adb reboot sideload' acts the same as user presses key combinations
// to enter the sideload mode. When 'sideload-auto-reboot' is used, text