diff options
-rw-r--r-- | bootloader_message/include/bootloader_message/bootloader_message.h | 5 | ||||
-rw-r--r-- | install.cpp | 9 | ||||
-rw-r--r-- | recovery.cpp | 196 | ||||
-rw-r--r-- | tests/Android.mk | 2 | ||||
-rw-r--r-- | tests/component/uncrypt_test.cpp | 174 | ||||
-rw-r--r-- | uncrypt/uncrypt.cpp | 2 | ||||
-rw-r--r-- | updater/blockimg.cpp | 2 |
7 files changed, 267 insertions, 123 deletions
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index b3d2182df..ec47facf6 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -42,8 +42,9 @@ static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; * It is also updated by the bootloader when firmware update * is complete (to boot into recovery for any final cleanup) * - * The status field is written by the bootloader after the - * completion of an "update-radio" or "update-hboot" command. + * The status field was used by the bootloader after the completion + * of an "update-radio" or "update-hboot" command, which has been + * deprecated since Froyo. * * The recovery field is only written by linux and used * for the system to send a message to recovery or the diff --git a/install.cpp b/install.cpp index dd6ed81cf..b23586c6e 100644 --- a/install.cpp +++ b/install.cpp @@ -335,15 +335,6 @@ try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache, // progress bar within the segment defined by the most // recent progress command. // - // firmware <"hboot"|"radio"> <filename> - // arrange to install the contents of <filename> in the - // given partition on reboot. - // - // (API v2: <filename> may start with "PACKAGE:" to - // indicate taking a file from the OTA package.) - // - // (API v3: this command no longer exists.) - // // ui_print <string> // display <string> on the screen. // diff --git a/recovery.cpp b/recovery.cpp index 9152bb128..0fdc31cb4 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -170,21 +170,7 @@ struct selabel_handle* sehandle; * -- after this, rebooting will (try to) restart the main system -- * 7. ** if install failed ** * 7a. prompt_and_wait() shows an error icon and waits for the user - * 7b; the user reboots (pulling the battery, etc) into the main system - * 8. main() calls maybe_install_firmware_update() - * ** if the update contained radio/hboot firmware **: - * 8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache" - * -- after this, rebooting will reformat cache & restart main system -- - * 8b. m_i_f_u() writes firmware image into raw cache partition - * 8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache" - * -- after this, rebooting will attempt to reinstall firmware -- - * 8d. bootloader tries to flash firmware - * 8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache") - * -- after this, rebooting will reformat cache & restart main system -- - * 8f. erase_volume() reformats /cache - * 8g. finish_recovery() erases BCB - * -- after this, rebooting will (try to) restart the main system -- - * 9. main() calls reboot() to boot main system + * 7b. the user reboots (pulling the battery, etc) into the main system */ // open a given path, mounting partitions as necessary @@ -513,117 +499,107 @@ static void finish_recovery() { sync(); // For good measure. } -typedef struct _saved_log_file { - char* name; - struct stat st; - unsigned char* data; - struct _saved_log_file* next; -} saved_log_file; +struct saved_log_file { + std::string name; + struct stat sb; + std::string data; +}; static bool erase_volume(const char* volume) { - bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); - bool is_data = (strcmp(volume, DATA_ROOT) == 0); + bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); + bool is_data = (strcmp(volume, DATA_ROOT) == 0); - ui->SetBackground(RecoveryUI::ERASING); - ui->SetProgressType(RecoveryUI::INDETERMINATE); + ui->SetBackground(RecoveryUI::ERASING); + ui->SetProgressType(RecoveryUI::INDETERMINATE); - saved_log_file* head = NULL; - - if (is_cache) { - // If we're reformatting /cache, we load any past logs - // (i.e. "/cache/recovery/last_*") and the current log - // ("/cache/recovery/log") into memory, so we can restore them after - // the reformat. - - ensure_path_mounted(volume); - - DIR* d; - struct dirent* de; - d = opendir(CACHE_LOG_DIR); - if (d) { - char path[PATH_MAX]; - strcpy(path, CACHE_LOG_DIR); - strcat(path, "/"); - int path_len = strlen(path); - while ((de = readdir(d)) != NULL) { - if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) { - saved_log_file* p = (saved_log_file*) malloc(sizeof(saved_log_file)); - strcpy(path+path_len, de->d_name); - p->name = strdup(path); - if (stat(path, &(p->st)) == 0) { - // truncate files to 512kb - if (p->st.st_size > (1 << 19)) { - p->st.st_size = 1 << 19; - } - p->data = (unsigned char*) malloc(p->st.st_size); - FILE* f = fopen(path, "rb"); - fread(p->data, 1, p->st.st_size, f); - fclose(f); - p->next = head; - head = p; - } else { - free(p); - } - } - } - closedir(d); - } else { - if (errno != ENOENT) { - printf("opendir failed: %s\n", strerror(errno)); + std::vector<saved_log_file> log_files; + + if (is_cache) { + // If we're reformatting /cache, we load any past logs + // (i.e. "/cache/recovery/last_*") and the current log + // ("/cache/recovery/log") into memory, so we can restore them after + // the reformat. + + ensure_path_mounted(volume); + + struct dirent* de; + std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir); + if (d) { + while ((de = readdir(d.get())) != nullptr) { + if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) { + std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name); + + struct stat sb; + if (stat(path.c_str(), &sb) == 0) { + // truncate files to 512kb + if (sb.st_size > (1 << 19)) { + sb.st_size = 1 << 19; } + + std::string data(sb.st_size, '\0'); + FILE* f = fopen(path.c_str(), "rb"); + fread(&data[0], 1, data.size(), f); + fclose(f); + + log_files.emplace_back(saved_log_file{ path, sb, data }); + } } + } + } else { + if (errno != ENOENT) { + PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR; + } } + } - ui->Print("Formatting %s...\n", volume); + ui->Print("Formatting %s...\n", volume); - ensure_path_unmounted(volume); + ensure_path_unmounted(volume); - int result; + 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 - if (mkdir(CONVERT_FBE_DIR, 0700) != 0) { - ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno)); - return true; - } - FILE* f = fopen(CONVERT_FBE_FILE, "wb"); - if (!f) { - ui->Print("Failed to convert to file encryption %s\n", strerror(errno)); - 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_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 + if (mkdir(CONVERT_FBE_DIR, 0700) != 0) { + ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno)); + return true; } + FILE* f = fopen(CONVERT_FBE_FILE, "wb"); + if (!f) { + ui->Print("Failed to convert to file encryption %s\n", strerror(errno)); + 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) { - FILE* f = fopen_path(head->name, "wb"); - if (f) { - fwrite(head->data, 1, head->st.st_size, f); - fclose(f); - chmod(head->name, head->st.st_mode); - chown(head->name, head->st.st_uid, head->st.st_gid); - } - free(head->name); - free(head->data); - saved_log_file* temp = head->next; - free(head); - head = temp; + if (is_cache) { + // Re-create the log dir and write back the log entries. + if (ensure_path_mounted(CACHE_LOG_DIR) == 0 && + dirCreateHierarchy(CACHE_LOG_DIR, 0777, nullptr, false, sehandle) == 0) { + for (const auto& log : log_files) { + if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid, + log.sb.st_gid)) { + PLOG(ERROR) << "Failed to write to " << log.name; } - - // Any part of the log we'd copied to cache is now gone. - // Reset the pointer so we copy from the beginning of the temp - // log. - tmplog_offset = 0; - copy_logs(); + } + } else { + PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR; } - return (result == 0); + // Any part of the log we'd copied to cache is now gone. + // Reset the pointer so we copy from the beginning of the temp + // log. + tmplog_offset = 0; + copy_logs(); + } + + return (result == 0); } static int diff --git a/tests/Android.mk b/tests/Android.mk index 5f6a7ce0c..1621f37bf 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -62,8 +62,10 @@ LOCAL_C_INCLUDES := bootable/recovery LOCAL_SRC_FILES := \ component/applypatch_test.cpp \ component/edify_test.cpp \ + component/uncrypt_test.cpp \ component/updater_test.cpp \ component/verifier_test.cpp + LOCAL_FORCE_STATIC_EXECUTABLE := true tune2fs_static_libraries := \ diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp new file mode 100644 index 000000000..a554c3e48 --- /dev/null +++ b/tests/component/uncrypt_test.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#include <string> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/unique_fd.h> +#include <bootloader_message/bootloader_message.h> +#include <gtest/gtest.h> + +static const std::string UNCRYPT_SOCKET = "/dev/socket/uncrypt"; +static const std::string INIT_SVC_SETUP_BCB = "init.svc.setup-bcb"; +static const std::string INIT_SVC_CLEAR_BCB = "init.svc.clear-bcb"; +static const std::string INIT_SVC_UNCRYPT = "init.svc.uncrypt"; +static constexpr int SOCKET_CONNECTION_MAX_RETRY = 30; + +class UncryptTest : public ::testing::Test { + protected: + virtual void SetUp() { + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt")); + + bool success = false; + for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { + std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, ""); + std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, ""); + std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, ""); + LOG(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb << "] uncrypt: [" + << uncrypt << "]"; + if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") { + success = true; + break; + } + sleep(1); + } + + ASSERT_TRUE(success) << "uncrypt service is not available."; + } +}; + +TEST_F(UncryptTest, setup_bcb) { + // Trigger the setup-bcb service. + ASSERT_TRUE(android::base::SetProperty("ctl.start", "setup-bcb")); + + // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). + sleep(1); + + struct sockaddr_un un = {}; + un.sun_family = AF_UNIX; + strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); + + int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + ASSERT_NE(-1, sockfd); + + // Connect to the uncrypt socket. + bool success = false; + for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { + if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&un), sizeof(struct sockaddr_un)) != 0) { + success = true; + break; + } + sleep(1); + } + ASSERT_TRUE(success); + + // Send out the BCB message. + std::string message = "--update_message=abc value"; + std::string message_in_bcb = "recovery\n--update_message=abc value\n"; + int length = static_cast<int>(message.size()); + int length_out = htonl(length); + ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int))) + << "Failed to write length: " << strerror(errno); + ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length)) + << "Failed to write message: " << strerror(errno); + + // Check the status code from uncrypt. + int status; + ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); + ASSERT_EQ(100U, ntohl(status)); + + // Ack having received the status code. + int code = 0; + ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); + + ASSERT_EQ(0, close(sockfd)); + + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); + + // Verify the message by reading from BCB directly. + bootloader_message boot; + std::string err; + ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + + ASSERT_EQ("boot-recovery", std::string(boot.command)); + ASSERT_EQ(message_in_bcb, std::string(boot.recovery)); + + // The rest of the boot.recovery message should be zero'd out. + ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery)); + size_t left = sizeof(boot.recovery) - message_in_bcb.size(); + ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left)); + + // Clear the BCB. + ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; +} + +TEST_F(UncryptTest, clear_bcb) { + // Trigger the clear-bcb service. + ASSERT_TRUE(android::base::SetProperty("ctl.start", "clear-bcb")); + + // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). + sleep(1); + + struct sockaddr_un un = {}; + un.sun_family = AF_UNIX; + strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); + + int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + ASSERT_NE(-1, sockfd); + + // Connect to the uncrypt socket. + bool success = false; + for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { + if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&un), sizeof(struct sockaddr_un)) != 0) { + success = true; + break; + } + sleep(1); + } + ASSERT_TRUE(success); + + // Check the status code from uncrypt. + int status; + ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); + ASSERT_EQ(100U, ntohl(status)); + + // Ack having received the status code. + int code = 0; + ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); + + ASSERT_EQ(0, close(sockfd)); + + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); + + // Verify the content by reading from BCB directly. + bootloader_message boot; + std::string err; + ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + + // All the bytes should be cleared. + ASSERT_EQ(std::string(sizeof(boot), '\0'), + std::string(reinterpret_cast<const char*>(&boot), sizeof(boot))); +} diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 38b25abc7..4ac516d21 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -530,7 +530,7 @@ static bool setup_bcb(const int socket) { std::string content; content.resize(length); if (!android::base::ReadFully(socket, &content[0], length)) { - PLOG(ERROR) << "failed to read the length"; + PLOG(ERROR) << "failed to read the message"; return false; } LOG(INFO) << " received command: [" << content << "] (" << content.size() << ")"; diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 7257e2399..4dadceb5e 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1358,7 +1358,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, int /* arg CommandParameters params = {}; params.canwrite = !dryrun; - LOG(INFO) << "performing " << dryrun ? "verification" : "update"; + LOG(INFO) << "performing " << (dryrun ? "verification" : "update"); if (state->is_retry) { is_retry = true; LOG(INFO) << "This update is a retry."; |