Merge "Add proto3 support for care_map" am: 8d9b3aec3d
am: f3480024c2
Change-Id: Id695d0d49b75c4f0a828bef01bedc36281e15939
This commit is contained in:
+2
-1
@@ -113,7 +113,8 @@ LOCAL_SRC_FILES := \
|
|||||||
component/verifier_test.cpp
|
component/verifier_test.cpp
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
libhidlbase
|
libhidlbase \
|
||||||
|
libprotobuf-cpp-lite
|
||||||
|
|
||||||
tune2fs_static_libraries := \
|
tune2fs_static_libraries := \
|
||||||
libext2_com_err \
|
libext2_com_err \
|
||||||
|
|||||||
@@ -14,14 +14,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <update_verifier/update_verifier.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <android-base/file.h>
|
#include <android-base/file.h>
|
||||||
#include <android-base/properties.h>
|
#include <android-base/properties.h>
|
||||||
#include <android-base/strings.h>
|
#include <android-base/strings.h>
|
||||||
#include <android-base/test_utils.h>
|
#include <android-base/test_utils.h>
|
||||||
|
#include <google/protobuf/repeated_field.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <update_verifier/update_verifier.h>
|
|
||||||
|
#include "care_map.pb.h"
|
||||||
|
|
||||||
class UpdateVerifierTest : public ::testing::Test {
|
class UpdateVerifierTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
@@ -30,7 +36,30 @@ class UpdateVerifierTest : public ::testing::Test {
|
|||||||
verity_supported = android::base::EqualsIgnoreCase(verity_mode, "enforcing");
|
verity_supported = android::base::EqualsIgnoreCase(verity_mode, "enforcing");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a serialized string of the proto3 message according to the given partition info.
|
||||||
|
std::string ConstructProto(
|
||||||
|
std::vector<std::unordered_map<std::string, std::string>>& partitions) {
|
||||||
|
UpdateVerifier::CareMap result;
|
||||||
|
for (const auto& partition : partitions) {
|
||||||
|
UpdateVerifier::CareMap::PartitionInfo info;
|
||||||
|
if (partition.find("name") != partition.end()) {
|
||||||
|
info.set_name(partition.at("name"));
|
||||||
|
}
|
||||||
|
if (partition.find("ranges") != partition.end()) {
|
||||||
|
info.set_ranges(partition.at("ranges"));
|
||||||
|
}
|
||||||
|
if (partition.find("fingerprint") != partition.end()) {
|
||||||
|
info.set_fingerprint(partition.at("fingerprint"));
|
||||||
|
}
|
||||||
|
|
||||||
|
*result.add_partitions() = info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.SerializeAsString();
|
||||||
|
}
|
||||||
|
|
||||||
bool verity_supported;
|
bool verity_supported;
|
||||||
|
TemporaryFile care_map_file;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(UpdateVerifierTest, verify_image_no_care_map) {
|
TEST_F(UpdateVerifierTest, verify_image_no_care_map) {
|
||||||
@@ -45,26 +74,26 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TemporaryFile temp_file;
|
|
||||||
std::string content = "system\n2,0,1";
|
std::string content = "system\n2,0,1";
|
||||||
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
|
ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path));
|
||||||
ASSERT_TRUE(verify_image(temp_file.path));
|
ASSERT_TRUE(verify_image(care_map_file.path));
|
||||||
|
|
||||||
// Leading and trailing newlines should be accepted.
|
// Leading and trailing newlines should be accepted.
|
||||||
ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", temp_file.path));
|
ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", care_map_file.path));
|
||||||
ASSERT_TRUE(verify_image(temp_file.path));
|
ASSERT_TRUE(verify_image(care_map_file.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UpdateVerifierTest, verify_image_empty_care_map) {
|
||||||
|
ASSERT_FALSE(verify_image(care_map_file.path));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UpdateVerifierTest, verify_image_wrong_lines) {
|
TEST_F(UpdateVerifierTest, verify_image_wrong_lines) {
|
||||||
// The care map file can have only 2 / 4 / 6 lines.
|
// The care map file can have only 2 / 4 / 6 lines.
|
||||||
TemporaryFile temp_file;
|
ASSERT_TRUE(android::base::WriteStringToFile("line1", care_map_file.path));
|
||||||
ASSERT_FALSE(verify_image(temp_file.path));
|
ASSERT_FALSE(verify_image(care_map_file.path));
|
||||||
|
|
||||||
ASSERT_TRUE(android::base::WriteStringToFile("line1", temp_file.path));
|
ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", care_map_file.path));
|
||||||
ASSERT_FALSE(verify_image(temp_file.path));
|
ASSERT_FALSE(verify_image(care_map_file.path));
|
||||||
|
|
||||||
ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", temp_file.path));
|
|
||||||
ASSERT_FALSE(verify_image(temp_file.path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
|
TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
|
||||||
@@ -74,10 +103,9 @@ TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TemporaryFile temp_file;
|
|
||||||
std::string content = "system\n2,1,0";
|
std::string content = "system\n2,1,0";
|
||||||
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
|
ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path));
|
||||||
ASSERT_FALSE(verify_image(temp_file.path));
|
ASSERT_FALSE(verify_image(care_map_file.path));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
|
TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
|
||||||
@@ -87,8 +115,55 @@ TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TemporaryFile temp_file;
|
|
||||||
std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0";
|
std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0";
|
||||||
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
|
ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path));
|
||||||
ASSERT_TRUE(verify_image(temp_file.path));
|
ASSERT_TRUE(verify_image(care_map_file.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) {
|
||||||
|
// This test relies on dm-verity support.
|
||||||
|
if (!verity_supported) {
|
||||||
|
GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unordered_map<std::string, std::string>> partitions = {
|
||||||
|
{ { "name", "system" }, { "ranges", "2,0,1" } },
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string proto = ConstructProto(partitions);
|
||||||
|
ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path));
|
||||||
|
ASSERT_TRUE(verify_image(care_map_file.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_missing_name) {
|
||||||
|
// This test relies on dm-verity support.
|
||||||
|
if (!verity_supported) {
|
||||||
|
GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unordered_map<std::string, std::string>> partitions = {
|
||||||
|
{ { "ranges", "2,0,1" } },
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string proto = ConstructProto(partitions);
|
||||||
|
ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path));
|
||||||
|
ASSERT_FALSE(verify_image(care_map_file.path));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_bad_ranges) {
|
||||||
|
// This test relies on dm-verity support.
|
||||||
|
if (!verity_supported) {
|
||||||
|
GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unordered_map<std::string, std::string>> partitions = {
|
||||||
|
{ { "name", "system" }, { "ranges", "3,0,1" } },
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string proto = ConstructProto(partitions);
|
||||||
|
ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path));
|
||||||
|
ASSERT_FALSE(verify_image(care_map_file.path));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ cc_library_static {
|
|||||||
],
|
],
|
||||||
|
|
||||||
srcs: [
|
srcs: [
|
||||||
|
"care_map.proto",
|
||||||
"update_verifier.cpp",
|
"update_verifier.cpp",
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -49,6 +50,11 @@ cc_library_static {
|
|||||||
"libbase",
|
"libbase",
|
||||||
"libcutils",
|
"libcutils",
|
||||||
],
|
],
|
||||||
|
|
||||||
|
proto: {
|
||||||
|
type: "lite",
|
||||||
|
export_proto_headers: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cc_binary {
|
cc_binary {
|
||||||
@@ -74,6 +80,7 @@ cc_binary {
|
|||||||
"libhardware",
|
"libhardware",
|
||||||
"libhidlbase",
|
"libhidlbase",
|
||||||
"liblog",
|
"liblog",
|
||||||
|
"libprotobuf-cpp-lite",
|
||||||
"libutils",
|
"libutils",
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package UpdateVerifier;
|
||||||
|
option optimize_for = LITE_RUNTIME;
|
||||||
|
|
||||||
|
message CareMap {
|
||||||
|
message PartitionInfo {
|
||||||
|
string name = 1;
|
||||||
|
string ranges = 2;
|
||||||
|
string id = 3;
|
||||||
|
string fingerprint = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
repeated PartitionInfo partitions = 1;
|
||||||
|
}
|
||||||
@@ -20,5 +20,13 @@
|
|||||||
|
|
||||||
int update_verifier(int argc, char** argv);
|
int update_verifier(int argc, char** argv);
|
||||||
|
|
||||||
// Exposed for testing purpose.
|
// Returns true to indicate a passing verification (or the error should be ignored); Otherwise
|
||||||
|
// returns false on fatal errors, where we should reject the current boot and trigger a fallback.
|
||||||
|
// This function tries to process the care_map.txt as protobuf message; and falls back to use the
|
||||||
|
// plain text format if the parse failed.
|
||||||
|
//
|
||||||
|
// Note that update_verifier should be backward compatible to not reject care_map.txt from old
|
||||||
|
// releases, which could otherwise fail to boot into the new release. For example, we've changed
|
||||||
|
// the care_map format between N and O. An O update_verifier would fail to work with N care_map.txt.
|
||||||
|
// This could be a result of sideloading an O OTA while the device having a pending N update.
|
||||||
bool verify_image(const std::string& care_map_name);
|
bool verify_image(const std::string& care_map_name);
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
#include <android/hardware/boot/1.0/IBootControl.h>
|
#include <android/hardware/boot/1.0/IBootControl.h>
|
||||||
#include <cutils/android_reboot.h>
|
#include <cutils/android_reboot.h>
|
||||||
|
|
||||||
|
#include "care_map.pb.h"
|
||||||
#include "otautil/rangeset.h"
|
#include "otautil/rangeset.h"
|
||||||
|
|
||||||
using android::sp;
|
using android::sp;
|
||||||
@@ -189,33 +190,12 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true to indicate a passing verification (or the error should be ignored); Otherwise
|
static bool process_care_map_plain_text(const std::string& care_map_contents) {
|
||||||
// returns false on fatal errors, where we should reject the current boot and trigger a fallback.
|
|
||||||
// Note that update_verifier should be backward compatible to not reject care_map.txt from old
|
|
||||||
// releases, which could otherwise fail to boot into the new release. For example, we've changed
|
|
||||||
// the care_map format between N and O. An O update_verifier would fail to work with N
|
|
||||||
// care_map.txt. This could be a result of sideloading an O OTA while the device having a pending N
|
|
||||||
// update.
|
|
||||||
bool verify_image(const std::string& care_map_name) {
|
|
||||||
android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
|
|
||||||
// If the device is flashed before the current boot, it may not have care_map.txt
|
|
||||||
// in /data/ota_package. To allow the device to continue booting in this situation,
|
|
||||||
// we should print a warning and skip the block verification.
|
|
||||||
if (care_map_fd.get() == -1) {
|
|
||||||
PLOG(WARNING) << "Failed to open " << care_map_name;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// care_map file has up to six lines, where every two lines make a pair. Within each pair, the
|
// care_map file has up to six lines, where every two lines make a pair. Within each pair, the
|
||||||
// first line has the partition name (e.g. "system"), while the second line holds the ranges of
|
// first line has the partition name (e.g. "system"), while the second line holds the ranges of
|
||||||
// all the blocks to verify.
|
// all the blocks to verify.
|
||||||
std::string file_content;
|
std::vector<std::string> lines =
|
||||||
if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) {
|
android::base::Split(android::base::Trim(care_map_contents), "\n");
|
||||||
LOG(ERROR) << "Error reading care map contents to string.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> lines;
|
|
||||||
lines = android::base::Split(android::base::Trim(file_content), "\n");
|
|
||||||
if (lines.size() != 2 && lines.size() != 4 && lines.size() != 6) {
|
if (lines.size() != 2 && lines.size() != 4 && lines.size() != 6) {
|
||||||
LOG(ERROR) << "Invalid lines in care_map: found " << lines.size()
|
LOG(ERROR) << "Invalid lines in care_map: found " << lines.size()
|
||||||
<< " lines, expecting 2 or 4 or 6 lines.";
|
<< " lines, expecting 2 or 4 or 6 lines.";
|
||||||
@@ -237,6 +217,50 @@ bool verify_image(const std::string& care_map_name) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool verify_image(const std::string& care_map_name) {
|
||||||
|
android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
|
||||||
|
// If the device is flashed before the current boot, it may not have care_map.txt in
|
||||||
|
// /data/ota_package. To allow the device to continue booting in this situation, we should
|
||||||
|
// print a warning and skip the block verification.
|
||||||
|
if (care_map_fd.get() == -1) {
|
||||||
|
PLOG(WARNING) << "Failed to open " << care_map_name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string file_content;
|
||||||
|
if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) {
|
||||||
|
PLOG(ERROR) << "Failed to read " << care_map_name;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_content.empty()) {
|
||||||
|
LOG(ERROR) << "Unexpected empty care map";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateVerifier::CareMap care_map;
|
||||||
|
// Falls back to use the plain text version if we cannot parse the file as protobuf message.
|
||||||
|
if (!care_map.ParseFromString(file_content)) {
|
||||||
|
return process_care_map_plain_text(file_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& partition : care_map.partitions()) {
|
||||||
|
if (partition.name().empty()) {
|
||||||
|
LOG(ERROR) << "Unexpected empty partition name.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (partition.ranges().empty()) {
|
||||||
|
LOG(ERROR) << "Unexpected block ranges for partition " << partition.name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!read_blocks(partition.name(), partition.ranges())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int reboot_device() {
|
static int reboot_device() {
|
||||||
if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) == -1) {
|
if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) == -1) {
|
||||||
LOG(ERROR) << "Failed to reboot.";
|
LOG(ERROR) << "Failed to reboot.";
|
||||||
|
|||||||
Reference in New Issue
Block a user