summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk15
-rw-r--r--etc/init.rc2
-rw-r--r--otautil/include/otautil/error_code.h1
-rw-r--r--recovery.cpp54
-rw-r--r--roots.cpp1
-rw-r--r--tests/Android.mk15
-rw-r--r--tests/component/updater_test.cpp81
-rw-r--r--tests/unit/commands_test.cpp1
-rw-r--r--tools/recovery_l10n/res/values-as/strings.xml9
-rw-r--r--tools/recovery_l10n/res/values-en-rCA/strings.xml9
-rw-r--r--tools/recovery_l10n/res/values-en-rXC/strings.xml9
-rw-r--r--tools/recovery_l10n/res/values-or/strings.xml9
-rw-r--r--updater/Android.mk1
-rw-r--r--updater/blockimg.cpp141
-rw-r--r--updater/commands.cpp3
-rw-r--r--updater/include/private/commands.h5
-rw-r--r--updater/install.cpp10
17 files changed, 322 insertions, 44 deletions
diff --git a/Android.mk b/Android.mk
index 9d6a00fca..aa959c909 100644
--- a/Android.mk
+++ b/Android.mk
@@ -111,14 +111,27 @@ LOCAL_SHARED_LIBRARIES := \
include $(BUILD_STATIC_LIBRARY)
+# Health HAL dependency
+health_hal_static_libraries := \
+ android.hardware.health@2.0-impl \
+ android.hardware.health@2.0 \
+ android.hardware.health@1.0 \
+ android.hardware.health@1.0-convert \
+ libhealthstoragedefault \
+ libhidltransport \
+ libhidlbase \
+ libhwbinder_noltopgo \
+ libvndksupport \
+ libbatterymonitor
+
librecovery_static_libraries := \
libbootloader_message \
libfusesideload \
libminui \
libverifier \
libotautil \
+ $(health_hal_static_libraries) \
libasyncio \
- libbatterymonitor \
libcrypto_utils \
libcrypto \
libext4_utils \
diff --git a/etc/init.rc b/etc/init.rc
index 8e18438b5..3821eb6a8 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -77,7 +77,7 @@ on late-init
trigger early-boot
trigger boot
-service ueventd /sbin/ueventd
+service ueventd /system/bin/ueventd
critical
seclabel u:r:ueventd:s0
diff --git a/otautil/include/otautil/error_code.h b/otautil/include/otautil/error_code.h
index b0ff42d8d..0f6c9f85f 100644
--- a/otautil/include/otautil/error_code.h
+++ b/otautil/include/otautil/error_code.h
@@ -48,6 +48,7 @@ enum CauseCode : int {
kRebootFailure,
kPackageExtractFileFailure,
kPatchApplicationFailure,
+ kHashTreeComputationFailure,
kVendorFailure = 200
};
diff --git a/recovery.cpp b/recovery.cpp
index 3828e29b3..cc30035df 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -49,7 +49,7 @@
#include <android-base/unique_fd.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/properties.h> /* for property_list */
-#include <healthd/BatteryMonitor.h>
+#include <health2/Health.h>
#include <ziparchive/zip_archive.h>
#include "adb_install.h"
@@ -876,6 +876,11 @@ void ui_print(const char* format, ...) {
}
static bool is_battery_ok(int* required_battery_level) {
+ using android::hardware::health::V1_0::BatteryStatus;
+ using android::hardware::health::V2_0::Result;
+ using android::hardware::health::V2_0::toString;
+ using android::hardware::health::V2_0::implementation::Health;
+
struct healthd_config healthd_config = {
.batteryStatusPath = android::String8(android::String8::kEmptyString),
.batteryHealthPath = android::String8(android::String8::kEmptyString),
@@ -893,37 +898,52 @@ static bool is_battery_ok(int* required_battery_level) {
.boot_min_cap = 0,
.screen_on = nullptr
};
- healthd_board_init(&healthd_config);
- android::BatteryMonitor monitor;
- monitor.init(&healthd_config);
+ auto health =
+ android::hardware::health::V2_0::implementation::Health::initInstance(&healthd_config);
static constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10;
int wait_second = 0;
while (true) {
- int charge_status = monitor.getChargeStatus();
+ auto charge_status = BatteryStatus::UNKNOWN;
+ health
+ ->getChargeStatus([&charge_status](auto res, auto out_status) {
+ if (res == Result::SUCCESS) {
+ charge_status = out_status;
+ }
+ })
+ .isOk(); // should not have transport error
+
// Treat unknown status as charged.
- bool charged = (charge_status != android::BATTERY_STATUS_DISCHARGING &&
- charge_status != android::BATTERY_STATUS_NOT_CHARGING);
- android::BatteryProperty capacity;
- android::status_t status = monitor.getProperty(android::BATTERY_PROP_CAPACITY, &capacity);
- ui_print("charge_status %d, charged %d, status %d, capacity %" PRId64 "\n", charge_status,
- charged, status, capacity.valueInt64);
+ bool charged = (charge_status != BatteryStatus::DISCHARGING &&
+ charge_status != BatteryStatus::NOT_CHARGING);
+
+ Result res = Result::UNKNOWN;
+ int32_t capacity = INT32_MIN;
+ health
+ ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) {
+ res = out_res;
+ capacity = out_capacity;
+ })
+ .isOk(); // should not have transport error
+
+ ui_print("charge_status %d, charged %d, status %s, capacity %" PRId32 "\n", charge_status,
+ charged, toString(res).c_str(), capacity);
// At startup, the battery drivers in devices like N5X/N6P take some time to load
// the battery profile. Before the load finishes, it reports value 50 as a fake
// capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected
// to finish loading the battery profile earlier than 10 seconds after kernel startup.
- if (status == 0 && capacity.valueInt64 == 50) {
+ if (res == Result::SUCCESS && capacity == 50) {
if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
sleep(1);
wait_second++;
continue;
}
}
- // If we can't read battery percentage, it may be a device without battery. In this situation,
- // use 100 as a fake battery percentage.
- if (status != 0) {
- capacity.valueInt64 = 100;
+ // If we can't read battery percentage, it may be a device without battery. In this
+ // situation, use 100 as a fake battery percentage.
+ if (res != Result::SUCCESS) {
+ capacity = 100;
}
// GmsCore enters recovery mode to install package when having enough battery percentage.
@@ -932,7 +952,7 @@ static bool is_battery_ok(int* required_battery_level) {
static constexpr int BATTERY_OK_PERCENTAGE = 20;
static constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
*required_battery_level = charged ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE;
- return capacity.valueInt64 >= *required_battery_level;
+ return capacity >= *required_battery_level;
}
}
diff --git a/roots.cpp b/roots.cpp
index 2b26bf43e..06a77c186 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -327,6 +327,7 @@ int format_volume(const char* volume, const char* directory) {
"-f",
"-O", "encrypt",
"-O", "quota",
+ "-O", "verity",
"-w", std::to_string(kSectorSize),
v->blk_device,
};
diff --git a/tests/Android.mk b/tests/Android.mk
index 8dd454944..5ef17776c 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -137,6 +137,7 @@ libupdater_static_libraries := \
libext4_utils \
libfec \
libfec_rs \
+ libverity_tree \
libfs_mgr \
libgtest_prod \
liblog \
@@ -153,6 +154,18 @@ libupdater_static_libraries := \
libbrotli \
$(tune2fs_static_libraries)
+health_hal_static_libraries := \
+ android.hardware.health@2.0-impl \
+ android.hardware.health@2.0 \
+ android.hardware.health@1.0 \
+ android.hardware.health@1.0-convert \
+ libhealthstoragedefault \
+ libhidltransport \
+ libhidlbase \
+ libhwbinder_noltopgo \
+ libvndksupport \
+ libbatterymonitor
+
librecovery_static_libraries := \
librecovery \
libbootloader_message \
@@ -163,8 +176,8 @@ librecovery_static_libraries := \
libminui \
libverifier \
libotautil \
+ $(health_hal_static_libraries) \
libasyncio \
- libbatterymonitor \
libcrypto_utils \
libcrypto \
libext4_utils \
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index 9fcf17f13..248b469b0 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -37,6 +37,7 @@
#include <brotli/encode.h>
#include <bsdiff/bsdiff.h>
#include <gtest/gtest.h>
+#include <verity/hash_tree_builder.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_writer.h>
@@ -389,6 +390,86 @@ TEST_F(UpdaterTest, read_file) {
expect("", script, kNoCause);
}
+TEST_F(UpdaterTest, compute_hash_tree_smoke) {
+ std::string data;
+ for (unsigned char i = 0; i < 128; i++) {
+ data += std::string(4096, i);
+ }
+ // Appends an additional block for verity data.
+ data += std::string(4096, 0);
+ ASSERT_EQ(129 * 4096, data.size());
+ ASSERT_TRUE(android::base::WriteStringToFile(data, image_file_));
+
+ std::string salt = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7";
+ std::string expected_root_hash =
+ "7e0a8d8747f54384014ab996f5b2dc4eb7ff00c630eede7134c9e3f05c0dd8ca";
+ // hash_tree_ranges, source_ranges, hash_algorithm, salt_hex, root_hash
+ std::vector<std::string> tokens{ "compute_hash_tree", "2,128,129", "2,0,128", "sha256", salt,
+ expected_root_hash };
+ std::string hash_tree_command = android::base::Join(tokens, " ");
+
+ std::vector<std::string> transfer_list{
+ "4", "2", "0", "2", hash_tree_command,
+ };
+
+ PackageEntries entries{
+ { "new_data", "" },
+ { "patch_data", "" },
+ { "transfer_list", android::base::Join(transfer_list, "\n") },
+ };
+
+ RunBlockImageUpdate(false, entries, image_file_, "t");
+
+ std::string updated;
+ ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated));
+ ASSERT_EQ(129 * 4096, updated.size());
+ ASSERT_EQ(data.substr(0, 128 * 4096), updated.substr(0, 128 * 4096));
+
+ // Computes the SHA256 of the salt + hash_tree_data and expects the result to match with the
+ // root_hash.
+ std::vector<unsigned char> salt_bytes;
+ ASSERT_TRUE(HashTreeBuilder::ParseBytesArrayFromString(salt, &salt_bytes));
+ std::vector<unsigned char> hash_tree = std::move(salt_bytes);
+ hash_tree.insert(hash_tree.end(), updated.begin() + 128 * 4096, updated.end());
+
+ std::vector<unsigned char> digest(SHA256_DIGEST_LENGTH);
+ SHA256(hash_tree.data(), hash_tree.size(), digest.data());
+ ASSERT_EQ(expected_root_hash, HashTreeBuilder::BytesArrayToString(digest));
+}
+
+TEST_F(UpdaterTest, compute_hash_tree_root_mismatch) {
+ std::string data;
+ for (size_t i = 0; i < 128; i++) {
+ data += std::string(4096, i);
+ }
+ // Appends an additional block for verity data.
+ data += std::string(4096, 0);
+ ASSERT_EQ(129 * 4096, data.size());
+ // Corrupts one bit
+ data[4096] = 'A';
+ ASSERT_TRUE(android::base::WriteStringToFile(data, image_file_));
+
+ std::string salt = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7";
+ std::string expected_root_hash =
+ "7e0a8d8747f54384014ab996f5b2dc4eb7ff00c630eede7134c9e3f05c0dd8ca";
+ // hash_tree_ranges, source_ranges, hash_algorithm, salt_hex, root_hash
+ std::vector<std::string> tokens{ "compute_hash_tree", "2,128,129", "2,0,128", "sha256", salt,
+ expected_root_hash };
+ std::string hash_tree_command = android::base::Join(tokens, " ");
+
+ std::vector<std::string> transfer_list{
+ "4", "2", "0", "2", hash_tree_command,
+ };
+
+ PackageEntries entries{
+ { "new_data", "" },
+ { "patch_data", "" },
+ { "transfer_list", android::base::Join(transfer_list, "\n") },
+ };
+
+ RunBlockImageUpdate(false, entries, image_file_, "", kHashTreeComputationFailure);
+}
+
TEST_F(UpdaterTest, write_value) {
// write_value() expects two arguments.
expect(nullptr, "write_value()", kArgsParsingFailure);
diff --git a/tests/unit/commands_test.cpp b/tests/unit/commands_test.cpp
index 3daa58f33..9679a9e73 100644
--- a/tests/unit/commands_test.cpp
+++ b/tests/unit/commands_test.cpp
@@ -30,6 +30,7 @@ TEST(CommandsTest, ParseType) {
ASSERT_EQ(Command::Type::IMGDIFF, Command::ParseType("imgdiff"));
ASSERT_EQ(Command::Type::STASH, Command::ParseType("stash"));
ASSERT_EQ(Command::Type::FREE, Command::ParseType("free"));
+ ASSERT_EQ(Command::Type::COMPUTE_HASH_TREE, Command::ParseType("compute_hash_tree"));
}
TEST(CommandsTest, ParseType_InvalidCommand) {
diff --git a/tools/recovery_l10n/res/values-as/strings.xml b/tools/recovery_l10n/res/values-as/strings.xml
new file mode 100644
index 000000000..2624cebe4
--- /dev/null
+++ b/tools/recovery_l10n/res/values-as/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="recovery_installing" msgid="2013591905463558223">"আপডেইট ইনষ্টল কৰি থকা হৈছে"</string>
+ <string name="recovery_erasing" msgid="7334826894904037088">"মচি থকা হৈছে"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"কোনো আদেশ নাই"</string>
+ <string name="recovery_error" msgid="5748178989622716736">"ত্ৰুটি!"</string>
+ <string name="recovery_installing_security" msgid="9184031299717114342">"সুৰক্ষা আপডেইট ইনষ্টল কৰি থকা হৈছে"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-en-rCA/strings.xml b/tools/recovery_l10n/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000..dc75c2374
--- /dev/null
+++ b/tools/recovery_l10n/res/values-en-rCA/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="recovery_installing" msgid="2013591905463558223">"Installing system update"</string>
+ <string name="recovery_erasing" msgid="7334826894904037088">"Erasing"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"No command"</string>
+ <string name="recovery_error" msgid="5748178989622716736">"Error!"</string>
+ <string name="recovery_installing_security" msgid="9184031299717114342">"Installing security update"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000..2d528b3fb
--- /dev/null
+++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="recovery_installing" msgid="2013591905463558223">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Installing system update‎‏‎‎‏‎"</string>
+ <string name="recovery_erasing" msgid="7334826894904037088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎Erasing‎‏‎‎‏‎"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎"</string>
+ <string name="recovery_error" msgid="5748178989622716736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎"</string>
+ <string name="recovery_installing_security" msgid="9184031299717114342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎"</string>
+</resources>
diff --git a/tools/recovery_l10n/res/values-or/strings.xml b/tools/recovery_l10n/res/values-or/strings.xml
new file mode 100644
index 000000000..2b0851cdd
--- /dev/null
+++ b/tools/recovery_l10n/res/values-or/strings.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="recovery_installing" msgid="2013591905463558223">"ସିଷ୍ଟମ ଅପଡେଟ ଇନଷ୍ଟଲ କରୁଛି"</string>
+ <string name="recovery_erasing" msgid="7334826894904037088">"ଲିଭାଉଛି"</string>
+ <string name="recovery_no_command" msgid="4465476568623024327">"କୌଣସି କମାଣ୍ଡ ନାହିଁ"</string>
+ <string name="recovery_error" msgid="5748178989622716736">"ତ୍ରୁଟି!"</string>
+ <string name="recovery_installing_security" msgid="9184031299717114342">"ସୁରକ୍ଷା ଅପ୍‌ଡେଟ୍‌ ଇନ୍‌ଷ୍ଟଲ୍‌ କରୁଛି"</string>
+</resources>
diff --git a/updater/Android.mk b/updater/Android.mk
index ac9aecb04..78d0bd451 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -34,6 +34,7 @@ updater_common_static_libraries := \
libext4_utils \
libfec \
libfec_rs \
+ libverity_tree \
libfs_mgr \
libgtest_prod \
liblog \
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index 2a2ab19a3..96b2d9feb 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -49,6 +49,7 @@
#include <fec/io.h>
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
+#include <verity/hash_tree_builder.h>
#include <ziparchive/zip_archive.h>
#include "edify/expr.h"
@@ -1495,6 +1496,105 @@ static int PerformCommandAbort(CommandParameters&) {
return -1;
}
+// Computes the hash_tree bytes based on the parameters, checks if the root hash of the tree
+// matches the expected hash and writes the result to the specified range on the block_device.
+// Hash_tree computation arguments:
+// hash_tree_ranges
+// source_ranges
+// hash_algorithm
+// salt_hex
+// root_hash
+static int PerformCommandComputeHashTree(CommandParameters& params) {
+ if (params.cpos + 5 != params.tokens.size()) {
+ LOG(ERROR) << "Invaild arguments count in hash computation " << params.cmdline;
+ return -1;
+ }
+
+ // Expects the hash_tree data to be contiguous.
+ RangeSet hash_tree_ranges = RangeSet::Parse(params.tokens[params.cpos++]);
+ if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
+ LOG(ERROR) << "Invalid hash tree ranges in " << params.cmdline;
+ return -1;
+ }
+
+ RangeSet source_ranges = RangeSet::Parse(params.tokens[params.cpos++]);
+ if (!source_ranges) {
+ LOG(ERROR) << "Invalid source ranges in " << params.cmdline;
+ return -1;
+ }
+
+ auto hash_function = HashTreeBuilder::HashFunction(params.tokens[params.cpos++]);
+ if (hash_function == nullptr) {
+ LOG(ERROR) << "Invalid hash algorithm in " << params.cmdline;
+ return -1;
+ }
+
+ std::vector<unsigned char> salt;
+ std::string salt_hex = params.tokens[params.cpos++];
+ if (salt_hex.empty() || !HashTreeBuilder::ParseBytesArrayFromString(salt_hex, &salt)) {
+ LOG(ERROR) << "Failed to parse salt in " << params.cmdline;
+ return -1;
+ }
+
+ std::string expected_root_hash = params.tokens[params.cpos++];
+ if (expected_root_hash.empty()) {
+ LOG(ERROR) << "Invalid root hash in " << params.cmdline;
+ return -1;
+ }
+
+ // Starts the hash_tree computation.
+ HashTreeBuilder builder(BLOCKSIZE, hash_function);
+ if (!builder.Initialize(source_ranges.blocks() * BLOCKSIZE, salt)) {
+ LOG(ERROR) << "Failed to initialize hash tree computation, source " << source_ranges.ToString()
+ << ", salt " << salt_hex;
+ return -1;
+ }
+
+ // Iterates through every block in the source_ranges and updates the hash tree structure
+ // accordingly.
+ for (const auto& range : source_ranges) {
+ uint8_t buffer[BLOCKSIZE];
+ if (!check_lseek(params.fd, static_cast<off64_t>(range.first) * BLOCKSIZE, SEEK_SET)) {
+ PLOG(ERROR) << "Failed to seek to block: " << range.first;
+ return -1;
+ }
+
+ for (size_t i = range.first; i < range.second; i++) {
+ if (read_all(params.fd, buffer, BLOCKSIZE) == -1) {
+ LOG(ERROR) << "Failed to read data in " << range.first << ":" << range.second;
+ return -1;
+ }
+
+ if (!builder.Update(reinterpret_cast<unsigned char*>(buffer), BLOCKSIZE)) {
+ LOG(ERROR) << "Failed to update hash tree builder";
+ return -1;
+ }
+ }
+ }
+
+ if (!builder.BuildHashTree()) {
+ LOG(ERROR) << "Failed to build hash tree";
+ return -1;
+ }
+
+ std::string root_hash_hex = HashTreeBuilder::BytesArrayToString(builder.root_hash());
+ if (root_hash_hex != expected_root_hash) {
+ LOG(ERROR) << "Root hash of the verity hash tree doesn't match the expected value. Expected: "
+ << expected_root_hash << ", actual: " << root_hash_hex;
+ return -1;
+ }
+
+ uint64_t write_offset = static_cast<uint64_t>(hash_tree_ranges.GetBlockNumber(0)) * BLOCKSIZE;
+ if (params.canwrite && !builder.WriteHashTreeToFd(params.fd, write_offset)) {
+ LOG(ERROR) << "Failed to write hash tree to output";
+ return -1;
+ }
+
+ // TODO(xunchang) validates the written bytes
+
+ return 0;
+}
+
using CommandFunction = std::function<int(CommandParameters&)>;
using CommandMap = std::unordered_map<Command::Type, CommandFunction>;
@@ -1737,6 +1837,9 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
if (performer(params) == -1) {
LOG(ERROR) << "failed to execute command [" << line << "]";
+ if (cmd_type == Command::Type::COMPUTE_HASH_TREE && failure_type == kNoCause) {
+ failure_type = kHashTreeComputationFailure;
+ }
goto pbiudone;
}
@@ -1894,15 +1997,16 @@ Value* BlockImageVerifyFn(const char* name, State* state,
// Commands which are not allowed are set to nullptr to skip them completely.
const CommandMap command_map{
// clang-format off
- { Command::Type::ABORT, PerformCommandAbort },
- { Command::Type::BSDIFF, PerformCommandDiff },
- { Command::Type::ERASE, nullptr },
- { Command::Type::FREE, PerformCommandFree },
- { Command::Type::IMGDIFF, PerformCommandDiff },
- { Command::Type::MOVE, PerformCommandMove },
- { Command::Type::NEW, nullptr },
- { Command::Type::STASH, PerformCommandStash },
- { Command::Type::ZERO, nullptr },
+ { Command::Type::ABORT, PerformCommandAbort },
+ { Command::Type::BSDIFF, PerformCommandDiff },
+ { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree },
+ { Command::Type::ERASE, nullptr },
+ { Command::Type::FREE, PerformCommandFree },
+ { Command::Type::IMGDIFF, PerformCommandDiff },
+ { Command::Type::MOVE, PerformCommandMove },
+ { Command::Type::NEW, nullptr },
+ { Command::Type::STASH, PerformCommandStash },
+ { Command::Type::ZERO, nullptr },
// clang-format on
};
CHECK_EQ(static_cast<size_t>(Command::Type::LAST), command_map.size());
@@ -1915,15 +2019,16 @@ Value* BlockImageUpdateFn(const char* name, State* state,
const std::vector<std::unique_ptr<Expr>>& argv) {
const CommandMap command_map{
// clang-format off
- { Command::Type::ABORT, PerformCommandAbort },
- { Command::Type::BSDIFF, PerformCommandDiff },
- { Command::Type::ERASE, PerformCommandErase },
- { Command::Type::FREE, PerformCommandFree },
- { Command::Type::IMGDIFF, PerformCommandDiff },
- { Command::Type::MOVE, PerformCommandMove },
- { Command::Type::NEW, PerformCommandNew },
- { Command::Type::STASH, PerformCommandStash },
- { Command::Type::ZERO, PerformCommandZero },
+ { Command::Type::ABORT, PerformCommandAbort },
+ { Command::Type::BSDIFF, PerformCommandDiff },
+ { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree },
+ { Command::Type::ERASE, PerformCommandErase },
+ { Command::Type::FREE, PerformCommandFree },
+ { Command::Type::IMGDIFF, PerformCommandDiff },
+ { Command::Type::MOVE, PerformCommandMove },
+ { Command::Type::NEW, PerformCommandNew },
+ { Command::Type::STASH, PerformCommandStash },
+ { Command::Type::ZERO, PerformCommandZero },
// clang-format on
};
CHECK_EQ(static_cast<size_t>(Command::Type::LAST), command_map.size());
diff --git a/updater/commands.cpp b/updater/commands.cpp
index e88149678..15a787c51 100644
--- a/updater/commands.cpp
+++ b/updater/commands.cpp
@@ -40,6 +40,8 @@ Command::Type Command::ParseType(const std::string& type_str) {
return Type::ABORT;
} else if (type_str == "bsdiff") {
return Type::BSDIFF;
+ } else if (type_str == "compute_hash_tree") {
+ return Type::COMPUTE_HASH_TREE;
} else if (type_str == "erase") {
return Type::ERASE;
} else if (type_str == "free") {
@@ -175,6 +177,7 @@ Command Command::Parse(const std::string& line, size_t index, std::string* err)
SourceInfo source_info;
StashInfo stash_info;
+ // TODO(xunchang) add the parse code of compute_hash_tree
if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
// zero/new/erase <rangeset>
if (pos + 1 != tokens.size()) {
diff --git a/updater/include/private/commands.h b/updater/include/private/commands.h
index 087d7cfbf..7f9dc79f4 100644
--- a/updater/include/private/commands.h
+++ b/updater/include/private/commands.h
@@ -213,6 +213,10 @@ class PatchInfo {
// - Free the given stash data.
// - Meaningful args: StashInfo
//
+// compute_hash_tree <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash>
+// - Computes the hash_tree bytes and writes the result to the specified range on the
+// block_device.
+//
// abort
// - Abort the current update. Allowed for testing code only.
//
@@ -221,6 +225,7 @@ class Command {
enum class Type {
ABORT,
BSDIFF,
+ COMPUTE_HASH_TREE,
ERASE,
FREE,
IMGDIFF,
diff --git a/updater/install.cpp b/updater/install.cpp
index 088d24b31..f9333459b 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -497,12 +497,10 @@ Value* FormatFn(const char* name, State* state, const std::vector<std::unique_pt
const char* f2fs_argv[] = { "mkfs.f2fs",
"-d1",
"-f",
- "-O",
- "encrypt",
- "-O",
- "quota",
- "-w",
- "512",
+ "-O", "encrypt",
+ "-O", "quota",
+ "-O", "verity",
+ "-w", "512",
location.c_str(),
(size < 512) ? nullptr : num_sectors.c_str(),
nullptr };