summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format3
-rw-r--r--recovery.cpp180
-rw-r--r--tests/Android.mk2
-rw-r--r--tests/component/uncrypt_test.cpp174
-rw-r--r--uncrypt/uncrypt.cpp2
5 files changed, 264 insertions, 97 deletions
diff --git a/.clang-format b/.clang-format
index b8c642840..532278864 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,6 +1,7 @@
BasedOnStyle: Google
AllowShortBlocksOnASingleLine: false
-AllowShortFunctionsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: true
ColumnLimit: 100
CommentPragmas: NOLINT:.*
diff --git a/recovery.cpp b/recovery.cpp
index e777c46cd..5213301c9 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -512,117 +512,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 59a70c2eb..597307607 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() << ")";