Skip QD1A.190821.011 in stage-aosp-master
Bug: 141248619
Change-Id: Ic52aa0aa6dcfa6832d3ce9c5b9374ea844d17118
diff --git a/.clang-format b/.clang-format
index c1244fe..3b6a627 100644
--- a/.clang-format
+++ b/.clang-format
@@ -34,5 +34,6 @@
BinPackParameters: false
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
+IncludeBlocks: Preserve
PointerAlignment: Left
TabWidth: 2
diff --git a/Android.bp b/Android.bp
index 284845d..a691e7e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -128,6 +128,7 @@
"libverity_tree",
],
shared_libs: [
+ "libziparchive",
"libbase",
"libcrypto",
"libfec",
@@ -164,6 +165,7 @@
"common/utils.cc",
"payload_consumer/bzip_extent_writer.cc",
"payload_consumer/cached_file_descriptor.cc",
+ "payload_consumer/certificate_parser_android.cc",
"payload_consumer/delta_performer.cc",
"payload_consumer/download_action.cc",
"payload_consumer/extent_reader.cc",
@@ -191,16 +193,24 @@
name: "libupdate_engine_boot_control_exports",
defaults: ["update_metadata-protos_exports"],
- static_libs: ["update_metadata-protos"],
+ static_libs: [
+ "libsnapshot",
+ "update_metadata-protos",
+ ],
shared_libs: [
"libbootloader_message",
"libfs_mgr",
- "libhwbinder",
"libhidlbase",
"liblp",
"libutils",
"android.hardware.boot@1.0",
],
+ target: {
+ recovery: {
+ static_libs: ["libsnapshot_nobinder"],
+ exclude_static_libs: ["libsnapshot"],
+ },
+ },
}
cc_library_static {
@@ -214,6 +224,7 @@
srcs: [
"boot_control_android.cc",
"dynamic_partition_control_android.cc",
+ "dynamic_partition_utils.cc",
],
}
@@ -293,7 +304,10 @@
],
static_libs: ["libupdate_engine_android"],
- required: ["cacerts_google"],
+ required: [
+ "cacerts_google",
+ "update_engine_payload_key",
+ ],
srcs: ["main.cc"],
init_rc: ["update_engine.rc"],
@@ -358,7 +372,6 @@
recovery: {
exclude_shared_libs: [
"libprotobuf-cpp-lite",
- "libhwbinder",
"libbrillo-stream",
"libbrillo",
"libchrome",
@@ -366,7 +379,9 @@
},
},
- required: ["android.hardware.boot@1.0-impl-wrapper.recovery"],
+ required: [
+ "update_engine_payload_key.recovery",
+ ],
}
// libupdate_engine_client (type: shared_library)
@@ -400,8 +415,7 @@
],
srcs: [
- "binder_bindings/android/brillo/IUpdateEngine.aidl",
- "binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl",
+ ":libupdate_engine_client_aidl",
"client_library/client.cc",
"client_library/client_binder.cc",
"parcelable_update_engine_status.cc",
@@ -409,6 +423,15 @@
],
}
+filegroup {
+ name: "libupdate_engine_client_aidl",
+ srcs: [
+ "binder_bindings/android/brillo/IUpdateEngine.aidl",
+ "binder_bindings/android/brillo/IUpdateEngineStatusCallback.aidl",
+ ],
+ path: "binder_bindings",
+}
+
// update_engine_client (type: executable)
// ========================================================
// update_engine console client.
@@ -544,8 +567,6 @@
gtest: false,
stem: "delta_generator",
- relative_install_path: "update_engine_unittests",
- no_named_install_directory: true,
}
// test_http_server (type: executable)
@@ -560,8 +581,6 @@
],
gtest: false,
- relative_install_path: "update_engine_unittests",
- no_named_install_directory: true,
}
// test_subprocess (type: executable)
@@ -573,8 +592,6 @@
srcs: ["test_subprocess.cc"],
gtest: false,
- relative_install_path: "update_engine_unittests",
- no_named_install_directory: true,
}
// Public keys for unittests.
@@ -583,16 +600,19 @@
name: "ue_unittest_keys",
cmd: "openssl rsa -in $(location unittest_key.pem) -pubout -out $(location unittest_key.pub.pem) &&" +
"openssl rsa -in $(location unittest_key2.pem) -pubout -out $(location unittest_key2.pub.pem) &&" +
- "openssl rsa -in $(location unittest_key_RSA4096.pem) -pubout -out $(location unittest_key_RSA4096.pub.pem)",
+ "openssl rsa -in $(location unittest_key_RSA4096.pem) -pubout -out $(location unittest_key_RSA4096.pub.pem) &&" +
+ "openssl pkey -in $(location unittest_key_EC.pem) -pubout -out $(location unittest_key_EC.pub.pem)",
srcs: [
"unittest_key.pem",
"unittest_key2.pem",
"unittest_key_RSA4096.pem",
+ "unittest_key_EC.pem",
],
out: [
"unittest_key.pub.pem",
"unittest_key2.pub.pem",
"unittest_key_RSA4096.pub.pem",
+ "unittest_key_EC.pub.pem",
],
}
@@ -622,11 +642,6 @@
"libpayload_generator_exports",
"libupdate_engine_android_exports",
],
- required: [
- "test_http_server",
- "test_subprocess",
- "ue_unittest_delta_generator",
- ],
static_libs: [
"libpayload_generator",
@@ -635,19 +650,30 @@
"libchrome_test_helpers",
"libupdate_engine_android",
],
- shared_libs: [
- "libhidltransport",
+
+ header_libs: [
+ "libstorage_literals_headers",
],
data: [
+ ":test_http_server",
+ ":test_subprocess",
+ ":ue_unittest_delta_generator",
":ue_unittest_disk_imgs",
":ue_unittest_keys",
+ "otacerts.zip",
"unittest_key.pem",
"unittest_key2.pem",
"unittest_key_RSA4096.pem",
+ "unittest_key_EC.pem",
"update_engine.conf",
],
+ // We cannot use the default generated AndroidTest.xml because of the use of helper modules
+ // (i.e. test_http_server, test_subprocess, ue_unittest_delta_generator).
+ test_config: "test_config.xml",
+ test_suites: ["device-tests"],
+
srcs: [
"boot_control_android_unittest.cc",
"certificate_checker_unittest.cc",
@@ -667,8 +693,10 @@
"common/terminator_unittest.cc",
"common/test_utils.cc",
"common/utils_unittest.cc",
+ "dynamic_partition_control_android_unittest.cc",
"payload_consumer/bzip_extent_writer_unittest.cc",
"payload_consumer/cached_file_descriptor_unittest.cc",
+ "payload_consumer/certificate_parser_android_unittest.cc",
"payload_consumer/delta_performer_integration_test.cc",
"payload_consumer/delta_performer_unittest.cc",
"payload_consumer/extent_reader_unittest.cc",
diff --git a/binder_bindings/android/os/IUpdateEngine.aidl b/binder_bindings/android/os/IUpdateEngine.aidl
index c0e29f5..1305079 100644
--- a/binder_bindings/android/os/IUpdateEngine.aidl
+++ b/binder_bindings/android/os/IUpdateEngine.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IUpdateEngineCallback;
+import android.os.ParcelFileDescriptor;
/** @hide */
interface IUpdateEngine {
@@ -26,6 +27,11 @@
in long payload_size,
in String[] headerKeyValuePairs);
/** @hide */
+ void applyPayloadFd(in ParcelFileDescriptor pfd,
+ in long payload_offset,
+ in long payload_size,
+ in String[] headerKeyValuePairs);
+ /** @hide */
boolean bind(IUpdateEngineCallback callback);
/** @hide */
boolean unbind(IUpdateEngineCallback callback);
diff --git a/binder_service_android.cc b/binder_service_android.cc
index 137694a..88bc1f2 100644
--- a/binder_service_android.cc
+++ b/binder_service_android.cc
@@ -24,6 +24,9 @@
using android::binder::Status;
using android::os::IUpdateEngineCallback;
+using android::os::ParcelFileDescriptor;
+using std::string;
+using std::vector;
using update_engine::UpdateEngineStatus;
namespace {
@@ -94,9 +97,9 @@
const android::String16& url,
int64_t payload_offset,
int64_t payload_size,
- const std::vector<android::String16>& header_kv_pairs) {
- const std::string payload_url{android::String8{url}.string()};
- std::vector<std::string> str_headers;
+ const vector<android::String16>& header_kv_pairs) {
+ const string payload_url{android::String8{url}.string()};
+ vector<string> str_headers;
str_headers.reserve(header_kv_pairs.size());
for (const auto& header : header_kv_pairs) {
str_headers.emplace_back(android::String8{header}.string());
@@ -110,6 +113,25 @@
return Status::ok();
}
+Status BinderUpdateEngineAndroidService::applyPayloadFd(
+ const ParcelFileDescriptor& pfd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<android::String16>& header_kv_pairs) {
+ vector<string> str_headers;
+ str_headers.reserve(header_kv_pairs.size());
+ for (const auto& header : header_kv_pairs) {
+ str_headers.emplace_back(android::String8{header}.string());
+ }
+
+ brillo::ErrorPtr error;
+ if (!service_delegate_->ApplyPayload(
+ pfd.get(), payload_offset, payload_size, str_headers, &error)) {
+ return ErrorPtrToStatus(error);
+ }
+ return Status::ok();
+}
+
Status BinderUpdateEngineAndroidService::suspend() {
brillo::ErrorPtr error;
if (!service_delegate_->SuspendUpdate(&error))
diff --git a/binder_service_android.h b/binder_service_android.h
index d8c4e9c..0dda93b 100644
--- a/binder_service_android.h
+++ b/binder_service_android.h
@@ -53,6 +53,11 @@
int64_t payload_offset,
int64_t payload_size,
const std::vector<android::String16>& header_kv_pairs) override;
+ android::binder::Status applyPayloadFd(
+ const ::android::os::ParcelFileDescriptor& pfd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<android::String16>& header_kv_pairs) override;
android::binder::Status bind(
const android::sp<android::os::IUpdateEngineCallback>& callback,
bool* return_value) override;
diff --git a/boot_control_android.cc b/boot_control_android.cc
index 1fab85f..4a010bd 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -22,11 +22,10 @@
#include <base/bind.h>
#include <base/logging.h>
-#include <base/strings/string_util.h>
#include <bootloader_message/bootloader_message.h>
#include <brillo/message_loops/message_loop.h>
-#include <fs_mgr.h>
#include <fs_mgr_overlayfs.h>
+#include <libdm/dm.h>
#include "update_engine/common/utils.h"
#include "update_engine/dynamic_partition_control_android.h"
@@ -34,15 +33,12 @@
using std::string;
using android::dm::DmDeviceState;
-using android::fs_mgr::Partition;
using android::hardware::hidl_string;
using android::hardware::Return;
using android::hardware::boot::V1_0::BoolResult;
using android::hardware::boot::V1_0::CommandResult;
using android::hardware::boot::V1_0::IBootControl;
using Slot = chromeos_update_engine::BootControlInterface::Slot;
-using PartitionMetadata =
- chromeos_update_engine::BootControlInterface::PartitionMetadata;
namespace {
@@ -111,9 +107,9 @@
Slot slot,
const string& partition_name_suffix) const {
string source_device =
- device_dir.Append(fs_mgr_get_super_partition_name(slot)).value();
- auto source_metadata = dynamic_control_->LoadMetadataBuilder(
- source_device, slot, BootControlInterface::kInvalidSlot);
+ device_dir.Append(dynamic_control_->GetSuperPartitionName(slot)).value();
+ auto source_metadata =
+ dynamic_control_->LoadMetadataBuilder(source_device, slot);
return source_metadata->HasBlockDevice(partition_name_suffix);
}
@@ -124,10 +120,9 @@
Slot slot,
string* device) const {
string super_device =
- device_dir.Append(fs_mgr_get_super_partition_name(slot)).value();
+ device_dir.Append(dynamic_control_->GetSuperPartitionName(slot)).value();
- auto builder = dynamic_control_->LoadMetadataBuilder(
- super_device, slot, BootControlInterface::kInvalidSlot);
+ auto builder = dynamic_control_->LoadMetadataBuilder(super_device, slot);
if (builder == nullptr) {
LOG(ERROR) << "No metadata in slot "
@@ -143,8 +138,8 @@
if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
LOG(ERROR) << "The static partition " << partition_name_suffix
<< " is a block device for current metadata ("
- << fs_mgr_get_super_partition_name(current_slot) << ", slot "
- << BootControlInterface::SlotName(current_slot)
+ << dynamic_control_->GetSuperPartitionName(current_slot)
+ << ", slot " << BootControlInterface::SlotName(current_slot)
<< "). It cannot be used as a logical partition.";
return DynamicPartitionDeviceStatus::ERROR;
}
@@ -196,7 +191,7 @@
// current payload doesn't encode them as dynamic partitions. This may happen
// when applying a retrofit update on top of a dynamic-partitions-enabled
// build.
- if (dynamic_control_->IsDynamicPartitionsEnabled() &&
+ if (dynamic_control_->GetDynamicPartitionsFeatureFlag().IsEnabled() &&
(slot == GetCurrentSlot() || is_target_dynamic_)) {
switch (GetDynamicPartitionDevice(
device_dir, partition_name_suffix, slot, device)) {
@@ -250,6 +245,10 @@
}
bool BootControlAndroid::SetActiveBootSlot(Slot slot) {
+ if (slot != GetCurrentSlot() && !dynamic_control_->FinishUpdate()) {
+ return false;
+ }
+
CommandResult result;
auto ret = module_->setActiveBootSlot(slot, StoreResultCallback(&result));
if (!ret.isOk()) {
@@ -280,113 +279,9 @@
brillo::MessageLoop::kTaskIdNull;
}
-namespace {
-
-bool UpdatePartitionMetadata(DynamicPartitionControlInterface* dynamic_control,
- Slot source_slot,
- Slot target_slot,
- const string& target_suffix,
- const PartitionMetadata& partition_metadata) {
- string device_dir_str;
- if (!dynamic_control->GetDeviceDir(&device_dir_str)) {
- return false;
- }
- base::FilePath device_dir(device_dir_str);
- auto source_device =
- device_dir.Append(fs_mgr_get_super_partition_name(source_slot)).value();
-
- auto builder = dynamic_control->LoadMetadataBuilder(
- source_device, source_slot, target_slot);
- if (builder == nullptr) {
- // TODO(elsk): allow reconstructing metadata from partition_metadata
- // in recovery sideload.
- LOG(ERROR) << "No metadata at "
- << BootControlInterface::SlotName(source_slot);
- return false;
- }
-
- std::vector<string> groups = builder->ListGroups();
- for (const auto& group_name : groups) {
- if (base::EndsWith(
- group_name, target_suffix, base::CompareCase::SENSITIVE)) {
- LOG(INFO) << "Removing group " << group_name;
- builder->RemoveGroupAndPartitions(group_name);
- }
- }
-
- uint64_t total_size = 0;
- for (const auto& group : partition_metadata.groups) {
- total_size += group.size;
- }
-
- string expr;
- uint64_t allocatable_space = builder->AllocatableSpace();
- if (!dynamic_control->IsDynamicPartitionsRetrofit()) {
- allocatable_space /= 2;
- expr = "half of ";
- }
- if (total_size > allocatable_space) {
- LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
- << " (" << total_size << ") has exceeded " << expr
- << " allocatable space for dynamic partitions "
- << allocatable_space << ".";
- return false;
- }
-
- for (const auto& group : partition_metadata.groups) {
- auto group_name_suffix = group.name + target_suffix;
- if (!builder->AddGroup(group_name_suffix, group.size)) {
- LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
- << group.size;
- return false;
- }
- LOG(INFO) << "Added group " << group_name_suffix << " with size "
- << group.size;
-
- for (const auto& partition : group.partitions) {
- auto partition_name_suffix = partition.name + target_suffix;
- Partition* p = builder->AddPartition(
- partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
- if (!p) {
- LOG(ERROR) << "Cannot add partition " << partition_name_suffix
- << " to group " << group_name_suffix;
- return false;
- }
- if (!builder->ResizePartition(p, partition.size)) {
- LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
- << " to size " << partition.size << ". Not enough space?";
- return false;
- }
- LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
- << group_name_suffix << " with size " << partition.size;
- }
- }
-
- auto target_device =
- device_dir.Append(fs_mgr_get_super_partition_name(target_slot)).value();
- return dynamic_control->StoreMetadata(
- target_device, builder.get(), target_slot);
-}
-
-bool UnmapTargetPartitions(DynamicPartitionControlInterface* dynamic_control,
- const string& target_suffix,
- const PartitionMetadata& partition_metadata) {
- for (const auto& group : partition_metadata.groups) {
- for (const auto& partition : group.partitions) {
- if (!dynamic_control->UnmapPartitionOnDeviceMapper(
- partition.name + target_suffix, true /* wait */)) {
- return false;
- }
- }
- }
- return true;
-}
-
-} // namespace
-
-bool BootControlAndroid::InitPartitionMetadata(
+bool BootControlAndroid::PreparePartitionsForUpdate(
Slot target_slot,
- const PartitionMetadata& partition_metadata,
+ const DeltaArchiveManifest& manifest,
bool update_metadata) {
if (fs_mgr_overlayfs_is_setup()) {
// Non DAP devices can use overlayfs as well.
@@ -395,20 +290,20 @@
"resources.\n"
<< "run adb enable-verity to deactivate if required and try again.";
}
- if (!dynamic_control_->IsDynamicPartitionsEnabled()) {
+ if (!dynamic_control_->GetDynamicPartitionsFeatureFlag().IsEnabled()) {
return true;
}
auto source_slot = GetCurrentSlot();
if (target_slot == source_slot) {
- LOG(ERROR) << "Cannot call InitPartitionMetadata on current slot.";
+ LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot.";
return false;
}
// Although the current build supports dynamic partitions, the given payload
// doesn't use it for target partitions. This could happen when applying a
// retrofit update. Skip updating the partition metadata for the target slot.
- is_target_dynamic_ = !partition_metadata.groups.empty();
+ is_target_dynamic_ = !manifest.dynamic_partition_metadata().groups().empty();
if (!is_target_dynamic_) {
return true;
}
@@ -417,23 +312,8 @@
return true;
}
- string target_suffix;
- if (!GetSuffix(target_slot, &target_suffix)) {
- return false;
- }
-
- // Unmap all the target dynamic partitions because they would become
- // inconsistent with the new metadata.
- if (!UnmapTargetPartitions(
- dynamic_control_.get(), target_suffix, partition_metadata)) {
- return false;
- }
-
- return UpdatePartitionMetadata(dynamic_control_.get(),
- source_slot,
- target_slot,
- target_suffix,
- partition_metadata);
+ return dynamic_control_->PreparePartitionsForUpdate(
+ source_slot, target_slot, manifest);
}
} // namespace chromeos_update_engine
diff --git a/boot_control_android.h b/boot_control_android.h
index a6f33be..65543ca 100644
--- a/boot_control_android.h
+++ b/boot_control_android.h
@@ -51,9 +51,9 @@
bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
- bool InitPartitionMetadata(Slot slot,
- const PartitionMetadata& partition_metadata,
- bool update_metadata) override;
+ bool PreparePartitionsForUpdate(Slot slot,
+ const DeltaArchiveManifest& manifest,
+ bool update_metadata) override;
void Cleanup() override;
private:
@@ -84,7 +84,7 @@
const std::string& partition_name_suffix) const;
// Whether the target partitions should be loaded as dynamic partitions. Set
- // by InitPartitionMetadata() per each update.
+ // by PreparePartitionsForUpdate() per each update.
bool is_target_dynamic_{false};
DISALLOW_COPY_AND_ASSIGN(BootControlAndroid);
diff --git a/boot_control_android_unittest.cc b/boot_control_android_unittest.cc
index b2885a3..f090de2 100644
--- a/boot_control_android_unittest.cc
+++ b/boot_control_android_unittest.cc
@@ -26,236 +26,22 @@
#include <gtest/gtest.h>
#include <libdm/dm.h>
+#include "update_engine/dynamic_partition_test_utils.h"
#include "update_engine/mock_boot_control_hal.h"
#include "update_engine/mock_dynamic_partition_control.h"
using android::dm::DmDeviceState;
-using android::fs_mgr::MetadataBuilder;
using android::hardware::Void;
using std::string;
using testing::_;
using testing::AnyNumber;
-using testing::Contains;
-using testing::Eq;
using testing::Invoke;
-using testing::Key;
-using testing::MakeMatcher;
-using testing::Matcher;
-using testing::MatcherInterface;
-using testing::MatchResultListener;
using testing::NiceMock;
using testing::Not;
using testing::Return;
namespace chromeos_update_engine {
-constexpr const uint32_t kMaxNumSlots = 2;
-constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
-constexpr const char* kFakeDevicePath = "/fake/dev/path/";
-constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/";
-constexpr const uint32_t kFakeMetadataSize = 65536;
-constexpr const char* kDefaultGroup = "foo";
-
-// A map describing the size of each partition.
-// "{name, size}"
-using PartitionSizes = std::map<string, uint64_t>;
-
-// "{name_a, size}"
-using PartitionSuffixSizes = std::map<string, uint64_t>;
-
-using PartitionMetadata = BootControlInterface::PartitionMetadata;
-
-// C++ standards do not allow uint64_t (aka unsigned long) to be the parameter
-// of user-defined literal operators.
-constexpr unsigned long long operator"" _MiB(unsigned long long x) { // NOLINT
- return x << 20;
-}
-constexpr unsigned long long operator"" _GiB(unsigned long long x) { // NOLINT
- return x << 30;
-}
-
-constexpr uint64_t kDefaultGroupSize = 5_GiB;
-// Super device size. 1 MiB for metadata.
-constexpr uint64_t kDefaultSuperSize = kDefaultGroupSize * 2 + 1_MiB;
-
-template <typename U, typename V>
-std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
- os << "{";
- bool first = true;
- for (const auto& pair : param) {
- if (!first)
- os << ", ";
- os << pair.first << ":" << pair.second;
- first = false;
- }
- return os << "}";
-}
-
-template <typename T>
-std::ostream& operator<<(std::ostream& os, const std::vector<T>& param) {
- os << "[";
- bool first = true;
- for (const auto& e : param) {
- if (!first)
- os << ", ";
- os << e;
- first = false;
- }
- return os << "]";
-}
-
-std::ostream& operator<<(std::ostream& os,
- const PartitionMetadata::Partition& p) {
- return os << "{" << p.name << ", " << p.size << "}";
-}
-
-std::ostream& operator<<(std::ostream& os, const PartitionMetadata::Group& g) {
- return os << "{" << g.name << ", " << g.size << ", " << g.partitions << "}";
-}
-
-std::ostream& operator<<(std::ostream& os, const PartitionMetadata& m) {
- return os << m.groups;
-}
-
-inline string GetDevice(const string& name) {
- return kFakeDevicePath + name;
-}
-
-inline string GetDmDevice(const string& name) {
- return kFakeDmDevicePath + name;
-}
-
-// TODO(elsk): fs_mgr_get_super_partition_name should be mocked.
-inline string GetSuperDevice(uint32_t slot) {
- return GetDevice(fs_mgr_get_super_partition_name(slot));
-}
-
-struct TestParam {
- uint32_t source;
- uint32_t target;
-};
-std::ostream& operator<<(std::ostream& os, const TestParam& param) {
- return os << "{source: " << param.source << ", target:" << param.target
- << "}";
-}
-
-// To support legacy tests, auto-convert {name_a: size} map to
-// PartitionMetadata.
-PartitionMetadata partitionSuffixSizesToMetadata(
- const PartitionSuffixSizes& partition_sizes) {
- PartitionMetadata metadata;
- for (const char* suffix : kSlotSuffixes) {
- metadata.groups.push_back(
- {string(kDefaultGroup) + suffix, kDefaultGroupSize, {}});
- }
- for (const auto& pair : partition_sizes) {
- for (size_t suffix_idx = 0; suffix_idx < kMaxNumSlots; ++suffix_idx) {
- if (base::EndsWith(pair.first,
- kSlotSuffixes[suffix_idx],
- base::CompareCase::SENSITIVE)) {
- metadata.groups[suffix_idx].partitions.push_back(
- {pair.first, pair.second});
- }
- }
- }
- return metadata;
-}
-
-// To support legacy tests, auto-convert {name: size} map to PartitionMetadata.
-PartitionMetadata partitionSizesToMetadata(
- const PartitionSizes& partition_sizes) {
- PartitionMetadata metadata;
- metadata.groups.push_back({string{kDefaultGroup}, kDefaultGroupSize, {}});
- for (const auto& pair : partition_sizes) {
- metadata.groups[0].partitions.push_back({pair.first, pair.second});
- }
- return metadata;
-}
-
-std::unique_ptr<MetadataBuilder> NewFakeMetadata(
- const PartitionMetadata& metadata) {
- auto builder =
- MetadataBuilder::New(kDefaultSuperSize, kFakeMetadataSize, kMaxNumSlots);
- EXPECT_GE(builder->AllocatableSpace(), kDefaultGroupSize * 2);
- EXPECT_NE(nullptr, builder);
- if (builder == nullptr)
- return nullptr;
- for (const auto& group : metadata.groups) {
- EXPECT_TRUE(builder->AddGroup(group.name, group.size));
- for (const auto& partition : group.partitions) {
- auto p = builder->AddPartition(partition.name, group.name, 0 /* attr */);
- EXPECT_TRUE(p && builder->ResizePartition(p, partition.size));
- }
- }
- return builder;
-}
-
-class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
- public:
- explicit MetadataMatcher(const PartitionSuffixSizes& partition_sizes)
- : partition_metadata_(partitionSuffixSizesToMetadata(partition_sizes)) {}
- explicit MetadataMatcher(const PartitionMetadata& partition_metadata)
- : partition_metadata_(partition_metadata) {}
-
- bool MatchAndExplain(MetadataBuilder* metadata,
- MatchResultListener* listener) const override {
- bool success = true;
- for (const auto& group : partition_metadata_.groups) {
- for (const auto& partition : group.partitions) {
- auto p = metadata->FindPartition(partition.name);
- if (p == nullptr) {
- if (!success)
- *listener << "; ";
- *listener << "No partition " << partition.name;
- success = false;
- continue;
- }
- if (p->size() != partition.size) {
- if (!success)
- *listener << "; ";
- *listener << "Partition " << partition.name << " has size "
- << p->size() << ", expected " << partition.size;
- success = false;
- }
- if (p->group_name() != group.name) {
- if (!success)
- *listener << "; ";
- *listener << "Partition " << partition.name << " has group "
- << p->group_name() << ", expected " << group.name;
- success = false;
- }
- }
- }
- return success;
- }
-
- void DescribeTo(std::ostream* os) const override {
- *os << "expect: " << partition_metadata_;
- }
-
- void DescribeNegationTo(std::ostream* os) const override {
- *os << "expect not: " << partition_metadata_;
- }
-
- private:
- PartitionMetadata partition_metadata_;
-};
-
-inline Matcher<MetadataBuilder*> MetadataMatches(
- const PartitionSuffixSizes& partition_sizes) {
- return MakeMatcher(new MetadataMatcher(partition_sizes));
-}
-
-inline Matcher<MetadataBuilder*> MetadataMatches(
- const PartitionMetadata& partition_metadata) {
- return MakeMatcher(new MetadataMatcher(partition_metadata));
-}
-
-MATCHER_P(HasGroup, group, " has group " + group) {
- auto groups = arg->ListGroups();
- return std::find(groups.begin(), groups.end(), group) != groups.end();
-}
-
class BootControlAndroidTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -274,10 +60,10 @@
return Void();
}));
- ON_CALL(dynamicControl(), IsDynamicPartitionsEnabled())
- .WillByDefault(Return(true));
- ON_CALL(dynamicControl(), IsDynamicPartitionsRetrofit())
- .WillByDefault(Return(false));
+ ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+ ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
ON_CALL(dynamicControl(), DeviceExists(_)).WillByDefault(Return(true));
ON_CALL(dynamicControl(), GetDeviceDir(_))
.WillByDefault(Invoke([](auto path) {
@@ -289,6 +75,13 @@
*device = GetDmDevice(partition_name_suffix);
return true;
}));
+
+ ON_CALL(dynamicControl(), GetSuperPartitionName(_))
+ .WillByDefault(Return(kFakeSuper));
+ }
+
+ std::string GetSuperDevice(uint32_t slot) {
+ return GetDevice(dynamicControl().GetSuperPartitionName(slot));
}
// Return the mocked HAL module.
@@ -305,53 +98,14 @@
// Set the fake metadata to return when LoadMetadataBuilder is called on
// |slot|.
void SetMetadata(uint32_t slot, const PartitionSuffixSizes& sizes) {
- SetMetadata(slot, partitionSuffixSizesToMetadata(sizes));
- }
-
- void SetMetadata(uint32_t slot, const PartitionMetadata& metadata) {
EXPECT_CALL(dynamicControl(),
- LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
+ LoadMetadataBuilder(GetSuperDevice(slot), slot))
.Times(AnyNumber())
- .WillRepeatedly(Invoke([metadata](auto, auto, auto) {
- return NewFakeMetadata(metadata);
+ .WillRepeatedly(Invoke([sizes](auto, auto) {
+ return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes));
}));
}
- // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
- // slot with each partition in |partitions|.
- void ExpectUnmap(const std::set<string>& partitions) {
- // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
- ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_, _))
- .WillByDefault(Return(false));
-
- for (const auto& partition : partitions) {
- EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition, _))
- .WillOnce(Invoke([this](auto partition, auto) {
- mapped_devices_.erase(partition);
- return true;
- }));
- }
- }
-
- void ExpectDevicesAreMapped(const std::set<string>& partitions) {
- ASSERT_EQ(partitions.size(), mapped_devices_.size());
- for (const auto& partition : partitions) {
- EXPECT_THAT(mapped_devices_, Contains(Key(Eq(partition))))
- << "Expect that " << partition << " is mapped, but it is not.";
- }
- }
-
- void ExpectStoreMetadata(const PartitionSuffixSizes& partition_sizes) {
- ExpectStoreMetadataMatch(MetadataMatches(partition_sizes));
- }
-
- virtual void ExpectStoreMetadataMatch(
- const Matcher<MetadataBuilder*>& matcher) {
- EXPECT_CALL(dynamicControl(),
- StoreMetadata(GetSuperDevice(target()), matcher, target()))
- .WillOnce(Return(true));
- }
-
uint32_t source() { return slots_.source; }
uint32_t target() { return slots_.target; }
@@ -369,28 +123,17 @@
ON_CALL(module(), getCurrentSlot()).WillByDefault(Invoke([this] {
return source();
}));
- // Should not store metadata to source slot.
- EXPECT_CALL(dynamicControl(),
- StoreMetadata(GetSuperDevice(source()), _, source()))
- .Times(0);
- // Should not load metadata from target slot.
- EXPECT_CALL(dynamicControl(),
- LoadMetadataBuilder(GetSuperDevice(target()), target(), _))
- .Times(0);
}
- bool InitPartitionMetadata(uint32_t slot,
- PartitionSizes partition_sizes,
- bool update_metadata = true) {
- auto m = partitionSizesToMetadata(partition_sizes);
- LOG(INFO) << m;
- return bootctl_.InitPartitionMetadata(slot, m, update_metadata);
+ bool PreparePartitionsForUpdate(uint32_t slot,
+ PartitionSizes partition_sizes,
+ bool update_metadata = true) {
+ auto m = PartitionSizesToManifest(partition_sizes);
+ return bootctl_.PreparePartitionsForUpdate(slot, m, update_metadata);
}
BootControlAndroid bootctl_; // BootControlAndroid under test.
TestParam slots_;
- // mapped devices through MapPartitionOnDeviceMapper.
- std::map<string, string> mapped_devices_;
};
class BootControlAndroidTestP
@@ -403,125 +146,6 @@
}
};
-// Test resize case. Grow if target metadata contains a partition with a size
-// less than expected.
-TEST_P(BootControlAndroidTestP, NeedGrowIfSizeNotMatchWhenResizing) {
- SetMetadata(source(),
- {{S("system"), 2_GiB},
- {S("vendor"), 1_GiB},
- {T("system"), 2_GiB},
- {T("vendor"), 1_GiB}});
- ExpectStoreMetadata({{S("system"), 2_GiB},
- {S("vendor"), 1_GiB},
- {T("system"), 3_GiB},
- {T("vendor"), 1_GiB}});
- ExpectUnmap({T("system"), T("vendor")});
-
- EXPECT_TRUE(
- InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 1_GiB}}));
-}
-
-// Test resize case. Shrink if target metadata contains a partition with a size
-// greater than expected.
-TEST_P(BootControlAndroidTestP, NeedShrinkIfSizeNotMatchWhenResizing) {
- SetMetadata(source(),
- {{S("system"), 2_GiB},
- {S("vendor"), 1_GiB},
- {T("system"), 2_GiB},
- {T("vendor"), 1_GiB}});
- ExpectStoreMetadata({{S("system"), 2_GiB},
- {S("vendor"), 1_GiB},
- {T("system"), 2_GiB},
- {T("vendor"), 150_MiB}});
- ExpectUnmap({T("system"), T("vendor")});
-
- EXPECT_TRUE(InitPartitionMetadata(target(),
- {{"system", 2_GiB}, {"vendor", 150_MiB}}));
-}
-
-// Test adding partitions on the first run.
-TEST_P(BootControlAndroidTestP, AddPartitionToEmptyMetadata) {
- SetMetadata(source(), PartitionSuffixSizes{});
- ExpectStoreMetadata({{T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
- ExpectUnmap({T("system"), T("vendor")});
-
- EXPECT_TRUE(
- InitPartitionMetadata(target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
-}
-
-// Test subsequent add case.
-TEST_P(BootControlAndroidTestP, AddAdditionalPartition) {
- SetMetadata(source(), {{S("system"), 2_GiB}, {T("system"), 2_GiB}});
- ExpectStoreMetadata(
- {{S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
- ExpectUnmap({T("system"), T("vendor")});
-
- EXPECT_TRUE(
- InitPartitionMetadata(target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}));
-}
-
-// Test delete one partition.
-TEST_P(BootControlAndroidTestP, DeletePartition) {
- SetMetadata(source(),
- {{S("system"), 2_GiB},
- {S("vendor"), 1_GiB},
- {T("system"), 2_GiB},
- {T("vendor"), 1_GiB}});
- // No T("vendor")
- ExpectStoreMetadata(
- {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}, {T("system"), 2_GiB}});
- ExpectUnmap({T("system")});
-
- EXPECT_TRUE(InitPartitionMetadata(target(), {{"system", 2_GiB}}));
-}
-
-// Test delete all partitions.
-TEST_P(BootControlAndroidTestP, DeleteAll) {
- SetMetadata(source(),
- {{S("system"), 2_GiB},
- {S("vendor"), 1_GiB},
- {T("system"), 2_GiB},
- {T("vendor"), 1_GiB}});
- ExpectStoreMetadata({{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
-
- EXPECT_TRUE(InitPartitionMetadata(target(), {}));
-}
-
-// Test corrupt source metadata case.
-TEST_P(BootControlAndroidTestP, CorruptedSourceMetadata) {
- EXPECT_CALL(dynamicControl(),
- LoadMetadataBuilder(GetSuperDevice(source()), source(), _))
- .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
- ExpectUnmap({T("system")});
-
- EXPECT_FALSE(InitPartitionMetadata(target(), {{"system", 1_GiB}}))
- << "Should not be able to continue with corrupt source metadata";
-}
-
-// Test that InitPartitionMetadata fail if there is not enough space on the
-// device.
-TEST_P(BootControlAndroidTestP, NotEnoughSpace) {
- SetMetadata(source(),
- {{S("system"), 3_GiB},
- {S("vendor"), 2_GiB},
- {T("system"), 0},
- {T("vendor"), 0}});
- EXPECT_FALSE(
- InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 3_GiB}}))
- << "Should not be able to fit 11GiB data into 10GiB space";
-}
-
-TEST_P(BootControlAndroidTestP, NotEnoughSpaceForSlot) {
- SetMetadata(source(),
- {{S("system"), 1_GiB},
- {S("vendor"), 1_GiB},
- {T("system"), 0},
- {T("vendor"), 0}});
- EXPECT_FALSE(
- InitPartitionMetadata(target(), {{"system", 3_GiB}, {"vendor", 3_GiB}}))
- << "Should not be able to grow over size of super / 2";
-}
-
// Test applying retrofit update on a build with dynamic partitions enabled.
TEST_P(BootControlAndroidTestP,
ApplyRetrofitUpdateOnDynamicPartitionsEnabledBuild) {
@@ -530,16 +154,10 @@
{S("vendor"), 1_GiB},
{T("system"), 2_GiB},
{T("vendor"), 1_GiB}});
- // Should not try to unmap any target partition.
- EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_, _)).Times(0);
- // Should not store metadata to target slot.
- EXPECT_CALL(dynamicControl(),
- StoreMetadata(GetSuperDevice(target()), _, target()))
- .Times(0);
- // Not calling through BootControlAndroidTest::InitPartitionMetadata(), since
- // we don't want any default group in the PartitionMetadata.
- EXPECT_TRUE(bootctl_.InitPartitionMetadata(target(), {}, true));
+ // Not calling through BootControlAndroidTest::PreparePartitionsForUpdate(),
+ // since we don't want any default group in the PartitionMetadata.
+ EXPECT_TRUE(bootctl_.PreparePartitionsForUpdate(target(), {}, true));
// Should use dynamic source partitions.
EXPECT_CALL(dynamicControl(), GetState(S("system")))
@@ -578,10 +196,8 @@
{S("vendor"), 1_GiB},
{T("system"), 2_GiB},
{T("vendor"), 1_GiB}});
- EXPECT_CALL(dynamicControl(),
- StoreMetadata(GetSuperDevice(target()), _, target()))
- .Times(0);
- EXPECT_TRUE(InitPartitionMetadata(
+
+ EXPECT_TRUE(PreparePartitionsForUpdate(
target(), {{"system", 2_GiB}, {"vendor", 1_GiB}}, false));
// Dynamic partition "system".
@@ -622,241 +238,10 @@
BootControlAndroidTestP,
testing::Values(TestParam{0, 1}, TestParam{1, 0}));
-const PartitionSuffixSizes update_sizes_0() {
- // Initial state is 0 for "other" slot.
- return {
- {"grown_a", 2_GiB},
- {"shrunk_a", 1_GiB},
- {"same_a", 100_MiB},
- {"deleted_a", 150_MiB},
- // no added_a
- {"grown_b", 200_MiB},
- // simulate system_other
- {"shrunk_b", 0},
- {"same_b", 0},
- {"deleted_b", 0},
- // no added_b
- };
-}
-
-const PartitionSuffixSizes update_sizes_1() {
- return {
- {"grown_a", 2_GiB},
- {"shrunk_a", 1_GiB},
- {"same_a", 100_MiB},
- {"deleted_a", 150_MiB},
- // no added_a
- {"grown_b", 3_GiB},
- {"shrunk_b", 150_MiB},
- {"same_b", 100_MiB},
- {"added_b", 150_MiB},
- // no deleted_b
- };
-}
-
-const PartitionSuffixSizes update_sizes_2() {
- return {
- {"grown_a", 4_GiB},
- {"shrunk_a", 100_MiB},
- {"same_a", 100_MiB},
- {"deleted_a", 64_MiB},
- // no added_a
- {"grown_b", 3_GiB},
- {"shrunk_b", 150_MiB},
- {"same_b", 100_MiB},
- {"added_b", 150_MiB},
- // no deleted_b
- };
-}
-
-// Test case for first update after the device is manufactured, in which
-// case the "other" slot is likely of size "0" (except system, which is
-// non-zero because of system_other partition)
-TEST_F(BootControlAndroidTest, SimulatedFirstUpdate) {
- SetSlots({0, 1});
-
- SetMetadata(source(), update_sizes_0());
- SetMetadata(target(), update_sizes_0());
- ExpectStoreMetadata(update_sizes_1());
- ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b"});
-
- EXPECT_TRUE(InitPartitionMetadata(target(),
- {{"grown", 3_GiB},
- {"shrunk", 150_MiB},
- {"same", 100_MiB},
- {"added", 150_MiB}}));
-}
-
-// After first update, test for the second update. In the second update, the
-// "added" partition is deleted and "deleted" partition is re-added.
-TEST_F(BootControlAndroidTest, SimulatedSecondUpdate) {
- SetSlots({1, 0});
-
- SetMetadata(source(), update_sizes_1());
- SetMetadata(target(), update_sizes_0());
-
- ExpectStoreMetadata(update_sizes_2());
- ExpectUnmap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
-
- EXPECT_TRUE(InitPartitionMetadata(target(),
- {{"grown", 4_GiB},
- {"shrunk", 100_MiB},
- {"same", 100_MiB},
- {"deleted", 64_MiB}}));
-}
-
TEST_F(BootControlAndroidTest, ApplyingToCurrentSlot) {
SetSlots({1, 1});
- EXPECT_FALSE(InitPartitionMetadata(target(), {}))
+ EXPECT_FALSE(PreparePartitionsForUpdate(target(), {}))
<< "Should not be able to apply to current slot.";
}
-class BootControlAndroidGroupTestP : public BootControlAndroidTestP {
- public:
- void SetUp() override {
- BootControlAndroidTestP::SetUp();
- SetMetadata(
- source(),
- {.groups = {SimpleGroup(S("android"), 3_GiB, S("system"), 2_GiB),
- SimpleGroup(S("oem"), 2_GiB, S("vendor"), 1_GiB),
- SimpleGroup(T("android"), 3_GiB, T("system"), 0),
- SimpleGroup(T("oem"), 2_GiB, T("vendor"), 0)}});
- }
-
- // Return a simple group with only one partition.
- PartitionMetadata::Group SimpleGroup(const string& group,
- uint64_t group_size,
- const string& partition,
- uint64_t partition_size) {
- return {.name = group,
- .size = group_size,
- .partitions = {{.name = partition, .size = partition_size}}};
- }
-
- void ExpectStoreMetadata(const PartitionMetadata& partition_metadata) {
- ExpectStoreMetadataMatch(MetadataMatches(partition_metadata));
- }
-
- // Expect that target slot is stored with target groups.
- void ExpectStoreMetadataMatch(
- const Matcher<MetadataBuilder*>& matcher) override {
- BootControlAndroidTestP::ExpectStoreMetadataMatch(AllOf(
- MetadataMatches(PartitionMetadata{
- .groups = {SimpleGroup(S("android"), 3_GiB, S("system"), 2_GiB),
- SimpleGroup(S("oem"), 2_GiB, S("vendor"), 1_GiB)}}),
- matcher));
- }
-};
-
-// Allow to resize within group.
-TEST_P(BootControlAndroidGroupTestP, ResizeWithinGroup) {
- ExpectStoreMetadata(PartitionMetadata{
- .groups = {SimpleGroup(T("android"), 3_GiB, T("system"), 3_GiB),
- SimpleGroup(T("oem"), 2_GiB, T("vendor"), 2_GiB)}});
- ExpectUnmap({T("system"), T("vendor")});
-
- EXPECT_TRUE(bootctl_.InitPartitionMetadata(
- target(),
- PartitionMetadata{
- .groups = {SimpleGroup("android", 3_GiB, "system", 3_GiB),
- SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}},
- true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, NotEnoughSpaceForGroup) {
- EXPECT_FALSE(bootctl_.InitPartitionMetadata(
- target(),
- PartitionMetadata{
- .groups = {SimpleGroup("android", 3_GiB, "system", 1_GiB),
- SimpleGroup("oem", 2_GiB, "vendor", 3_GiB)}},
- true))
- << "Should not be able to grow over maximum size of group";
-}
-
-TEST_P(BootControlAndroidGroupTestP, GroupTooBig) {
- EXPECT_FALSE(bootctl_.InitPartitionMetadata(
- target(),
- PartitionMetadata{.groups = {{.name = "android", .size = 3_GiB},
- {.name = "oem", .size = 3_GiB}}},
- true))
- << "Should not be able to grow over size of super / 2";
-}
-
-TEST_P(BootControlAndroidGroupTestP, AddPartitionToGroup) {
- ExpectStoreMetadata(PartitionMetadata{
- .groups = {
- {.name = T("android"),
- .size = 3_GiB,
- .partitions = {{.name = T("system"), .size = 2_GiB},
- {.name = T("product_services"), .size = 1_GiB}}}}});
- ExpectUnmap({T("system"), T("vendor"), T("product_services")});
-
- EXPECT_TRUE(bootctl_.InitPartitionMetadata(
- target(),
- PartitionMetadata{
- .groups = {{.name = "android",
- .size = 3_GiB,
- .partitions = {{.name = "system", .size = 2_GiB},
- {.name = "product_services",
- .size = 1_GiB}}},
- SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}},
- true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, RemovePartitionFromGroup) {
- ExpectStoreMetadata(PartitionMetadata{
- .groups = {{.name = T("android"), .size = 3_GiB, .partitions = {}}}});
- ExpectUnmap({T("vendor")});
-
- EXPECT_TRUE(bootctl_.InitPartitionMetadata(
- target(),
- PartitionMetadata{
- .groups = {{.name = "android", .size = 3_GiB, .partitions = {}},
- SimpleGroup("oem", 2_GiB, "vendor", 2_GiB)}},
- true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, AddGroup) {
- ExpectStoreMetadata(PartitionMetadata{
- .groups = {
- SimpleGroup(T("new_group"), 2_GiB, T("new_partition"), 2_GiB)}});
- ExpectUnmap({T("system"), T("vendor"), T("new_partition")});
-
- EXPECT_TRUE(bootctl_.InitPartitionMetadata(
- target(),
- PartitionMetadata{
- .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
- SimpleGroup("oem", 1_GiB, "vendor", 1_GiB),
- SimpleGroup("new_group", 2_GiB, "new_partition", 2_GiB)}},
- true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, RemoveGroup) {
- ExpectStoreMetadataMatch(Not(HasGroup(T("oem"))));
- ExpectUnmap({T("system")});
- EXPECT_TRUE(bootctl_.InitPartitionMetadata(
- target(),
- PartitionMetadata{
- .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB)}},
- true));
-}
-
-TEST_P(BootControlAndroidGroupTestP, ResizeGroup) {
- ExpectStoreMetadata(PartitionMetadata{
- .groups = {SimpleGroup(T("android"), 2_GiB, T("system"), 2_GiB),
- SimpleGroup(T("oem"), 3_GiB, T("vendor"), 3_GiB)}});
- ExpectUnmap({T("system"), T("vendor")});
-
- EXPECT_TRUE(bootctl_.InitPartitionMetadata(
- target(),
- PartitionMetadata{
- .groups = {SimpleGroup("android", 2_GiB, "system", 2_GiB),
- SimpleGroup("oem", 3_GiB, "vendor", 3_GiB)}},
- true));
-}
-
-INSTANTIATE_TEST_CASE_P(BootControlAndroidTest,
- BootControlAndroidGroupTestP,
- testing::Values(TestParam{0, 1}, TestParam{1, 0}));
-
} // namespace chromeos_update_engine
diff --git a/boot_control_chromeos.cc b/boot_control_chromeos.cc
index ccba316..7e748d5 100644
--- a/boot_control_chromeos.cc
+++ b/boot_control_chromeos.cc
@@ -326,10 +326,8 @@
return -1;
}
-bool BootControlChromeOS::InitPartitionMetadata(
- Slot slot,
- const PartitionMetadata& partition_metadata,
- bool update_metadata) {
+bool BootControlChromeOS::PreparePartitionsForUpdate(
+ Slot slot, const DeltaArchiveManifest& manifest, bool update_metadata) {
return true;
}
diff --git a/boot_control_chromeos.h b/boot_control_chromeos.h
index f3682e9..29841c9 100644
--- a/boot_control_chromeos.h
+++ b/boot_control_chromeos.h
@@ -50,9 +50,9 @@
bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
- bool InitPartitionMetadata(Slot slot,
- const PartitionMetadata& partition_metadata,
- bool update_metadata) override;
+ bool PreparePartitionsForUpdate(Slot slot,
+ const DeltaArchiveManifest& manifest,
+ bool update_metadata) override;
void Cleanup() override;
private:
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index 392d785..9bf639a 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -25,6 +25,8 @@
#include <base/callback.h>
#include <base/macros.h>
+#include "update_engine/update_metadata.pb.h"
+
namespace chromeos_update_engine {
// The abstract boot control interface defines the interaction with the
@@ -35,19 +37,6 @@
public:
using Slot = unsigned int;
- struct PartitionMetadata {
- struct Partition {
- std::string name;
- uint64_t size;
- };
- struct Group {
- std::string name;
- uint64_t size;
- std::vector<Partition> partitions;
- };
- std::vector<Group> groups;
- };
-
static const Slot kInvalidSlot = UINT_MAX;
virtual ~BootControlInterface() = default;
@@ -67,9 +56,9 @@
// The |slot| number must be between 0 and GetNumSlots() - 1 and the
// |partition_name| is a platform-specific name that identifies a partition on
// every slot. In order to access the dynamic partitions in the target slot,
- // InitPartitionMetadata() must be called (once per payload) prior to calling
- // this function. On success, returns true and stores the block device in
- // |device|.
+ // PreparePartitionsForUpdate() must be called (once per payload) prior to
+ // calling this function. On success, returns true and stores the block device
+ // in |device|.
virtual bool GetPartitionDevice(const std::string& partition_name,
Slot slot,
std::string* device) const = 0;
@@ -96,12 +85,11 @@
// Initializes the metadata of the underlying partitions for a given |slot|
// and sets up the states for accessing dynamic partitions.
- // |partition_metadata| will be written to the specified |slot| if
+ // Metadata will be written to the specified |slot| if
// |update_metadata| is set.
- virtual bool InitPartitionMetadata(
- Slot slot,
- const PartitionMetadata& partition_metadata,
- bool update_metadata) = 0;
+ virtual bool PreparePartitionsForUpdate(Slot slot,
+ const DeltaArchiveManifest& manifest,
+ bool update_metadata) = 0;
// Do necessary clean-up operations after the whole update.
virtual void Cleanup() = 0;
diff --git a/common/boot_control_stub.cc b/common/boot_control_stub.cc
index 0fe8a98..b10e82f 100644
--- a/common/boot_control_stub.cc
+++ b/common/boot_control_stub.cc
@@ -59,10 +59,8 @@
return false;
}
-bool BootControlStub::InitPartitionMetadata(
- Slot slot,
- const PartitionMetadata& partition_metadata,
- bool update_metadata) {
+bool BootControlStub::PreparePartitionsForUpdate(
+ Slot slot, const DeltaArchiveManifest& manifest, bool update_metadata) {
LOG(ERROR) << __FUNCTION__ << " should never be called.";
return false;
}
diff --git a/common/boot_control_stub.h b/common/boot_control_stub.h
index 8dfaffc..f2973a2 100644
--- a/common/boot_control_stub.h
+++ b/common/boot_control_stub.h
@@ -45,9 +45,9 @@
bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
- bool InitPartitionMetadata(Slot slot,
- const PartitionMetadata& partition_metadata,
- bool update_metadata) override;
+ bool PreparePartitionsForUpdate(Slot slot,
+ const DeltaArchiveManifest& manifest,
+ bool update_metadata) override;
void Cleanup() override;
private:
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 3d65075..11810d1 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -74,9 +74,9 @@
return true;
}
- bool InitPartitionMetadata(Slot slot,
- const PartitionMetadata& partition_metadata,
- bool update_metadata) override {
+ bool PreparePartitionsForUpdate(Slot slot,
+ const DeltaArchiveManifest& manifest,
+ bool update_metadata) override {
return true;
}
diff --git a/common/file_fetcher.cc b/common/file_fetcher.cc
index 3836e54..7134fd6 100644
--- a/common/file_fetcher.cc
+++ b/common/file_fetcher.cc
@@ -43,8 +43,9 @@
// static
bool FileFetcher::SupportedUrl(const string& url) {
// Note that we require the file path to start with a "/".
- return base::StartsWith(
- url, "file:///", base::CompareCase::INSENSITIVE_ASCII);
+ return (
+ base::StartsWith(url, "file:///", base::CompareCase::INSENSITIVE_ASCII) ||
+ base::StartsWith(url, "fd://", base::CompareCase::INSENSITIVE_ASCII));
}
FileFetcher::~FileFetcher() {
@@ -67,12 +68,20 @@
return;
}
- string file_path = url.substr(strlen("file://"));
- stream_ =
- brillo::FileStream::Open(base::FilePath(file_path),
- brillo::Stream::AccessMode::READ,
- brillo::FileStream::Disposition::OPEN_EXISTING,
- nullptr);
+ string file_path;
+
+ if (base::StartsWith(url, "fd://", base::CompareCase::INSENSITIVE_ASCII)) {
+ int fd = std::stoi(url.substr(strlen("fd://")));
+ file_path = url;
+ stream_ = brillo::FileStream::FromFileDescriptor(fd, false, nullptr);
+ } else {
+ file_path = url.substr(strlen("file://"));
+ stream_ =
+ brillo::FileStream::Open(base::FilePath(file_path),
+ brillo::Stream::AccessMode::READ,
+ brillo::FileStream::Disposition::OPEN_EXISTING,
+ nullptr);
+ }
if (!stream_) {
LOG(ERROR) << "Couldn't open " << file_path;
@@ -183,5 +192,4 @@
transfer_in_progress_ = false;
transfer_paused_ = false;
}
-
} // namespace chromeos_update_engine
diff --git a/common/platform_constants.h b/common/platform_constants.h
index 6eaa940..243af69 100644
--- a/common/platform_constants.h
+++ b/common/platform_constants.h
@@ -38,6 +38,10 @@
// whole payload.
extern const char kUpdatePayloadPublicKeyPath[];
+// Path to the location of the zip archive file that contains PEM encoded X509
+// certificates. e.g. 'system/etc/security/otacerts.zip'.
+extern const char kUpdateCertificatesPath[];
+
// Path to the directory containing all the SSL certificates accepted by
// update_engine when sending requests to Omaha and the download server (if
// HTTPS is used for that as well).
diff --git a/common/platform_constants_android.cc b/common/platform_constants_android.cc
index 9d8d30e..f468c3b 100644
--- a/common/platform_constants_android.cc
+++ b/common/platform_constants_android.cc
@@ -25,8 +25,8 @@
"https://clients2.google.com/service/update2/brillo";
const char kOmahaUpdaterID[] = "Brillo";
const char kOmahaPlatformName[] = "Brillo";
-const char kUpdatePayloadPublicKeyPath[] =
- "/etc/update_engine/update-payload-key.pub.pem";
+const char kUpdatePayloadPublicKeyPath[] = "";
+const char kUpdateCertificatesPath[] = "/system/etc/security/otacerts.zip";
const char kCACertificatesPath[] = "/system/etc/security/cacerts_google";
// No deadline file API support on Android.
const char kOmahaResponseDeadlineFile[] = "";
diff --git a/common/platform_constants_chromeos.cc b/common/platform_constants_chromeos.cc
index f1ac490..fe94a45 100644
--- a/common/platform_constants_chromeos.cc
+++ b/common/platform_constants_chromeos.cc
@@ -27,6 +27,7 @@
const char kOmahaPlatformName[] = "Chrome OS";
const char kUpdatePayloadPublicKeyPath[] =
"/usr/share/update_engine/update-payload-key.pub.pem";
+const char kUpdateCertificatesPath[] = "";
const char kCACertificatesPath[] = "/usr/share/chromeos-ca-certificates";
const char kOmahaResponseDeadlineFile[] = "/tmp/update-check-response-deadline";
// This directory is wiped during powerwash.
diff --git a/common/utils.cc b/common/utils.cc
index 34d97a2..e7b6975 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -1064,6 +1064,16 @@
}
}
+string GetFilePath(int fd) {
+ base::FilePath proc("/proc/self/fd/" + std::to_string(fd));
+ base::FilePath file_name;
+
+ if (!base::ReadSymbolicLink(proc, &file_name)) {
+ return "not found";
+ }
+ return file_name.value();
+}
+
} // namespace utils
} // namespace chromeos_update_engine
diff --git a/common/utils.h b/common/utils.h
index 9160d9f..9dca9e8 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -304,6 +304,9 @@
// reboot. Returns whether it succeeded getting the boot_id.
bool GetBootId(std::string* boot_id);
+// This function gets the file path of the file pointed to by FileDiscriptor.
+std::string GetFilePath(int fd);
+
// Divide |x| by |y| and round up to the nearest integer.
constexpr uint64_t DivRoundUp(uint64_t x, uint64_t y) {
return (x + y - 1) / y;
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
index 7d1c59e..b4ac2f5 100644
--- a/common/utils_unittest.cc
+++ b/common/utils_unittest.cc
@@ -507,4 +507,13 @@
ExpectInvalidParseRollbackKeyVersion("1.99999");
}
+TEST(UtilsTest, GetFilePathTest) {
+ test_utils::ScopedTempFile file;
+ int fd = HANDLE_EINTR(open(file.path().c_str(), O_RDONLY));
+ EXPECT_GE(fd, 0);
+ EXPECT_EQ(file.path(), utils::GetFilePath(fd));
+ EXPECT_EQ("not found", utils::GetFilePath(-1));
+ IGNORE_EINTR(close(fd));
+}
+
} // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index 40c2663..8dcf343 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -16,46 +16,87 @@
#include "update_engine/dynamic_partition_control_android.h"
+#include <chrono> // NOLINT(build/c++11) - using libsnapshot / liblp API
+#include <map>
#include <memory>
#include <set>
#include <string>
+#include <vector>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <base/files/file_util.h>
#include <base/logging.h>
+#include <base/strings/string_util.h>
#include <bootloader_message/bootloader_message.h>
+#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
+#include <libsnapshot/snapshot.h>
#include "update_engine/common/boot_control_interface.h"
#include "update_engine/common/utils.h"
+#include "update_engine/dynamic_partition_utils.h"
using android::base::GetBoolProperty;
using android::base::Join;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
using android::fs_mgr::CreateLogicalPartition;
+using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::DestroyLogicalPartition;
using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::Partition;
using android::fs_mgr::PartitionOpener;
+using android::fs_mgr::SlotSuffixForSlotNumber;
namespace chromeos_update_engine {
constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions";
constexpr char kRetrfoitDynamicPartitions[] =
"ro.boot.dynamic_partitions_retrofit";
-constexpr uint64_t kMapTimeoutMillis = 1000;
+constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled";
+constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit";
+// Map timeout for dynamic partitions.
+constexpr std::chrono::milliseconds kMapTimeout{1000};
+// Map timeout for dynamic partitions with snapshots. Since several devices
+// needs to be mapped, this timeout is longer than |kMapTimeout|.
+constexpr std::chrono::milliseconds kMapSnapshotTimeout{5000};
+
+DynamicPartitionControlAndroid::DynamicPartitionControlAndroid() {
+ if (GetVirtualAbFeatureFlag().IsEnabled()) {
+ snapshot_ = android::snapshot::SnapshotManager::New();
+ CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
+ }
+}
DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
CleanupInternal(false /* wait */);
}
-bool DynamicPartitionControlAndroid::IsDynamicPartitionsEnabled() {
- return GetBoolProperty(kUseDynamicPartitions, false);
+static FeatureFlag GetFeatureFlag(const char* enable_prop,
+ const char* retrofit_prop) {
+ bool retrofit = GetBoolProperty(retrofit_prop, false);
+ bool enabled = GetBoolProperty(enable_prop, false);
+ if (retrofit && !enabled) {
+ LOG(ERROR) << retrofit_prop << " is true but " << enable_prop
+ << " is not. These sysprops are inconsistent. Assume that "
+ << enable_prop << " is true from now on.";
+ }
+ if (retrofit) {
+ return FeatureFlag(FeatureFlag::Value::RETROFIT);
+ }
+ if (enabled) {
+ return FeatureFlag(FeatureFlag::Value::LAUNCH);
+ }
+ return FeatureFlag(FeatureFlag::Value::NONE);
}
-bool DynamicPartitionControlAndroid::IsDynamicPartitionsRetrofit() {
- return GetBoolProperty(kRetrfoitDynamicPartitions, false);
+FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() {
+ return GetFeatureFlag(kUseDynamicPartitions, kRetrfoitDynamicPartitions);
+}
+
+FeatureFlag DynamicPartitionControlAndroid::GetVirtualAbFeatureFlag() {
+ return GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit);
}
bool DynamicPartitionControlAndroid::MapPartitionInternal(
@@ -64,12 +105,25 @@
uint32_t slot,
bool force_writable,
std::string* path) {
- if (!CreateLogicalPartition(super_device.c_str(),
- slot,
- target_partition_name,
- force_writable,
- std::chrono::milliseconds(kMapTimeoutMillis),
- path)) {
+ CreateLogicalPartitionParams params = {
+ .block_device = super_device,
+ .metadata_slot = slot,
+ .partition_name = target_partition_name,
+ .force_writable = force_writable,
+ };
+ bool success = false;
+ if (GetVirtualAbFeatureFlag().IsEnabled() && force_writable) {
+ // Only target partitions are mapped with force_writable. On Virtual
+ // A/B devices, target partitions may overlap with source partitions, so
+ // they must be mapped with snapshot.
+ params.timeout_ms = kMapSnapshotTimeout;
+ success = snapshot_->MapUpdateSnapshot(params, path);
+ } else {
+ params.timeout_ms = kMapTimeout;
+ success = CreateLogicalPartition(params, path);
+ }
+
+ if (!success) {
LOG(ERROR) << "Cannot map " << target_partition_name << " in "
<< super_device << " on device mapper.";
return false;
@@ -103,7 +157,7 @@
// Note that for source partitions, if GetState() == ACTIVE, callers (e.g.
// BootControlAndroid) should not call MapPartitionOnDeviceMapper, but
// should directly call GetDmDevicePathByName.
- if (!UnmapPartitionOnDeviceMapper(target_partition_name, true /* wait */)) {
+ if (!UnmapPartitionOnDeviceMapper(target_partition_name)) {
LOG(ERROR) << target_partition_name
<< " is mapped before the update, and it cannot be unmapped.";
return false;
@@ -127,12 +181,22 @@
}
bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper(
- const std::string& target_partition_name, bool wait) {
+ const std::string& target_partition_name) {
if (DeviceMapper::Instance().GetState(target_partition_name) !=
DmDeviceState::INVALID) {
- if (!DestroyLogicalPartition(
- target_partition_name,
- std::chrono::milliseconds(wait ? kMapTimeoutMillis : 0))) {
+ // Partitions at target slot on non-Virtual A/B devices are mapped as
+ // dm-linear. Also, on Virtual A/B devices, system_other may be mapped for
+ // preopt apps as dm-linear.
+ // Call DestroyLogicalPartition to handle these cases.
+ bool success = DestroyLogicalPartition(target_partition_name);
+
+ // On a Virtual A/B device, |target_partition_name| may be a leftover from
+ // a paused update. Clean up any underlying devices.
+ if (GetVirtualAbFeatureFlag().IsEnabled()) {
+ success &= snapshot_->UnmapUpdateSnapshot(target_partition_name);
+ }
+
+ if (!success) {
LOG(ERROR) << "Cannot unmap " << target_partition_name
<< " from device mapper.";
return false;
@@ -145,12 +209,15 @@
}
void DynamicPartitionControlAndroid::CleanupInternal(bool wait) {
+ if (mapped_devices_.empty()) {
+ return;
+ }
// UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence
// a copy is needed for the loop.
std::set<std::string> mapped = mapped_devices_;
LOG(INFO) << "Destroying [" << Join(mapped, ", ") << "] from device mapper";
for (const auto& partition_name : mapped) {
- ignore_result(UnmapPartitionOnDeviceMapper(partition_name, wait));
+ ignore_result(UnmapPartitionOnDeviceMapper(partition_name));
}
}
@@ -174,18 +241,23 @@
std::unique_ptr<MetadataBuilder>
DynamicPartitionControlAndroid::LoadMetadataBuilder(
+ const std::string& super_device, uint32_t source_slot) {
+ return LoadMetadataBuilder(
+ super_device, source_slot, BootControlInterface::kInvalidSlot);
+}
+
+std::unique_ptr<MetadataBuilder>
+DynamicPartitionControlAndroid::LoadMetadataBuilder(
const std::string& super_device,
uint32_t source_slot,
uint32_t target_slot) {
std::unique_ptr<MetadataBuilder> builder;
-
- if (target_slot != BootControlInterface::kInvalidSlot &&
- IsDynamicPartitionsRetrofit()) {
- builder = MetadataBuilder::NewForUpdate(
- PartitionOpener(), super_device, source_slot, target_slot);
- } else {
+ if (target_slot == BootControlInterface::kInvalidSlot) {
builder =
MetadataBuilder::New(PartitionOpener(), super_device, source_slot);
+ } else {
+ builder = MetadataBuilder::NewForUpdate(
+ PartitionOpener(), super_device, source_slot, target_slot);
}
if (builder == nullptr) {
@@ -212,7 +284,7 @@
return false;
}
- if (IsDynamicPartitionsRetrofit()) {
+ if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
if (!FlashPartitionTable(super_device, *metadata)) {
LOG(ERROR) << "Cannot write metadata to " << super_device;
return false;
@@ -267,4 +339,162 @@
*out = base::FilePath(misc_device).DirName().value();
return true;
}
+
+bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest) {
+ // TODO(elsk): Also call PrepareDynamicPartitionsForUpdate when applying
+ // downgrade packages on retrofit Virtual A/B devices and when applying
+ // secondary OTA. b/138258570
+ if (GetVirtualAbFeatureFlag().IsEnabled()) {
+ return PrepareSnapshotPartitionsForUpdate(
+ source_slot, target_slot, manifest);
+ }
+ return PrepareDynamicPartitionsForUpdate(source_slot, target_slot, manifest);
+}
+
+bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest) {
+ const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+
+ // Unmap all the target dynamic partitions because they would become
+ // inconsistent with the new metadata.
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ for (const auto& partition_name : group.partition_names()) {
+ if (!UnmapPartitionOnDeviceMapper(partition_name + target_suffix)) {
+ return false;
+ }
+ }
+ }
+
+ std::string device_dir_str;
+ if (!GetDeviceDir(&device_dir_str)) {
+ return false;
+ }
+ base::FilePath device_dir(device_dir_str);
+ auto source_device =
+ device_dir.Append(GetSuperPartitionName(source_slot)).value();
+
+ auto builder = LoadMetadataBuilder(source_device, source_slot, target_slot);
+ if (builder == nullptr) {
+ LOG(ERROR) << "No metadata at "
+ << BootControlInterface::SlotName(source_slot);
+ return false;
+ }
+
+ if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
+ return false;
+ }
+
+ auto target_device =
+ device_dir.Append(GetSuperPartitionName(target_slot)).value();
+ return StoreMetadata(target_device, builder.get(), target_slot);
+}
+
+bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest) {
+ if (!snapshot_->BeginUpdate()) {
+ LOG(ERROR) << "Cannot begin new update.";
+ return false;
+ }
+ if (!snapshot_->CreateUpdateSnapshots(manifest)) {
+ LOG(ERROR) << "Cannot create update snapshots.";
+ return false;
+ }
+ return true;
+}
+
+std::string DynamicPartitionControlAndroid::GetSuperPartitionName(
+ uint32_t slot) {
+ return fs_mgr_get_super_partition_name(slot);
+}
+
+bool DynamicPartitionControlAndroid::UpdatePartitionMetadata(
+ MetadataBuilder* builder,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest) {
+ const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+ DeleteGroupsWithSuffix(builder, target_suffix);
+
+ uint64_t total_size = 0;
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ total_size += group.size();
+ }
+
+ std::string expr;
+ uint64_t allocatable_space = builder->AllocatableSpace();
+ if (!GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
+ allocatable_space /= 2;
+ expr = "half of ";
+ }
+ if (total_size > allocatable_space) {
+ LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
+ << " (" << total_size << ") has exceeded " << expr
+ << "allocatable space for dynamic partitions "
+ << allocatable_space << ".";
+ return false;
+ }
+
+ // name of partition(e.g. "system") -> size in bytes
+ std::map<std::string, uint64_t> partition_sizes;
+ for (const auto& partition : manifest.partitions()) {
+ partition_sizes.emplace(partition.partition_name(),
+ partition.new_partition_info().size());
+ }
+
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ auto group_name_suffix = group.name() + target_suffix;
+ if (!builder->AddGroup(group_name_suffix, group.size())) {
+ LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
+ << group.size();
+ return false;
+ }
+ LOG(INFO) << "Added group " << group_name_suffix << " with size "
+ << group.size();
+
+ for (const auto& partition_name : group.partition_names()) {
+ auto partition_sizes_it = partition_sizes.find(partition_name);
+ if (partition_sizes_it == partition_sizes.end()) {
+ // TODO(tbao): Support auto-filling partition info for framework-only
+ // OTA.
+ LOG(ERROR) << "dynamic_partition_metadata contains partition "
+ << partition_name << " but it is not part of the manifest. "
+ << "This is not supported.";
+ return false;
+ }
+ uint64_t partition_size = partition_sizes_it->second;
+
+ auto partition_name_suffix = partition_name + target_suffix;
+ Partition* p = builder->AddPartition(
+ partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
+ if (!p) {
+ LOG(ERROR) << "Cannot add partition " << partition_name_suffix
+ << " to group " << group_name_suffix;
+ return false;
+ }
+ if (!builder->ResizePartition(p, partition_size)) {
+ LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
+ << " to size " << partition_size << ". Not enough space?";
+ return false;
+ }
+ LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
+ << group_name_suffix << " with size " << partition_size;
+ }
+ }
+
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::FinishUpdate() {
+ if (!GetVirtualAbFeatureFlag().IsEnabled())
+ return true;
+ LOG(INFO) << "Snapshot writes are done.";
+ return snapshot_->FinishedSnapshotWrites();
+}
+
} // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 1233b64..f9dfd89 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -23,37 +23,68 @@
#include <set>
#include <string>
+#include <libsnapshot/snapshot.h>
+
namespace chromeos_update_engine {
class DynamicPartitionControlAndroid : public DynamicPartitionControlInterface {
public:
- DynamicPartitionControlAndroid() = default;
+ DynamicPartitionControlAndroid();
~DynamicPartitionControlAndroid();
- bool IsDynamicPartitionsEnabled() override;
- bool IsDynamicPartitionsRetrofit() override;
+ FeatureFlag GetDynamicPartitionsFeatureFlag() override;
+ FeatureFlag GetVirtualAbFeatureFlag() override;
bool MapPartitionOnDeviceMapper(const std::string& super_device,
const std::string& target_partition_name,
uint32_t slot,
bool force_writable,
std::string* path) override;
- bool UnmapPartitionOnDeviceMapper(const std::string& target_partition_name,
- bool wait) override;
void Cleanup() override;
bool DeviceExists(const std::string& path) override;
android::dm::DmDeviceState GetState(const std::string& name) override;
bool GetDmDevicePathByName(const std::string& name,
std::string* path) override;
std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
+ const std::string& super_device, uint32_t source_slot) override;
+
+ bool PreparePartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest) override;
+ bool GetDeviceDir(std::string* path) override;
+ std::string GetSuperPartitionName(uint32_t slot) override;
+ bool FinishUpdate() override;
+
+ protected:
+ // These functions are exposed for testing.
+
+ // Unmap logical partition on device mapper. This is the reverse operation
+ // of MapPartitionOnDeviceMapper.
+ // Returns true if unmapped successfully.
+ virtual bool UnmapPartitionOnDeviceMapper(
+ const std::string& target_partition_name);
+
+ // Retrieve metadata from |super_device| at slot |source_slot|.
+ //
+ // If |target_slot| != kInvalidSlot, before returning the metadata, this
+ // function modifies the metadata so that during updates, the metadata can be
+ // written to |target_slot|. In particular, on retrofit devices, the returned
+ // metadata automatically includes block devices at |target_slot|.
+ //
+ // If |target_slot| == kInvalidSlot, this function returns metadata at
+ // |source_slot| without modifying it. This is the same as
+ // LoadMetadataBuilder(const std::string&, uint32_t).
+ virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
const std::string& super_device,
uint32_t source_slot,
- uint32_t target_slot) override;
- bool StoreMetadata(const std::string& super_device,
- android::fs_mgr::MetadataBuilder* builder,
- uint32_t target_slot) override;
- bool GetDeviceDir(std::string* path) override;
+ uint32_t target_slot);
+
+ // Write metadata |builder| to |super_device| at slot |target_slot|.
+ virtual bool StoreMetadata(const std::string& super_device,
+ android::fs_mgr::MetadataBuilder* builder,
+ uint32_t target_slot);
private:
- std::set<std::string> mapped_devices_;
+ friend class DynamicPartitionControlAndroidTest;
void CleanupInternal(bool wait);
bool MapPartitionInternal(const std::string& super_device,
@@ -62,6 +93,27 @@
bool force_writable,
std::string* path);
+ // Update |builder| according to |partition_metadata|, assuming the device
+ // does not have Virtual A/B.
+ bool UpdatePartitionMetadata(android::fs_mgr::MetadataBuilder* builder,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest);
+
+ // Helper for PreparePartitionsForUpdate. Used for dynamic partitions without
+ // Virtual A/B update.
+ bool PrepareDynamicPartitionsForUpdate(uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest);
+
+ // Helper for PreparePartitionsForUpdate. Used for snapshotted partitions for
+ // Virtual A/B update.
+ bool PrepareSnapshotPartitionsForUpdate(uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest);
+
+ std::set<std::string> mapped_devices_;
+ std::unique_ptr<android::snapshot::SnapshotManager> snapshot_;
+
DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid);
};
diff --git a/dynamic_partition_control_android_unittest.cc b/dynamic_partition_control_android_unittest.cc
new file mode 100644
index 0000000..552774e
--- /dev/null
+++ b/dynamic_partition_control_android_unittest.cc
@@ -0,0 +1,489 @@
+//
+// Copyright (C) 2019 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 "update_engine/dynamic_partition_control_android.h"
+
+#include <set>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/dynamic_partition_test_utils.h"
+#include "update_engine/mock_dynamic_partition_control.h"
+
+using std::string;
+using testing::_;
+using testing::AnyNumber;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Not;
+using testing::Return;
+
+namespace chromeos_update_engine {
+
+class DynamicPartitionControlAndroidTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ module_ = std::make_unique<NiceMock<MockDynamicPartitionControlAndroid>>();
+
+ ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+ ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+ .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
+
+ ON_CALL(dynamicControl(), GetDeviceDir(_))
+ .WillByDefault(Invoke([](auto path) {
+ *path = kFakeDevicePath;
+ return true;
+ }));
+
+ ON_CALL(dynamicControl(), GetSuperPartitionName(_))
+ .WillByDefault(Return(kFakeSuper));
+ }
+
+ // Return the mocked DynamicPartitionControlInterface.
+ NiceMock<MockDynamicPartitionControlAndroid>& dynamicControl() {
+ return static_cast<NiceMock<MockDynamicPartitionControlAndroid>&>(*module_);
+ }
+
+ std::string GetSuperDevice(uint32_t slot) {
+ return GetDevice(dynamicControl().GetSuperPartitionName(slot));
+ }
+
+ uint32_t source() { return slots_.source; }
+ uint32_t target() { return slots_.target; }
+
+ // Return partition names with suffix of source().
+ std::string S(const std::string& name) {
+ return name + kSlotSuffixes[source()];
+ }
+
+ // Return partition names with suffix of target().
+ std::string T(const std::string& name) {
+ return name + kSlotSuffixes[target()];
+ }
+
+ // Set the fake metadata to return when LoadMetadataBuilder is called on
+ // |slot|.
+ void SetMetadata(uint32_t slot, const PartitionSuffixSizes& sizes) {
+ EXPECT_CALL(dynamicControl(),
+ LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke([sizes](auto, auto, auto) {
+ return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes));
+ }));
+ }
+
+ void ExpectStoreMetadata(const PartitionSuffixSizes& partition_sizes) {
+ EXPECT_CALL(dynamicControl(),
+ StoreMetadata(GetSuperDevice(target()),
+ MetadataMatches(partition_sizes),
+ target()))
+ .WillOnce(Return(true));
+ }
+
+ // Expect that UnmapPartitionOnDeviceMapper is called on target() metadata
+ // slot with each partition in |partitions|.
+ void ExpectUnmap(const std::set<std::string>& partitions) {
+ // Error when UnmapPartitionOnDeviceMapper is called on unknown arguments.
+ ON_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(_))
+ .WillByDefault(Return(false));
+
+ for (const auto& partition : partitions) {
+ EXPECT_CALL(dynamicControl(), UnmapPartitionOnDeviceMapper(partition))
+ .WillOnce(Return(true));
+ }
+ }
+ bool PreparePartitionsForUpdate(const PartitionSizes& partition_sizes) {
+ return dynamicControl().PreparePartitionsForUpdate(
+ source(), target(), PartitionSizesToManifest(partition_sizes));
+ }
+ void SetSlots(const TestParam& slots) { slots_ = slots; }
+
+ struct Listener : public ::testing::MatchResultListener {
+ explicit Listener(std::ostream* os) : MatchResultListener(os) {}
+ };
+
+ testing::AssertionResult UpdatePartitionMetadata(
+ const PartitionSuffixSizes& source_metadata,
+ const PartitionSizes& update_metadata,
+ const PartitionSuffixSizes& expected) {
+ return UpdatePartitionMetadata(
+ PartitionSuffixSizesToManifest(source_metadata),
+ PartitionSizesToManifest(update_metadata),
+ PartitionSuffixSizesToManifest(expected));
+ }
+ testing::AssertionResult UpdatePartitionMetadata(
+ const DeltaArchiveManifest& source_manifest,
+ const DeltaArchiveManifest& update_manifest,
+ const DeltaArchiveManifest& expected) {
+ return UpdatePartitionMetadata(
+ source_manifest, update_manifest, MetadataMatches(expected));
+ }
+ testing::AssertionResult UpdatePartitionMetadata(
+ const DeltaArchiveManifest& source_manifest,
+ const DeltaArchiveManifest& update_manifest,
+ const Matcher<MetadataBuilder*>& matcher) {
+ auto super_metadata = NewFakeMetadata(source_manifest);
+ if (!module_->UpdatePartitionMetadata(
+ super_metadata.get(), target(), update_manifest)) {
+ return testing::AssertionFailure()
+ << "UpdatePartitionMetadataInternal failed";
+ }
+ std::stringstream ss;
+ Listener listener(&ss);
+ if (matcher.MatchAndExplain(super_metadata.get(), &listener)) {
+ return testing::AssertionSuccess() << ss.str();
+ } else {
+ return testing::AssertionFailure() << ss.str();
+ }
+ }
+
+ std::unique_ptr<DynamicPartitionControlAndroid> module_;
+ TestParam slots_;
+};
+
+class DynamicPartitionControlAndroidTestP
+ : public DynamicPartitionControlAndroidTest,
+ public ::testing::WithParamInterface<TestParam> {
+ public:
+ void SetUp() override {
+ DynamicPartitionControlAndroidTest::SetUp();
+ SetSlots(GetParam());
+ }
+};
+
+// Test resize case. Grow if target metadata contains a partition with a size
+// less than expected.
+TEST_P(DynamicPartitionControlAndroidTestP,
+ NeedGrowIfSizeNotMatchWhenResizing) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}};
+ PartitionSuffixSizes expected{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 3_GiB},
+ {T("vendor"), 1_GiB}};
+ PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 1_GiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test resize case. Shrink if target metadata contains a partition with a size
+// greater than expected.
+TEST_P(DynamicPartitionControlAndroidTestP,
+ NeedShrinkIfSizeNotMatchWhenResizing) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}};
+ PartitionSuffixSizes expected{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 150_MiB}};
+ PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 150_MiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test adding partitions on the first run.
+TEST_P(DynamicPartitionControlAndroidTestP, AddPartitionToEmptyMetadata) {
+ PartitionSuffixSizes source_metadata{};
+ PartitionSuffixSizes expected{{T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
+ PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test subsequent add case.
+TEST_P(DynamicPartitionControlAndroidTestP, AddAdditionalPartition) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {T("system"), 2_GiB}};
+ PartitionSuffixSizes expected{
+ {S("system"), 2_GiB}, {T("system"), 2_GiB}, {T("vendor"), 1_GiB}};
+ PartitionSizes update_metadata{{"system", 2_GiB}, {"vendor", 1_GiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test delete one partition.
+TEST_P(DynamicPartitionControlAndroidTestP, DeletePartition) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}};
+ // No T("vendor")
+ PartitionSuffixSizes expected{
+ {S("system"), 2_GiB}, {S("vendor"), 1_GiB}, {T("system"), 2_GiB}};
+ PartitionSizes update_metadata{{"system", 2_GiB}};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test delete all partitions.
+TEST_P(DynamicPartitionControlAndroidTestP, DeleteAll) {
+ PartitionSuffixSizes source_metadata{{S("system"), 2_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 2_GiB},
+ {T("vendor"), 1_GiB}};
+ PartitionSuffixSizes expected{{S("system"), 2_GiB}, {S("vendor"), 1_GiB}};
+ PartitionSizes update_metadata{};
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_metadata, update_metadata, expected));
+}
+
+// Test corrupt source metadata case.
+TEST_P(DynamicPartitionControlAndroidTestP, CorruptedSourceMetadata) {
+ EXPECT_CALL(dynamicControl(),
+ LoadMetadataBuilder(GetSuperDevice(source()), source(), _))
+ .WillOnce(Invoke([](auto, auto, auto) { return nullptr; }));
+ ExpectUnmap({T("system")});
+
+ EXPECT_FALSE(PreparePartitionsForUpdate({{"system", 1_GiB}}))
+ << "Should not be able to continue with corrupt source metadata";
+}
+
+// Test that UpdatePartitionMetadata fails if there is not enough space on the
+// device.
+TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpace) {
+ PartitionSuffixSizes source_metadata{{S("system"), 3_GiB},
+ {S("vendor"), 2_GiB},
+ {T("system"), 0},
+ {T("vendor"), 0}};
+ PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
+
+ EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+ << "Should not be able to fit 11GiB data into 10GiB space";
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, NotEnoughSpaceForSlot) {
+ PartitionSuffixSizes source_metadata{{S("system"), 1_GiB},
+ {S("vendor"), 1_GiB},
+ {T("system"), 0},
+ {T("vendor"), 0}};
+ PartitionSizes update_metadata{{"system", 3_GiB}, {"vendor", 3_GiB}};
+ EXPECT_FALSE(UpdatePartitionMetadata(source_metadata, update_metadata, {}))
+ << "Should not be able to grow over size of super / 2";
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+ DynamicPartitionControlAndroidTestP,
+ testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+class DynamicPartitionControlAndroidGroupTestP
+ : public DynamicPartitionControlAndroidTestP {
+ public:
+ DeltaArchiveManifest source_manifest;
+ void SetUp() override {
+ DynamicPartitionControlAndroidTestP::SetUp();
+ AddGroupAndPartition(
+ &source_manifest, S("android"), 3_GiB, S("system"), 2_GiB);
+ AddGroupAndPartition(&source_manifest, S("oem"), 2_GiB, S("vendor"), 1_GiB);
+ AddGroupAndPartition(&source_manifest, T("android"), 3_GiB, T("system"), 0);
+ AddGroupAndPartition(&source_manifest, T("oem"), 2_GiB, T("vendor"), 0);
+ }
+
+ void AddGroupAndPartition(DeltaArchiveManifest* manifest,
+ const string& group,
+ uint64_t group_size,
+ const string& partition,
+ uint64_t partition_size) {
+ auto* g = AddGroup(manifest, group, group_size);
+ AddPartition(manifest, g, partition, partition_size);
+ }
+};
+
+// Allow to resize within group.
+TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeWithinGroup) {
+ DeltaArchiveManifest expected;
+ AddGroupAndPartition(&expected, T("android"), 3_GiB, T("system"), 3_GiB);
+ AddGroupAndPartition(&expected, T("oem"), 2_GiB, T("vendor"), 2_GiB);
+
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 3_GiB, "system", 3_GiB);
+ AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, NotEnoughSpaceForGroup) {
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 3_GiB, "system", 1_GiB),
+ AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 3_GiB);
+ EXPECT_FALSE(UpdatePartitionMetadata(source_manifest, update_manifest, {}))
+ << "Should not be able to grow over maximum size of group";
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, GroupTooBig) {
+ DeltaArchiveManifest update_manifest;
+ AddGroup(&update_manifest, "android", 3_GiB);
+ AddGroup(&update_manifest, "oem", 3_GiB);
+ EXPECT_FALSE(UpdatePartitionMetadata(source_manifest, update_manifest, {}))
+ << "Should not be able to grow over size of super / 2";
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, AddPartitionToGroup) {
+ DeltaArchiveManifest expected;
+ auto* g = AddGroup(&expected, T("android"), 3_GiB);
+ AddPartition(&expected, g, T("system"), 2_GiB);
+ AddPartition(&expected, g, T("system_ext"), 1_GiB);
+
+ DeltaArchiveManifest update_manifest;
+ g = AddGroup(&update_manifest, "android", 3_GiB);
+ AddPartition(&update_manifest, g, "system", 2_GiB);
+ AddPartition(&update_manifest, g, "system_ext", 1_GiB);
+ AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, RemovePartitionFromGroup) {
+ DeltaArchiveManifest expected;
+ AddGroup(&expected, T("android"), 3_GiB);
+
+ DeltaArchiveManifest update_manifest;
+ AddGroup(&update_manifest, "android", 3_GiB);
+ AddGroupAndPartition(&update_manifest, "oem", 2_GiB, "vendor", 2_GiB);
+
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, AddGroup) {
+ DeltaArchiveManifest expected;
+ AddGroupAndPartition(
+ &expected, T("new_group"), 2_GiB, T("new_partition"), 2_GiB);
+
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB);
+ AddGroupAndPartition(&update_manifest, "oem", 1_GiB, "vendor", 1_GiB);
+ AddGroupAndPartition(
+ &update_manifest, "new_group", 2_GiB, "new_partition", 2_GiB);
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, RemoveGroup) {
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB);
+
+ EXPECT_TRUE(UpdatePartitionMetadata(
+ source_manifest, update_manifest, Not(HasGroup(T("oem")))));
+}
+
+TEST_P(DynamicPartitionControlAndroidGroupTestP, ResizeGroup) {
+ DeltaArchiveManifest expected;
+ AddGroupAndPartition(&expected, T("android"), 2_GiB, T("system"), 2_GiB);
+ AddGroupAndPartition(&expected, T("oem"), 3_GiB, T("vendor"), 3_GiB);
+ DeltaArchiveManifest update_manifest;
+ AddGroupAndPartition(&update_manifest, "android", 2_GiB, "system", 2_GiB),
+ AddGroupAndPartition(&update_manifest, "oem", 3_GiB, "vendor", 3_GiB);
+ EXPECT_TRUE(
+ UpdatePartitionMetadata(source_manifest, update_manifest, expected));
+}
+
+INSTANTIATE_TEST_CASE_P(DynamicPartitionControlAndroidTest,
+ DynamicPartitionControlAndroidGroupTestP,
+ testing::Values(TestParam{0, 1}, TestParam{1, 0}));
+
+const PartitionSuffixSizes update_sizes_0() {
+ // Initial state is 0 for "other" slot.
+ return {
+ {"grown_a", 2_GiB},
+ {"shrunk_a", 1_GiB},
+ {"same_a", 100_MiB},
+ {"deleted_a", 150_MiB},
+ // no added_a
+ {"grown_b", 200_MiB},
+ // simulate system_other
+ {"shrunk_b", 0},
+ {"same_b", 0},
+ {"deleted_b", 0},
+ // no added_b
+ };
+}
+
+const PartitionSuffixSizes update_sizes_1() {
+ return {
+ {"grown_a", 2_GiB},
+ {"shrunk_a", 1_GiB},
+ {"same_a", 100_MiB},
+ {"deleted_a", 150_MiB},
+ // no added_a
+ {"grown_b", 3_GiB},
+ {"shrunk_b", 150_MiB},
+ {"same_b", 100_MiB},
+ {"added_b", 150_MiB},
+ // no deleted_b
+ };
+}
+
+const PartitionSuffixSizes update_sizes_2() {
+ return {
+ {"grown_a", 4_GiB},
+ {"shrunk_a", 100_MiB},
+ {"same_a", 100_MiB},
+ {"deleted_a", 64_MiB},
+ // no added_a
+ {"grown_b", 3_GiB},
+ {"shrunk_b", 150_MiB},
+ {"same_b", 100_MiB},
+ {"added_b", 150_MiB},
+ // no deleted_b
+ };
+}
+
+// Test case for first update after the device is manufactured, in which
+// case the "other" slot is likely of size "0" (except system, which is
+// non-zero because of system_other partition)
+TEST_F(DynamicPartitionControlAndroidTest, SimulatedFirstUpdate) {
+ SetSlots({0, 1});
+
+ SetMetadata(source(), update_sizes_0());
+ SetMetadata(target(), update_sizes_0());
+ ExpectStoreMetadata(update_sizes_1());
+ ExpectUnmap({"grown_b", "shrunk_b", "same_b", "added_b"});
+
+ EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 3_GiB},
+ {"shrunk", 150_MiB},
+ {"same", 100_MiB},
+ {"added", 150_MiB}}));
+}
+
+// After first update, test for the second update. In the second update, the
+// "added" partition is deleted and "deleted" partition is re-added.
+TEST_F(DynamicPartitionControlAndroidTest, SimulatedSecondUpdate) {
+ SetSlots({1, 0});
+
+ SetMetadata(source(), update_sizes_1());
+ SetMetadata(target(), update_sizes_0());
+
+ ExpectStoreMetadata(update_sizes_2());
+ ExpectUnmap({"grown_a", "shrunk_a", "same_a", "deleted_a"});
+
+ EXPECT_TRUE(PreparePartitionsForUpdate({{"grown", 4_GiB},
+ {"shrunk", 100_MiB},
+ {"same", 100_MiB},
+ {"deleted", 64_MiB}}));
+}
+
+} // namespace chromeos_update_engine
diff --git a/dynamic_partition_control_interface.h b/dynamic_partition_control_interface.h
index 86a0730..0ccfcd6 100644
--- a/dynamic_partition_control_interface.h
+++ b/dynamic_partition_control_interface.h
@@ -26,17 +26,33 @@
#include <libdm/dm.h>
#include <liblp/builder.h>
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/update_metadata.pb.h"
+
namespace chromeos_update_engine {
+struct FeatureFlag {
+ enum class Value { NONE = 0, RETROFIT, LAUNCH };
+ constexpr explicit FeatureFlag(Value value) : value_(value) {}
+ constexpr bool IsEnabled() const { return value_ != Value::NONE; }
+ constexpr bool IsRetrofit() const { return value_ == Value::RETROFIT; }
+
+ private:
+ Value value_;
+};
+
class DynamicPartitionControlInterface {
public:
virtual ~DynamicPartitionControlInterface() = default;
- // Return true iff dynamic partitions is enabled on this device.
- virtual bool IsDynamicPartitionsEnabled() = 0;
+ // Return the feature flags of dynamic partitions on this device.
+ // Return RETROFIT iff dynamic partitions is retrofitted on this device,
+ // LAUNCH iff this device is launched with dynamic partitions,
+ // NONE iff dynamic partitions is disabled on this device.
+ virtual FeatureFlag GetDynamicPartitionsFeatureFlag() = 0;
- // Return true iff dynamic partitions is retrofitted on this device.
- virtual bool IsDynamicPartitionsRetrofit() = 0;
+ // Return the feature flags of Virtual A/B on this device.
+ virtual FeatureFlag GetVirtualAbFeatureFlag() = 0;
// Map logical partition on device-mapper.
// |super_device| is the device path of the physical partition ("super").
@@ -52,13 +68,6 @@
bool force_writable,
std::string* path) = 0;
- // Unmap logical partition on device mapper. This is the reverse operation
- // of MapPartitionOnDeviceMapper.
- // If |wait| is set, wait until the device is unmapped.
- // Returns true if unmapped successfully.
- virtual bool UnmapPartitionOnDeviceMapper(
- const std::string& target_partition_name, bool wait) = 0;
-
// Do necessary cleanups before destroying the object.
virtual void Cleanup() = 0;
@@ -77,20 +86,25 @@
std::string* path) = 0;
// Retrieve metadata from |super_device| at slot |source_slot|.
- // On retrofit devices, if |target_slot| != kInvalidSlot, the returned
- // metadata automatically includes block devices at |target_slot|.
virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
- const std::string& super_device,
- uint32_t source_slot,
- uint32_t target_slot) = 0;
+ const std::string& super_device, uint32_t source_slot) = 0;
- // Write metadata |builder| to |super_device| at slot |target_slot|.
- virtual bool StoreMetadata(const std::string& super_device,
- android::fs_mgr::MetadataBuilder* builder,
- uint32_t target_slot) = 0;
+ // Prepare all partitions for an update specified in |manifest|.
+ // This is needed before calling MapPartitionOnDeviceMapper(), otherwise the
+ // device would be mapped in an inconsistent way.
+ virtual bool PreparePartitionsForUpdate(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const DeltaArchiveManifest& manifest) = 0;
// Return a possible location for devices listed by name.
virtual bool GetDeviceDir(std::string* path) = 0;
+
+ // Return the name of the super partition (which stores super partition
+ // metadata) for a given slot.
+ virtual std::string GetSuperPartitionName(uint32_t slot) = 0;
+
+ virtual bool FinishUpdate() = 0;
};
} // namespace chromeos_update_engine
diff --git a/dynamic_partition_test_utils.h b/dynamic_partition_test_utils.h
new file mode 100644
index 0000000..346998f
--- /dev/null
+++ b/dynamic_partition_test_utils.h
@@ -0,0 +1,286 @@
+//
+// Copyright (C) 2019 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.
+//
+
+#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
+#define UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/strings/string_util.h>
+#include <fs_mgr.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <storage_literals/storage_literals.h>
+
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+using android::fs_mgr::MetadataBuilder;
+using testing::_;
+using testing::MakeMatcher;
+using testing::Matcher;
+using testing::MatcherInterface;
+using testing::MatchResultListener;
+using namespace android::storage_literals; // NOLINT(build/namespaces)
+
+constexpr const uint32_t kMaxNumSlots = 2;
+constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
+constexpr const char* kFakeDevicePath = "/fake/dev/path/";
+constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/";
+constexpr const uint32_t kFakeMetadataSize = 65536;
+constexpr const char* kDefaultGroup = "foo";
+constexpr const char* kFakeSuper = "fake_super";
+
+// A map describing the size of each partition.
+// "{name, size}"
+using PartitionSizes = std::map<std::string, uint64_t>;
+
+// "{name_a, size}"
+using PartitionSuffixSizes = std::map<std::string, uint64_t>;
+
+constexpr uint64_t kDefaultGroupSize = 5_GiB;
+// Super device size. 1 MiB for metadata.
+constexpr uint64_t kDefaultSuperSize = kDefaultGroupSize * 2 + 1_MiB;
+
+template <typename U, typename V>
+inline std::ostream& operator<<(std::ostream& os, const std::map<U, V>& param) {
+ os << "{";
+ bool first = true;
+ for (const auto& pair : param) {
+ if (!first)
+ os << ", ";
+ os << pair.first << ":" << pair.second;
+ first = false;
+ }
+ return os << "}";
+}
+
+template <typename V>
+inline void VectorToStream(std::ostream& os, const V& param) {
+ os << "[";
+ bool first = true;
+ for (const auto& e : param) {
+ if (!first)
+ os << ", ";
+ os << e;
+ first = false;
+ }
+ os << "]";
+}
+
+inline std::ostream& operator<<(std::ostream& os, const PartitionUpdate& p) {
+ return os << "{" << p.partition_name() << ", "
+ << p.new_partition_info().size() << "}";
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ const DynamicPartitionGroup& g) {
+ os << "{" << g.name() << ", " << g.size() << ", ";
+ VectorToStream(os, g.partition_names());
+ return os << "}";
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ const DeltaArchiveManifest& m) {
+ os << "{.groups = ";
+ VectorToStream(os, m.dynamic_partition_metadata().groups());
+ os << ", .partitions = ";
+ VectorToStream(os, m.partitions());
+ return os;
+}
+
+inline std::string GetDevice(const std::string& name) {
+ return kFakeDevicePath + name;
+}
+
+inline std::string GetDmDevice(const std::string& name) {
+ return kFakeDmDevicePath + name;
+}
+
+inline DynamicPartitionGroup* AddGroup(DeltaArchiveManifest* manifest,
+ const std::string& group,
+ uint64_t group_size) {
+ auto* g = manifest->mutable_dynamic_partition_metadata()->add_groups();
+ g->set_name(group);
+ g->set_size(group_size);
+ return g;
+}
+
+inline void AddPartition(DeltaArchiveManifest* manifest,
+ DynamicPartitionGroup* group,
+ const std::string& partition,
+ uint64_t partition_size) {
+ group->add_partition_names(partition);
+ auto* p = manifest->add_partitions();
+ p->set_partition_name(partition);
+ p->mutable_new_partition_info()->set_size(partition_size);
+}
+
+// To support legacy tests, auto-convert {name_a: size} map to
+// DeltaArchiveManifest.
+inline DeltaArchiveManifest PartitionSuffixSizesToManifest(
+ const PartitionSuffixSizes& partition_sizes) {
+ DeltaArchiveManifest manifest;
+ for (const char* suffix : kSlotSuffixes) {
+ AddGroup(&manifest, std::string(kDefaultGroup) + suffix, kDefaultGroupSize);
+ }
+ for (const auto& pair : partition_sizes) {
+ for (size_t suffix_idx = 0; suffix_idx < kMaxNumSlots; ++suffix_idx) {
+ if (base::EndsWith(pair.first,
+ kSlotSuffixes[suffix_idx],
+ base::CompareCase::SENSITIVE)) {
+ AddPartition(
+ &manifest,
+ manifest.mutable_dynamic_partition_metadata()->mutable_groups(
+ suffix_idx),
+ pair.first,
+ pair.second);
+ }
+ }
+ }
+ return manifest;
+}
+
+// To support legacy tests, auto-convert {name: size} map to PartitionMetadata.
+inline DeltaArchiveManifest PartitionSizesToManifest(
+ const PartitionSizes& partition_sizes) {
+ DeltaArchiveManifest manifest;
+ auto* g = AddGroup(&manifest, std::string(kDefaultGroup), kDefaultGroupSize);
+ for (const auto& pair : partition_sizes) {
+ AddPartition(&manifest, g, pair.first, pair.second);
+ }
+ return manifest;
+}
+
+inline std::unique_ptr<MetadataBuilder> NewFakeMetadata(
+ const DeltaArchiveManifest& manifest) {
+ auto builder =
+ MetadataBuilder::New(kDefaultSuperSize, kFakeMetadataSize, kMaxNumSlots);
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ EXPECT_TRUE(builder->AddGroup(group.name(), group.size()));
+ for (const auto& partition_name : group.partition_names()) {
+ EXPECT_NE(
+ nullptr,
+ builder->AddPartition(partition_name, group.name(), 0 /* attr */));
+ }
+ }
+ for (const auto& partition : manifest.partitions()) {
+ auto p = builder->FindPartition(partition.partition_name());
+ EXPECT_TRUE(p && builder->ResizePartition(
+ p, partition.new_partition_info().size()));
+ }
+ return builder;
+}
+
+class MetadataMatcher : public MatcherInterface<MetadataBuilder*> {
+ public:
+ explicit MetadataMatcher(const PartitionSuffixSizes& partition_sizes)
+ : manifest_(PartitionSuffixSizesToManifest(partition_sizes)) {}
+ explicit MetadataMatcher(const DeltaArchiveManifest& manifest)
+ : manifest_(manifest) {}
+
+ bool MatchAndExplain(MetadataBuilder* metadata,
+ MatchResultListener* listener) const override {
+ bool success = true;
+ for (const auto& group : manifest_.dynamic_partition_metadata().groups()) {
+ for (const auto& partition_name : group.partition_names()) {
+ auto p = metadata->FindPartition(partition_name);
+ if (p == nullptr) {
+ if (!success)
+ *listener << "; ";
+ *listener << "No partition " << partition_name;
+ success = false;
+ continue;
+ }
+ const auto& partition_updates = manifest_.partitions();
+ auto it = std::find_if(partition_updates.begin(),
+ partition_updates.end(),
+ [&](const auto& p) {
+ return p.partition_name() == partition_name;
+ });
+ if (it == partition_updates.end()) {
+ *listener << "Can't find partition update " << partition_name;
+ success = false;
+ continue;
+ }
+ auto partition_size = it->new_partition_info().size();
+ if (p->size() != partition_size) {
+ if (!success)
+ *listener << "; ";
+ *listener << "Partition " << partition_name << " has size "
+ << p->size() << ", expected " << partition_size;
+ success = false;
+ }
+ if (p->group_name() != group.name()) {
+ if (!success)
+ *listener << "; ";
+ *listener << "Partition " << partition_name << " has group "
+ << p->group_name() << ", expected " << group.name();
+ success = false;
+ }
+ }
+ }
+ return success;
+ }
+
+ void DescribeTo(std::ostream* os) const override {
+ *os << "expect: " << manifest_;
+ }
+
+ void DescribeNegationTo(std::ostream* os) const override {
+ *os << "expect not: " << manifest_;
+ }
+
+ private:
+ DeltaArchiveManifest manifest_;
+};
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+ const PartitionSuffixSizes& partition_sizes) {
+ return MakeMatcher(new MetadataMatcher(partition_sizes));
+}
+
+inline Matcher<MetadataBuilder*> MetadataMatches(
+ const DeltaArchiveManifest& manifest) {
+ return MakeMatcher(new MetadataMatcher(manifest));
+}
+
+MATCHER_P(HasGroup, group, " has group " + group) {
+ auto groups = arg->ListGroups();
+ return std::find(groups.begin(), groups.end(), group) != groups.end();
+}
+
+struct TestParam {
+ uint32_t source;
+ uint32_t target;
+};
+inline std::ostream& operator<<(std::ostream& os, const TestParam& param) {
+ return os << "{source: " << param.source << ", target:" << param.target
+ << "}";
+}
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_DYNAMIC_PARTITION_TEST_UTILS_H_
diff --git a/dynamic_partition_utils.cc b/dynamic_partition_utils.cc
new file mode 100644
index 0000000..f9bd886
--- /dev/null
+++ b/dynamic_partition_utils.cc
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2019 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 "update_engine/dynamic_partition_utils.h"
+
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+
+using android::fs_mgr::MetadataBuilder;
+
+namespace chromeos_update_engine {
+
+void DeleteGroupsWithSuffix(MetadataBuilder* builder,
+ const std::string& suffix) {
+ std::vector<std::string> groups = builder->ListGroups();
+ for (const auto& group_name : groups) {
+ if (base::EndsWith(group_name, suffix, base::CompareCase::SENSITIVE)) {
+ LOG(INFO) << "Removing group " << group_name;
+ builder->RemoveGroupAndPartitions(group_name);
+ }
+ }
+}
+
+} // namespace chromeos_update_engine
diff --git a/dynamic_partition_utils.h b/dynamic_partition_utils.h
new file mode 100644
index 0000000..09fce00
--- /dev/null
+++ b/dynamic_partition_utils.h
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 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.
+//
+
+#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
+#define UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
+
+#include <string>
+
+#include <liblp/builder.h>
+
+namespace chromeos_update_engine {
+
+// Delete all groups (and their partitions) in |builder| that have names
+// ending with |suffix|.
+void DeleteGroupsWithSuffix(android::fs_mgr::MetadataBuilder* builder,
+ const std::string& suffix);
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_
diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h
index 24aca06..1af6cfe 100644
--- a/mock_dynamic_partition_control.h
+++ b/mock_dynamic_partition_control.h
@@ -21,6 +21,8 @@
#include <gmock/gmock.h>
+#include "update_engine/common/boot_control_interface.h"
+#include "update_engine/dynamic_partition_control_android.h"
#include "update_engine/dynamic_partition_control_interface.h"
namespace chromeos_update_engine {
@@ -33,7 +35,32 @@
uint32_t,
bool,
std::string*));
- MOCK_METHOD2(UnmapPartitionOnDeviceMapper, bool(const std::string&, bool));
+ MOCK_METHOD0(Cleanup, void());
+ MOCK_METHOD1(DeviceExists, bool(const std::string&));
+ MOCK_METHOD1(GetState, ::android::dm::DmDeviceState(const std::string&));
+ MOCK_METHOD2(GetDmDevicePathByName, bool(const std::string&, std::string*));
+ MOCK_METHOD2(LoadMetadataBuilder,
+ std::unique_ptr<::android::fs_mgr::MetadataBuilder>(
+ const std::string&, uint32_t));
+ MOCK_METHOD1(GetDeviceDir, bool(std::string*));
+ MOCK_METHOD0(GetDynamicPartitionsFeatureFlag, FeatureFlag());
+ MOCK_METHOD3(PreparePartitionsForUpdate,
+ bool(uint32_t, uint32_t, const DeltaArchiveManifest&));
+ MOCK_METHOD1(GetSuperPartitionName, std::string(uint32_t));
+ MOCK_METHOD0(GetVirtualAbFeatureFlag, FeatureFlag());
+ MOCK_METHOD0(FinishUpdate, bool());
+};
+
+class MockDynamicPartitionControlAndroid
+ : public DynamicPartitionControlAndroid {
+ public:
+ MOCK_METHOD5(MapPartitionOnDeviceMapper,
+ bool(const std::string&,
+ const std::string&,
+ uint32_t,
+ bool,
+ std::string*));
+ MOCK_METHOD1(UnmapPartitionOnDeviceMapper, bool(const std::string&));
MOCK_METHOD0(Cleanup, void());
MOCK_METHOD1(DeviceExists, bool(const std::string&));
MOCK_METHOD1(GetState, ::android::dm::DmDeviceState(const std::string&));
@@ -46,8 +73,10 @@
android::fs_mgr::MetadataBuilder*,
uint32_t));
MOCK_METHOD1(GetDeviceDir, bool(std::string*));
- MOCK_METHOD0(IsDynamicPartitionsEnabled, bool());
- MOCK_METHOD0(IsDynamicPartitionsRetrofit, bool());
+ MOCK_METHOD0(GetDynamicPartitionsFeatureFlag, FeatureFlag());
+ MOCK_METHOD1(GetSuperPartitionName, std::string(uint32_t));
+ MOCK_METHOD0(GetVirtualAbFeatureFlag, FeatureFlag());
+ MOCK_METHOD0(FinishUpdate, bool());
};
} // namespace chromeos_update_engine
diff --git a/otacerts.zip b/otacerts.zip
new file mode 100644
index 0000000..00a5a51
--- /dev/null
+++ b/otacerts.zip
Binary files differ
diff --git a/payload_consumer/certificate_parser_android.cc b/payload_consumer/certificate_parser_android.cc
new file mode 100644
index 0000000..4a20547
--- /dev/null
+++ b/payload_consumer/certificate_parser_android.cc
@@ -0,0 +1,121 @@
+//
+// Copyright (C) 2019 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 "update_engine/payload_consumer/certificate_parser_android.h"
+
+#include <memory>
+#include <utility>
+
+#include <base/logging.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <ziparchive/zip_archive.h>
+
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
+
+namespace {
+bool IterateZipEntriesAndSearchForKeys(
+ const ZipArchiveHandle& handle, std::vector<std::vector<uint8_t>>* result) {
+ void* cookie;
+ int32_t iter_status = StartIteration(handle, &cookie, "", "x509.pem");
+ if (iter_status != 0) {
+ LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: "
+ << ErrorCodeString(iter_status);
+ return false;
+ }
+ std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+
+ std::vector<std::vector<uint8_t>> pem_keys;
+ std::string_view name;
+ ZipEntry entry;
+ while ((iter_status = Next(cookie, &entry, &name)) == 0) {
+ std::vector<uint8_t> pem_content(entry.uncompressed_length);
+ if (int32_t extract_status = ExtractToMemory(
+ handle, &entry, pem_content.data(), pem_content.size());
+ extract_status != 0) {
+ LOG(ERROR) << "Failed to extract " << name << ": "
+ << ErrorCodeString(extract_status);
+ return false;
+ }
+ pem_keys.push_back(pem_content);
+ }
+
+ if (iter_status != -1) {
+ LOG(ERROR) << "Error while iterating over zip entries: "
+ << ErrorCodeString(iter_status);
+ return false;
+ }
+
+ *result = std::move(pem_keys);
+ return true;
+}
+
+} // namespace
+
+namespace chromeos_update_engine {
+bool CertificateParserAndroid::ReadPublicKeysFromCertificates(
+ const std::string& path,
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+ out_public_keys) {
+ out_public_keys->clear();
+
+ ZipArchiveHandle handle;
+ if (int32_t open_status = OpenArchive(path.c_str(), &handle);
+ open_status != 0) {
+ LOG(ERROR) << "Failed to open " << path << ": "
+ << ErrorCodeString(open_status);
+ return false;
+ }
+
+ std::vector<std::vector<uint8_t>> pem_certs;
+ if (!IterateZipEntriesAndSearchForKeys(handle, &pem_certs)) {
+ CloseArchive(handle);
+ return false;
+ }
+ CloseArchive(handle);
+
+ // Convert the certificates into public keys. Stop and return false if we
+ // encounter an error.
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> result;
+ for (const auto& cert : pem_certs) {
+ std::unique_ptr<BIO, decltype(&BIO_free)> input(
+ BIO_new_mem_buf(cert.data(), cert.size()), BIO_free);
+
+ std::unique_ptr<X509, decltype(&X509_free)> x509(
+ PEM_read_bio_X509(input.get(), nullptr, nullptr, nullptr), X509_free);
+ if (!x509) {
+ LOG(ERROR) << "Failed to read x509 certificate";
+ return false;
+ }
+
+ std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> public_key(
+ X509_get_pubkey(x509.get()), EVP_PKEY_free);
+ if (!public_key) {
+ LOG(ERROR) << "Failed to extract the public key from x509 certificate";
+ return false;
+ }
+ result.push_back(std::move(public_key));
+ }
+
+ *out_public_keys = std::move(result);
+ return true;
+}
+
+std::unique_ptr<CertificateParserInterface> CreateCertificateParser() {
+ return std::make_unique<CertificateParserAndroid>();
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/certificate_parser_android.h b/payload_consumer/certificate_parser_android.h
new file mode 100644
index 0000000..ccb9293
--- /dev/null
+++ b/payload_consumer/certificate_parser_android.h
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2019 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.
+//
+
+#ifndef UPDATE_ENGINE_CERTIFICATE_PARSER_ANDROID_H_
+#define UPDATE_ENGINE_CERTIFICATE_PARSER_ANDROID_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "payload_consumer/certificate_parser_interface.h"
+
+namespace chromeos_update_engine {
+// This class parses the certificates from a zip file. Because the Android
+// build system stores the certs in otacerts.zip.
+class CertificateParserAndroid : public CertificateParserInterface {
+ public:
+ CertificateParserAndroid() = default;
+
+ bool ReadPublicKeysFromCertificates(
+ const std::string& path,
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+ out_public_keys) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CertificateParserAndroid);
+};
+
+} // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/certificate_parser_android_unittest.cc b/payload_consumer/certificate_parser_android_unittest.cc
new file mode 100644
index 0000000..e300414
--- /dev/null
+++ b/payload_consumer/certificate_parser_android_unittest.cc
@@ -0,0 +1,61 @@
+//
+// Copyright (C) 2019 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 "update_engine/payload_consumer/certificate_parser_interface.h"
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
+#include "update_engine/payload_generator/payload_signer.h"
+
+namespace chromeos_update_engine {
+
+extern const char* kUnittestPrivateKeyPath;
+const char* kUnittestOtacertsPath = "otacerts.zip";
+
+TEST(CertificateParserAndroidTest, ParseZipArchive) {
+ std::string ota_cert =
+ test_utils::GetBuildArtifactsPath(kUnittestOtacertsPath);
+ ASSERT_TRUE(utils::FileExists(ota_cert.c_str()));
+
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> keys;
+ auto parser = CreateCertificateParser();
+ ASSERT_TRUE(parser->ReadPublicKeysFromCertificates(ota_cert, &keys));
+ ASSERT_EQ(1u, keys.size());
+}
+
+TEST(CertificateParserAndroidTest, VerifySignature) {
+ brillo::Blob hash_blob;
+ ASSERT_TRUE(HashCalculator::RawHashOfData({'x'}, &hash_blob));
+ brillo::Blob sig_blob;
+ ASSERT_TRUE(PayloadSigner::SignHash(
+ hash_blob,
+ test_utils::GetBuildArtifactsPath(kUnittestPrivateKeyPath),
+ &sig_blob));
+
+ auto verifier = PayloadVerifier::CreateInstanceFromZipPath(
+ test_utils::GetBuildArtifactsPath(kUnittestOtacertsPath));
+ ASSERT_TRUE(verifier != nullptr);
+ ASSERT_TRUE(verifier->VerifyRawSignature(sig_blob, hash_blob, nullptr));
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/certificate_parser_interface.h b/payload_consumer/certificate_parser_interface.h
new file mode 100644
index 0000000..dad23d2
--- /dev/null
+++ b/payload_consumer/certificate_parser_interface.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2019 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.
+//
+
+#ifndef UPDATE_ENGINE_CERTIFICATE_PARSER_INTERFACE_H_
+#define UPDATE_ENGINE_CERTIFICATE_PARSER_INTERFACE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <openssl/pem.h>
+
+namespace chromeos_update_engine {
+
+// This class parses the PEM encoded X509 certificates from |path|; and
+// passes the parsed public keys to the caller.
+class CertificateParserInterface {
+ public:
+ virtual ~CertificateParserInterface() = default;
+
+ virtual bool ReadPublicKeysFromCertificates(
+ const std::string& path,
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+ out_public_keys) = 0;
+};
+
+std::unique_ptr<CertificateParserInterface> CreateCertificateParser();
+
+} // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/certificate_parser_stub.cc b/payload_consumer/certificate_parser_stub.cc
new file mode 100644
index 0000000..95fd6e8
--- /dev/null
+++ b/payload_consumer/certificate_parser_stub.cc
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2019 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 <payload_consumer/certificate_parser_stub.h>
+
+namespace chromeos_update_engine {
+bool CertificateParserStub::ReadPublicKeysFromCertificates(
+ const std::string& path,
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+ out_public_keys) {
+ return true;
+}
+
+std::unique_ptr<CertificateParserInterface> CreateCertificateParser() {
+ return std::make_unique<CertificateParserStub>();
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/certificate_parser_stub.h b/payload_consumer/certificate_parser_stub.h
new file mode 100644
index 0000000..f4f8825
--- /dev/null
+++ b/payload_consumer/certificate_parser_stub.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2019 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.
+//
+
+#ifndef UPDATE_ENGINE_CERTIFICATE_PARSER_STUB_H_
+#define UPDATE_ENGINE_CERTIFICATE_PARSER_STUB_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "payload_consumer/certificate_parser_interface.h"
+
+namespace chromeos_update_engine {
+class CertificateParserStub : public CertificateParserInterface {
+ public:
+ CertificateParserStub() = default;
+
+ bool ReadPublicKeysFromCertificates(
+ const std::string& path,
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+ out_public_keys) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CertificateParserStub);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_CERTIFICATE_PARSER_STUB_H_
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index d76a959..4b80ae6 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -46,6 +46,7 @@
#include "update_engine/common/terminator.h"
#include "update_engine/payload_consumer/bzip_extent_writer.h"
#include "update_engine/payload_consumer/cached_file_descriptor.h"
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
#include "update_engine/payload_consumer/download_action.h"
#include "update_engine/payload_consumer/extent_reader.h"
#include "update_engine/payload_consumer/extent_writer.h"
@@ -526,9 +527,10 @@
<< "Trusting metadata size in payload = " << metadata_size_;
}
- string public_key;
- if (!GetPublicKey(&public_key)) {
- LOG(ERROR) << "Failed to get public key.";
+ // Perform the verification unconditionally.
+ auto [payload_verifier, perform_verification] = CreatePayloadVerifier();
+ if (!payload_verifier) {
+ LOG(ERROR) << "Failed to create payload verifier.";
*error = ErrorCode::kDownloadMetadataSignatureVerificationError;
return MetadataParseResult::kError;
}
@@ -536,7 +538,7 @@
// We have the full metadata in |payload|. Verify its integrity
// and authenticity based on the information we have in Omaha response.
*error = payload_metadata_.ValidateMetadataSignature(
- payload, payload_->metadata_signature, public_key);
+ payload, payload_->metadata_signature, *payload_verifier);
if (*error != ErrorCode::kSuccess) {
if (install_plan_->hash_checks_mandatory) {
// The autoupdate_CatchBadSignatures test checks for this string
@@ -808,7 +810,6 @@
for (const PartitionUpdate& partition : manifest_.partitions()) {
partitions_.push_back(partition);
}
- manifest_.clear_partitions();
} else if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
LOG(INFO) << "Converting update information from old format.";
PartitionUpdate root_part;
@@ -923,12 +924,16 @@
}
if (install_plan_->target_slot != BootControlInterface::kInvalidSlot) {
- if (!InitPartitionMetadata()) {
+ if (!PreparePartitionsForUpdate()) {
*error = ErrorCode::kInstallDeviceOpenError;
return false;
}
}
+ if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+ manifest_.clear_partitions();
+ }
+
if (!install_plan_->LoadPartitionsFromSlots(boot_control_)) {
LOG(ERROR) << "Unable to determine all the partition devices.";
*error = ErrorCode::kInstallDeviceOpenError;
@@ -938,45 +943,18 @@
return true;
}
-bool DeltaPerformer::InitPartitionMetadata() {
- BootControlInterface::PartitionMetadata partition_metadata;
- if (manifest_.has_dynamic_partition_metadata()) {
- std::map<string, uint64_t> partition_sizes;
- for (const auto& partition : install_plan_->partitions) {
- partition_sizes.emplace(partition.name, partition.target_size);
- }
- for (const auto& group : manifest_.dynamic_partition_metadata().groups()) {
- BootControlInterface::PartitionMetadata::Group e;
- e.name = group.name();
- e.size = group.size();
- for (const auto& partition_name : group.partition_names()) {
- auto it = partition_sizes.find(partition_name);
- if (it == partition_sizes.end()) {
- // TODO(tbao): Support auto-filling partition info for framework-only
- // OTA.
- LOG(ERROR) << "dynamic_partition_metadata contains partition "
- << partition_name
- << " but it is not part of the manifest. "
- << "This is not supported.";
- return false;
- }
- e.partitions.push_back({partition_name, it->second});
- }
- partition_metadata.groups.push_back(std::move(e));
- }
- }
-
+bool DeltaPerformer::PreparePartitionsForUpdate() {
bool metadata_updated = false;
prefs_->GetBoolean(kPrefsDynamicPartitionMetadataUpdated, &metadata_updated);
- if (!boot_control_->InitPartitionMetadata(
- install_plan_->target_slot, partition_metadata, !metadata_updated)) {
+ if (!boot_control_->PreparePartitionsForUpdate(
+ install_plan_->target_slot, manifest_, !metadata_updated)) {
LOG(ERROR) << "Unable to initialize partition metadata for slot "
<< BootControlInterface::SlotName(install_plan_->target_slot);
return false;
}
TEST_AND_RETURN_FALSE(
prefs_->SetBoolean(kPrefsDynamicPartitionMetadataUpdated, true));
- LOG(INFO) << "InitPartitionMetadata done.";
+ LOG(INFO) << "PreparePartitionsForUpdate done.";
return true;
}
@@ -1620,10 +1598,32 @@
return brillo::data_encoding::Base64Decode(install_plan_->public_key_rsa,
out_public_key);
}
-
+ LOG(INFO) << "No public keys found for verification.";
return true;
}
+std::pair<std::unique_ptr<PayloadVerifier>, bool>
+DeltaPerformer::CreatePayloadVerifier() {
+ if (utils::FileExists(update_certificates_path_.c_str())) {
+ LOG(INFO) << "Verifying using certificates: " << update_certificates_path_;
+ return {
+ PayloadVerifier::CreateInstanceFromZipPath(update_certificates_path_),
+ true};
+ }
+
+ string public_key;
+ if (!GetPublicKey(&public_key)) {
+ LOG(ERROR) << "Failed to read public key";
+ return {nullptr, true};
+ }
+
+ // Skips the verification if the public key is empty.
+ if (public_key.empty()) {
+ return {nullptr, false};
+ }
+ return {PayloadVerifier::CreateInstance(public_key), true};
+}
+
ErrorCode DeltaPerformer::ValidateManifest() {
// Perform assorted checks to sanity check the manifest, make sure it
// matches data from other sources, and that it is a supported version.
@@ -1784,12 +1784,6 @@
ErrorCode DeltaPerformer::VerifyPayload(
const brillo::Blob& update_check_response_hash,
const uint64_t update_check_response_size) {
- string public_key;
- if (!GetPublicKey(&public_key)) {
- LOG(ERROR) << "Failed to get public key.";
- return ErrorCode::kDownloadPayloadPubKeyVerificationError;
- }
-
// Verifies the download size.
if (update_check_response_size !=
metadata_size_ + metadata_signature_size_ + buffer_offset_) {
@@ -1807,19 +1801,22 @@
ErrorCode::kPayloadHashMismatchError,
payload_hash_calculator_.raw_hash() == update_check_response_hash);
- // Verifies the signed payload hash.
- if (public_key.empty()) {
- LOG(WARNING) << "Not verifying signed delta payload -- missing public key.";
- return ErrorCode::kSuccess;
- }
TEST_AND_RETURN_VAL(ErrorCode::kSignedDeltaPayloadExpectedError,
!signatures_message_data_.empty());
brillo::Blob hash_data = signed_hash_calculator_.raw_hash();
TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
hash_data.size() == kSHA256Size);
- if (!PayloadVerifier::VerifySignature(
- signatures_message_data_, public_key, hash_data)) {
+ auto [payload_verifier, perform_verification] = CreatePayloadVerifier();
+ if (!perform_verification) {
+ LOG(WARNING) << "Not verifying signed delta payload -- missing public key.";
+ return ErrorCode::kSuccess;
+ }
+ if (!payload_verifier) {
+ LOG(ERROR) << "Failed to create the payload verifier.";
+ return ErrorCode::kDownloadPayloadPubKeyVerificationError;
+ }
+ if (!payload_verifier->VerifySignature(signatures_message_data_, hash_data)) {
// The autoupdate_CatchBadSignatures test checks for this string
// in log-files. Keep in sync.
LOG(ERROR) << "Public key verification failed, thus update failed.";
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 17cb599..4c64dfa 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -20,7 +20,9 @@
#include <inttypes.h>
#include <limits>
+#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include <base/time/time.h>
@@ -34,6 +36,7 @@
#include "update_engine/payload_consumer/file_writer.h"
#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/payload_consumer/payload_metadata.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
@@ -156,6 +159,11 @@
public_key_path_ = public_key_path;
}
+ void set_update_certificates_path(
+ const std::string& update_certificates_path) {
+ update_certificates_path_ = update_certificates_path;
+ }
+
// Return true if header parsing is finished and no errors occurred.
bool IsHeaderParsed() const;
@@ -273,9 +281,15 @@
// |out_public_key|. Returns false on failures.
bool GetPublicKey(std::string* out_public_key);
+ // Creates a PayloadVerifier from the zip file containing certificates. If the
+ // path to the zip file doesn't exist, falls back to use the public key.
+ // Returns a tuple with the created PayloadVerifier and if we should perform
+ // the verification.
+ std::pair<std::unique_ptr<PayloadVerifier>, bool> CreatePayloadVerifier();
+
// After install_plan_ is filled with partition names and sizes, initialize
// metadata of partitions and map necessary devices before opening devices.
- bool InitPartitionMetadata();
+ bool PreparePartitionsForUpdate();
// Update Engine preference store.
PrefsInterface* prefs_;
@@ -383,6 +397,9 @@
// override with test keys.
std::string public_key_path_{constants::kUpdatePayloadPublicKeyPath};
+ // The path to the zip file with X509 certificates.
+ std::string update_certificates_path_{constants::kUpdateCertificatesPath};
+
// The number of bytes received so far, used for progress tracking.
size_t total_bytes_received_{0};
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index 6b4771d..a2ad77b 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -60,6 +60,8 @@
extern const char* kUnittestPublicKeyPath;
extern const char* kUnittestPrivateKey2Path;
extern const char* kUnittestPublicKey2Path;
+extern const char* kUnittestPrivateKeyECPath;
+extern const char* kUnittestPublicKeyECPath;
static const uint32_t kDefaultKernelSize = 4096; // Something small for a test
// clang-format off
@@ -107,6 +109,7 @@
kSignatureGeneratedPlaceholder, // Insert placeholder signatures, then real.
kSignatureGeneratedPlaceholderMismatch, // Insert a wrong sized placeholder.
kSignatureGeneratedShell, // Sign the generated payload through shell cmds.
+ kSignatureGeneratedShellECKey, // Sign with a EC key through shell cmds.
kSignatureGeneratedShellBadKey, // Sign with a bad key through shell cmds.
kSignatureGeneratedShellRotateCl1, // Rotate key, test client v1
kSignatureGeneratedShellRotateCl2, // Rotate key, test client v2
@@ -164,53 +167,127 @@
return true;
}
-static size_t GetSignatureSize(const string& private_key_path) {
- const brillo::Blob data(1, 'x');
- brillo::Blob hash;
- EXPECT_TRUE(HashCalculator::RawHashOfData(data, &hash));
- brillo::Blob signature;
- EXPECT_TRUE(PayloadSigner::SignHash(hash, private_key_path, &signature));
- return signature.size();
-}
-
-static bool InsertSignaturePlaceholder(int signature_size,
+static bool InsertSignaturePlaceholder(size_t signature_size,
const string& payload_path,
uint64_t* out_metadata_size) {
vector<brillo::Blob> signatures;
signatures.push_back(brillo::Blob(signature_size, 0));
- return PayloadSigner::AddSignatureToPayload(
- payload_path, signatures, {}, payload_path, out_metadata_size);
+ return PayloadSigner::AddSignatureToPayload(payload_path,
+ {signature_size},
+ signatures,
+ {},
+ payload_path,
+ out_metadata_size);
}
static void SignGeneratedPayload(const string& payload_path,
uint64_t* out_metadata_size) {
string private_key_path = GetBuildArtifactsPath(kUnittestPrivateKeyPath);
- int signature_size = GetSignatureSize(private_key_path);
+ size_t signature_size;
+ ASSERT_TRUE(PayloadSigner::GetMaximumSignatureSize(private_key_path,
+ &signature_size));
brillo::Blob hash;
ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
payload_path, {signature_size}, &hash, nullptr));
brillo::Blob signature;
ASSERT_TRUE(PayloadSigner::SignHash(hash, private_key_path, &signature));
- ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(
- payload_path, {signature}, {}, payload_path, out_metadata_size));
+ ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(payload_path,
+ {signature_size},
+ {signature},
+ {},
+ payload_path,
+ out_metadata_size));
EXPECT_TRUE(PayloadSigner::VerifySignedPayload(
payload_path, GetBuildArtifactsPath(kUnittestPublicKeyPath)));
}
+static void SignGeneratedShellPayloadWithKeys(
+ const string& payload_path,
+ const vector<string>& private_key_paths,
+ const string& public_key_path,
+ bool verification_success) {
+ vector<string> signature_size_strings;
+ for (const auto& key_path : private_key_paths) {
+ size_t signature_size;
+ ASSERT_TRUE(
+ PayloadSigner::GetMaximumSignatureSize(key_path, &signature_size));
+ signature_size_strings.push_back(base::StringPrintf("%zu", signature_size));
+ }
+ string signature_size_string = base::JoinString(signature_size_strings, ":");
+
+ test_utils::ScopedTempFile hash_file("hash.XXXXXX");
+ string delta_generator_path = GetBuildArtifactsPath("delta_generator");
+ ASSERT_EQ(0,
+ System(base::StringPrintf(
+ "%s -in_file=%s -signature_size=%s -out_hash_file=%s",
+ delta_generator_path.c_str(),
+ payload_path.c_str(),
+ signature_size_string.c_str(),
+ hash_file.path().c_str())));
+
+ // Sign the hash with all private keys.
+ vector<test_utils::ScopedTempFile> sig_files;
+ vector<string> sig_file_paths;
+ for (const auto& key_path : private_key_paths) {
+ brillo::Blob hash, signature;
+ ASSERT_TRUE(utils::ReadFile(hash_file.path(), &hash));
+ ASSERT_TRUE(PayloadSigner::SignHash(hash, key_path, &signature));
+
+ test_utils::ScopedTempFile sig_file("signature.XXXXXX");
+ ASSERT_TRUE(test_utils::WriteFileVector(sig_file.path(), signature));
+ sig_file_paths.push_back(sig_file.path());
+ sig_files.push_back(std::move(sig_file));
+ }
+ string sig_files_string = base::JoinString(sig_file_paths, ":");
+
+ // Add the signature to the payload.
+ ASSERT_EQ(0,
+ System(base::StringPrintf("%s --signature_size=%s -in_file=%s "
+ "-payload_signature_file=%s -out_file=%s",
+ delta_generator_path.c_str(),
+ signature_size_string.c_str(),
+ payload_path.c_str(),
+ sig_files_string.c_str(),
+ payload_path.c_str())));
+
+ int verify_result = System(base::StringPrintf("%s -in_file=%s -public_key=%s",
+ delta_generator_path.c_str(),
+ payload_path.c_str(),
+ public_key_path.c_str()));
+
+ if (verification_success) {
+ ASSERT_EQ(0, verify_result);
+ } else {
+ ASSERT_NE(0, verify_result);
+ }
+}
+
static void SignGeneratedShellPayload(SignatureTest signature_test,
const string& payload_path) {
- string private_key_path = GetBuildArtifactsPath(kUnittestPrivateKeyPath);
+ vector<SignatureTest> supported_test = {
+ kSignatureGeneratedShell,
+ kSignatureGeneratedShellBadKey,
+ kSignatureGeneratedShellECKey,
+ kSignatureGeneratedShellRotateCl1,
+ kSignatureGeneratedShellRotateCl2,
+ };
+ ASSERT_TRUE(std::find(supported_test.begin(),
+ supported_test.end(),
+ signature_test) != supported_test.end());
+
+ string private_key_path;
if (signature_test == kSignatureGeneratedShellBadKey) {
ASSERT_TRUE(utils::MakeTempFile("key.XXXXXX", &private_key_path, nullptr));
+ } else if (signature_test == kSignatureGeneratedShellECKey) {
+ private_key_path = GetBuildArtifactsPath(kUnittestPrivateKeyECPath);
} else {
- ASSERT_TRUE(signature_test == kSignatureGeneratedShell ||
- signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2);
+ private_key_path = GetBuildArtifactsPath(kUnittestPrivateKeyPath);
}
ScopedPathUnlinker key_unlinker(private_key_path);
key_unlinker.set_should_remove(signature_test ==
kSignatureGeneratedShellBadKey);
+
// Generates a new private key that will not match the public key.
if (signature_test == kSignatureGeneratedShellBadKey) {
LOG(INFO) << "Generating a mismatched private key.";
@@ -229,64 +306,26 @@
fclose(fprikey);
RSA_free(rsa);
}
- int signature_size = GetSignatureSize(private_key_path);
- test_utils::ScopedTempFile hash_file("hash.XXXXXX");
- string signature_size_string;
- if (signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2)
- signature_size_string =
- base::StringPrintf("%d:%d", signature_size, signature_size);
- else
- signature_size_string = base::StringPrintf("%d", signature_size);
- string delta_generator_path = GetBuildArtifactsPath("delta_generator");
- ASSERT_EQ(0,
- System(base::StringPrintf(
- "%s -in_file=%s -signature_size=%s -out_hash_file=%s",
- delta_generator_path.c_str(),
- payload_path.c_str(),
- signature_size_string.c_str(),
- hash_file.path().c_str())));
- // Sign the hash
- brillo::Blob hash, signature;
- ASSERT_TRUE(utils::ReadFile(hash_file.path(), &hash));
- ASSERT_TRUE(PayloadSigner::SignHash(hash, private_key_path, &signature));
-
- test_utils::ScopedTempFile sig_file("signature.XXXXXX");
- ASSERT_TRUE(test_utils::WriteFileVector(sig_file.path(), signature));
- string sig_files = sig_file.path();
-
- test_utils::ScopedTempFile sig_file2("signature.XXXXXX");
+ vector<string> private_key_paths = {private_key_path};
if (signature_test == kSignatureGeneratedShellRotateCl1 ||
signature_test == kSignatureGeneratedShellRotateCl2) {
- ASSERT_TRUE(PayloadSigner::SignHash(
- hash, GetBuildArtifactsPath(kUnittestPrivateKey2Path), &signature));
- ASSERT_TRUE(test_utils::WriteFileVector(sig_file2.path(), signature));
- // Append second sig file to first path
- sig_files += ":" + sig_file2.path();
+ private_key_paths.push_back(
+ GetBuildArtifactsPath(kUnittestPrivateKey2Path));
}
- ASSERT_EQ(0,
- System(base::StringPrintf(
- "%s -in_file=%s -payload_signature_file=%s -out_file=%s",
- delta_generator_path.c_str(),
- payload_path.c_str(),
- sig_files.c_str(),
- payload_path.c_str())));
- int verify_result = System(base::StringPrintf(
- "%s -in_file=%s -public_key=%s -public_key_version=%d",
- delta_generator_path.c_str(),
- payload_path.c_str(),
- (signature_test == kSignatureGeneratedShellRotateCl2
- ? GetBuildArtifactsPath(kUnittestPublicKey2Path)
- : GetBuildArtifactsPath(kUnittestPublicKeyPath))
- .c_str(),
- signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1));
- if (signature_test == kSignatureGeneratedShellBadKey) {
- ASSERT_NE(0, verify_result);
+ std::string public_key;
+ if (signature_test == kSignatureGeneratedShellRotateCl2) {
+ public_key = GetBuildArtifactsPath(kUnittestPublicKey2Path);
+ } else if (signature_test == kSignatureGeneratedShellECKey) {
+ public_key = GetBuildArtifactsPath(kUnittestPublicKeyECPath);
} else {
- ASSERT_EQ(0, verify_result);
+ public_key = GetBuildArtifactsPath(kUnittestPublicKeyPath);
}
+
+ bool verification_success = signature_test != kSignatureGeneratedShellBadKey;
+ SignGeneratedShellPayloadWithKeys(
+ payload_path, private_key_paths, public_key, verification_success);
}
static void GenerateDeltaFile(bool full_kernel,
@@ -531,8 +570,9 @@
if (signature_test == kSignatureGeneratedPlaceholder ||
signature_test == kSignatureGeneratedPlaceholderMismatch) {
- int signature_size =
- GetSignatureSize(GetBuildArtifactsPath(kUnittestPrivateKeyPath));
+ size_t signature_size;
+ ASSERT_TRUE(PayloadSigner::GetMaximumSignatureSize(
+ GetBuildArtifactsPath(kUnittestPrivateKeyPath), &signature_size));
LOG(INFO) << "Inserting placeholder signature.";
ASSERT_TRUE(InsertSignaturePlaceholder(
signature_size, state->delta_path, &state->metadata_size));
@@ -555,6 +595,7 @@
LOG(INFO) << "Signing payload.";
SignGeneratedPayload(state->delta_path, &state->metadata_size);
} else if (signature_test == kSignatureGeneratedShell ||
+ signature_test == kSignatureGeneratedShellECKey ||
signature_test == kSignatureGeneratedShellBadKey ||
signature_test == kSignatureGeneratedShellRotateCl1 ||
signature_test == kSignatureGeneratedShellRotateCl2) {
@@ -597,14 +638,15 @@
else
EXPECT_EQ(1, sigs_message.signatures_size());
const Signatures::Signature& signature = sigs_message.signatures(0);
- EXPECT_EQ(1U, signature.version());
- uint64_t expected_sig_data_length = 0;
vector<string> key_paths{GetBuildArtifactsPath(kUnittestPrivateKeyPath)};
- if (signature_test == kSignatureGeneratedShellRotateCl1 ||
- signature_test == kSignatureGeneratedShellRotateCl2) {
+ if (signature_test == kSignatureGeneratedShellECKey) {
+ key_paths = {GetBuildArtifactsPath(kUnittestPrivateKeyECPath)};
+ } else if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2) {
key_paths.push_back(GetBuildArtifactsPath(kUnittestPrivateKey2Path));
}
+ uint64_t expected_sig_data_length = 0;
EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
key_paths, &expected_sig_data_length));
EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
@@ -717,7 +759,9 @@
ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
state->delta.data(),
state->metadata_size,
- GetBuildArtifactsPath(kUnittestPrivateKeyPath),
+ (signature_test == kSignatureGeneratedShellECKey)
+ ? GetBuildArtifactsPath(kUnittestPrivateKeyECPath)
+ : GetBuildArtifactsPath(kUnittestPrivateKeyPath),
&install_plan->payloads[0].metadata_signature));
EXPECT_FALSE(install_plan->payloads[0].metadata_signature.empty());
@@ -728,9 +772,12 @@
install_plan,
&install_plan->payloads[0],
false /* interactive */);
- string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
+ string public_key_path = signature_test == kSignatureGeneratedShellECKey
+ ? GetBuildArtifactsPath(kUnittestPublicKeyECPath)
+ : GetBuildArtifactsPath(kUnittestPublicKeyPath);
EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
(*performer)->set_public_key_path(public_key_path);
+ (*performer)->set_update_certificates_path("");
EXPECT_EQ(static_cast<off_t>(state->image_size),
HashCalculator::RawHashOfFile(
@@ -1060,6 +1107,17 @@
}
TEST(DeltaPerformerIntegrationTest,
+ RunAsRootSmallImageSignGeneratedShellECKeyTest) {
+ DoSmallImageTest(false,
+ false,
+ false,
+ -1,
+ kSignatureGeneratedShellECKey,
+ false,
+ kInPlaceMinorPayloadVersion);
+}
+
+TEST(DeltaPerformerIntegrationTest,
RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
DoSmallImageTest(false,
false,
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index b7a38cc..e9022ba 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -159,6 +159,11 @@
install_plan_.target_slot = 1;
EXPECT_CALL(mock_delegate_, ShouldCancel(_))
.WillRepeatedly(testing::Return(false));
+ performer_.set_update_certificates_path("");
+ // Set the public key corresponding to the unittest private key.
+ string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
+ EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
+ performer_.set_public_key_path(public_key_path);
}
// Test helper placed where it can easily be friended from DeltaPerformer.
@@ -388,12 +393,6 @@
expected_error = ErrorCode::kSuccess;
}
- // Use the public key corresponding to the private key used above to
- // sign the metadata.
- string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
- EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
- performer_.set_public_key_path(public_key_path);
-
// Init actual_error with an invalid value so that we make sure
// ParsePayloadMetadata properly populates it in all cases.
actual_error = ErrorCode::kUmaReportedMax;
@@ -920,7 +919,6 @@
brillo::Blob payload_data = GeneratePayload(
{}, {}, true, kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion);
install_plan_.hash_checks_mandatory = true;
- performer_.set_public_key_path(GetBuildArtifactsPath(kUnittestPublicKeyPath));
ErrorCode error;
EXPECT_EQ(MetadataParseResult::kSuccess,
performer_.ParsePayloadMetadata(payload_data, &error));
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 2e7b6d4..766b27c 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -19,6 +19,7 @@
#include <base/format_macros.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include "update_engine/common/utils.h"
@@ -80,11 +81,18 @@
base::StringPrintf(", system_version: %s", system_version.c_str());
}
+ string url_str = download_url;
+ if (base::StartsWith(
+ url_str, "fd://", base::CompareCase::INSENSITIVE_ASCII)) {
+ int fd = std::stoi(url_str.substr(strlen("fd://")));
+ url_str = utils::GetFilePath(fd);
+ }
+
LOG(INFO) << "InstallPlan: " << (is_resume ? "resume" : "new_update")
<< version_str
<< ", source_slot: " << BootControlInterface::SlotName(source_slot)
<< ", target_slot: " << BootControlInterface::SlotName(target_slot)
- << ", url: " << download_url << payloads_str << partitions_str
+ << ", url: " << url_str << payloads_str << partitions_str
<< ", hash_checks_mandatory: "
<< utils::ToString(hash_checks_mandatory)
<< ", powerwash_required: " << utils::ToString(powerwash_required)
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc
index 3739767..0952646 100644
--- a/payload_consumer/payload_metadata.cc
+++ b/payload_consumer/payload_metadata.cc
@@ -159,7 +159,7 @@
ErrorCode PayloadMetadata::ValidateMetadataSignature(
const brillo::Blob& payload,
const string& metadata_signature,
- const string& pem_public_key) const {
+ const PayloadVerifier& payload_verifier) const {
if (payload.size() < metadata_size_ + metadata_signature_size_)
return ErrorCode::kDownloadMetadataSignatureError;
@@ -202,31 +202,18 @@
}
if (!metadata_signature_blob.empty()) {
- brillo::Blob expected_metadata_hash;
- if (!PayloadVerifier::GetRawHashFromSignature(
- metadata_signature_blob, pem_public_key, &expected_metadata_hash)) {
- LOG(ERROR) << "Unable to compute expected hash from metadata signature";
- return ErrorCode::kDownloadMetadataSignatureError;
- }
-
- brillo::Blob padded_metadata_hash = metadata_hash;
- if (!PayloadVerifier::PadRSASHA256Hash(&padded_metadata_hash,
- expected_metadata_hash.size())) {
- LOG(ERROR) << "Failed to pad the SHA256 hash to "
- << expected_metadata_hash.size() << " bytes.";
- return ErrorCode::kDownloadMetadataSignatureVerificationError;
- }
-
- if (padded_metadata_hash != expected_metadata_hash) {
- LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
- utils::HexDumpVector(expected_metadata_hash);
- LOG(ERROR) << "Calculated hash = ";
- utils::HexDumpVector(padded_metadata_hash);
+ brillo::Blob decrypted_signature;
+ if (!payload_verifier.VerifyRawSignature(
+ metadata_signature_blob, metadata_hash, &decrypted_signature)) {
+ LOG(ERROR) << "Manifest hash verification failed. Decrypted hash = ";
+ utils::HexDumpVector(decrypted_signature);
+ LOG(ERROR) << "Calculated hash before padding = ";
+ utils::HexDumpVector(metadata_hash);
return ErrorCode::kDownloadMetadataSignatureMismatch;
}
} else {
- if (!PayloadVerifier::VerifySignature(
- metadata_signature_protobuf, pem_public_key, metadata_hash)) {
+ if (!payload_verifier.VerifySignature(metadata_signature_protobuf,
+ metadata_hash)) {
LOG(ERROR) << "Manifest hash verification failed.";
return ErrorCode::kDownloadMetadataSignatureMismatch;
}
diff --git a/payload_consumer/payload_metadata.h b/payload_consumer/payload_metadata.h
index 1b4c5c8..75ef8f9 100644
--- a/payload_consumer/payload_metadata.h
+++ b/payload_consumer/payload_metadata.h
@@ -27,6 +27,7 @@
#include "update_engine/common/error_code.h"
#include "update_engine/common/platform_constants.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
@@ -65,9 +66,10 @@
// metadata is parsed so that a man-in-the-middle attack on the SSL connection
// to the payload server doesn't exploit any vulnerability in the code that
// parses the protocol buffer.
- ErrorCode ValidateMetadataSignature(const brillo::Blob& payload,
- const std::string& metadata_signature,
- const std::string& pem_public_key) const;
+ ErrorCode ValidateMetadataSignature(
+ const brillo::Blob& payload,
+ const std::string& metadata_signature,
+ const PayloadVerifier& payload_verifier) const;
// Returns the major payload version. If the version was not yet parsed,
// returns zero.
diff --git a/payload_consumer/payload_verifier.cc b/payload_consumer/payload_verifier.cc
index 3a3ccbf..24e337e 100644
--- a/payload_consumer/payload_verifier.cc
+++ b/payload_consumer/payload_verifier.cc
@@ -25,6 +25,7 @@
#include "update_engine/common/constants.h"
#include "update_engine/common/hash_calculator.h"
#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
#include "update_engine/update_metadata.pb.h"
using std::string;
@@ -51,9 +52,52 @@
} // namespace
-bool PayloadVerifier::VerifySignature(const string& signature_proto,
- const string& pem_public_key,
- const brillo::Blob& sha256_hash_data) {
+std::unique_ptr<PayloadVerifier> PayloadVerifier::CreateInstance(
+ const std::string& pem_public_key) {
+ std::unique_ptr<BIO, decltype(&BIO_free)> bp(
+ BIO_new_mem_buf(pem_public_key.data(), pem_public_key.size()), BIO_free);
+ if (!bp) {
+ LOG(ERROR) << "Failed to read " << pem_public_key << " into buffer.";
+ return nullptr;
+ }
+
+ auto pub_key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>(
+ PEM_read_bio_PUBKEY(bp.get(), nullptr, nullptr, nullptr), EVP_PKEY_free);
+ if (!pub_key) {
+ LOG(ERROR) << "Failed to parse the public key in: " << pem_public_key;
+ return nullptr;
+ }
+
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> keys;
+ keys.emplace_back(std::move(pub_key));
+ return std::unique_ptr<PayloadVerifier>(new PayloadVerifier(std::move(keys)));
+}
+
+std::unique_ptr<PayloadVerifier> PayloadVerifier::CreateInstanceFromZipPath(
+ const std::string& certificate_zip_path) {
+ auto parser = CreateCertificateParser();
+ if (!parser) {
+ LOG(ERROR) << "Failed to create certificate parser from "
+ << certificate_zip_path;
+ return nullptr;
+ }
+
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> public_keys;
+ if (!parser->ReadPublicKeysFromCertificates(certificate_zip_path,
+ &public_keys) ||
+ public_keys.empty()) {
+ LOG(ERROR) << "Failed to parse public keys in: " << certificate_zip_path;
+ return nullptr;
+ }
+
+ return std::unique_ptr<PayloadVerifier>(
+ new PayloadVerifier(std::move(public_keys)));
+}
+
+bool PayloadVerifier::VerifySignature(
+ const string& signature_proto, const brillo::Blob& sha256_hash_data) const {
+ TEST_AND_RETURN_FALSE(!public_keys_.empty());
+
Signatures signatures;
LOG(INFO) << "signature blob size = " << signature_proto.size();
TEST_AND_RETURN_FALSE(signatures.ParseFromString(signature_proto));
@@ -67,48 +111,104 @@
// Tries every signature in the signature blob.
for (int i = 0; i < signatures.signatures_size(); i++) {
const Signatures::Signature& signature = signatures.signatures(i);
- brillo::Blob sig_data(signature.data().begin(), signature.data().end());
- brillo::Blob sig_hash_data;
- if (!GetRawHashFromSignature(sig_data, pem_public_key, &sig_hash_data))
- continue;
+ brillo::Blob sig_data;
+ if (signature.has_unpadded_signature_size()) {
+ TEST_AND_RETURN_FALSE(signature.unpadded_signature_size() <=
+ signature.data().size());
+ LOG(INFO) << "Truncating the signature to its unpadded size: "
+ << signature.unpadded_signature_size() << ".";
+ sig_data.assign(
+ signature.data().begin(),
+ signature.data().begin() + signature.unpadded_signature_size());
+ } else {
+ sig_data.assign(signature.data().begin(), signature.data().end());
+ }
- brillo::Blob padded_hash_data = sha256_hash_data;
- if (PadRSASHA256Hash(&padded_hash_data, sig_hash_data.size()) &&
- padded_hash_data == sig_hash_data) {
+ brillo::Blob sig_hash_data;
+ if (VerifyRawSignature(sig_data, sha256_hash_data, &sig_hash_data)) {
LOG(INFO) << "Verified correct signature " << i + 1 << " out of "
<< signatures.signatures_size() << " signatures.";
return true;
}
- tested_hashes.push_back(sig_hash_data);
+ if (!sig_hash_data.empty()) {
+ tested_hashes.push_back(sig_hash_data);
+ }
}
LOG(ERROR) << "None of the " << signatures.signatures_size()
<< " signatures is correct. Expected hash before padding:";
utils::HexDumpVector(sha256_hash_data);
- LOG(ERROR) << "But found decrypted hashes:";
+ LOG(ERROR) << "But found RSA decrypted hashes:";
for (const auto& sig_hash_data : tested_hashes) {
utils::HexDumpVector(sig_hash_data);
}
return false;
}
-bool PayloadVerifier::GetRawHashFromSignature(const brillo::Blob& sig_data,
- const string& pem_public_key,
- brillo::Blob* out_hash_data) {
+bool PayloadVerifier::VerifyRawSignature(
+ const brillo::Blob& sig_data,
+ const brillo::Blob& sha256_hash_data,
+ brillo::Blob* decrypted_sig_data) const {
+ TEST_AND_RETURN_FALSE(!public_keys_.empty());
+
+ for (const auto& public_key : public_keys_) {
+ int key_type = EVP_PKEY_id(public_key.get());
+ if (key_type == EVP_PKEY_RSA) {
+ brillo::Blob sig_hash_data;
+ if (!GetRawHashFromSignature(
+ sig_data, public_key.get(), &sig_hash_data)) {
+ LOG(WARNING)
+ << "Failed to get the raw hash with RSA key. Trying other keys.";
+ continue;
+ }
+
+ if (decrypted_sig_data != nullptr) {
+ *decrypted_sig_data = sig_hash_data;
+ }
+
+ brillo::Blob padded_hash_data = sha256_hash_data;
+ TEST_AND_RETURN_FALSE(
+ PadRSASHA256Hash(&padded_hash_data, sig_hash_data.size()));
+
+ if (padded_hash_data == sig_hash_data) {
+ return true;
+ }
+ }
+
+ if (key_type == EVP_PKEY_EC) {
+ EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(public_key.get());
+ TEST_AND_RETURN_FALSE(ec_key != nullptr);
+ if (ECDSA_verify(0,
+ sha256_hash_data.data(),
+ sha256_hash_data.size(),
+ sig_data.data(),
+ sig_data.size(),
+ ec_key) == 1) {
+ return true;
+ }
+ }
+
+ LOG(ERROR) << "Unsupported key type " << key_type;
+ return false;
+ }
+ LOG(INFO) << "Failed to verify the signature with " << public_keys_.size()
+ << " keys.";
+ return false;
+}
+
+bool PayloadVerifier::GetRawHashFromSignature(
+ const brillo::Blob& sig_data,
+ const EVP_PKEY* public_key,
+ brillo::Blob* out_hash_data) const {
// The code below executes the equivalent of:
//
- // openssl rsautl -verify -pubin -inkey <(echo |pem_public_key|)
+ // openssl rsautl -verify -pubin -inkey <(echo pem_public_key)
// -in |sig_data| -out |out_hash_data|
-
- BIO* bp = BIO_new_mem_buf(pem_public_key.data(), pem_public_key.size());
- char dummy_password[] = {' ', 0}; // Ensure no password is read from stdin.
- RSA* rsa = PEM_read_bio_RSA_PUBKEY(bp, nullptr, nullptr, dummy_password);
- BIO_free(bp);
+ RSA* rsa = EVP_PKEY_get0_RSA(public_key);
TEST_AND_RETURN_FALSE(rsa != nullptr);
unsigned int keysize = RSA_size(rsa);
if (sig_data.size() > 2 * keysize) {
LOG(ERROR) << "Signature size is too big for public key size.";
- RSA_free(rsa);
return false;
}
@@ -116,7 +216,6 @@
brillo::Blob hash_data(keysize);
int decrypt_size = RSA_public_decrypt(
sig_data.size(), sig_data.data(), hash_data.data(), rsa, RSA_NO_PADDING);
- RSA_free(rsa);
TEST_AND_RETURN_FALSE(decrypt_size > 0 &&
decrypt_size <= static_cast<int>(hash_data.size()));
hash_data.resize(decrypt_size);
diff --git a/payload_consumer/payload_verifier.h b/payload_consumer/payload_verifier.h
index af8e05f..bc5231f 100644
--- a/payload_consumer/payload_verifier.h
+++ b/payload_consumer/payload_verifier.h
@@ -17,38 +17,24 @@
#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_VERIFIER_H_
#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_VERIFIER_H_
+#include <memory>
#include <string>
+#include <utility>
+#include <vector>
-#include <base/macros.h>
#include <brillo/secure_blob.h>
+#include <openssl/evp.h>
#include "update_engine/update_metadata.pb.h"
-// This class encapsulates methods used for payload signature verification.
-// See payload_generator/payload_signer.h for payload signing.
+// This class holds the public keys and implements methods used for payload
+// signature verification. See payload_generator/payload_signer.h for payload
+// signing.
namespace chromeos_update_engine {
class PayloadVerifier {
public:
- // Interprets |signature_proto| as a protocol buffer containing the Signatures
- // message and decrypts each signature data using the |pem_public_key|.
- // |pem_public_key| should be a PEM format RSA public key data.
- // Pads the 32 bytes |sha256_hash_data| to 256 or 512 bytes according to the
- // PKCS#1 v1.5 standard; and returns whether *any* of the decrypted hashes
- // matches the padded hash data. In case of any error parsing the signatures
- // or the public key, returns false.
- static bool VerifySignature(const std::string& signature_proto,
- const std::string& pem_public_key,
- const brillo::Blob& sha256_hash_data);
-
- // Decrypts |sig_data| with the given |pem_public_key| and populates
- // |out_hash_data| with the decoded raw hash. |pem_public_key| should be a PEM
- // format RSA public key data. Returns true if successful, false otherwise.
- static bool GetRawHashFromSignature(const brillo::Blob& sig_data,
- const std::string& pem_public_key,
- brillo::Blob* out_hash_data);
-
// Pads a SHA256 hash so that it may be encrypted/signed with RSA2048 or
// RSA4096 using the PKCS#1 v1.5 scheme.
// hash should be a pointer to vector of exactly 256 bits. |rsa_size| must be
@@ -57,9 +43,46 @@
// Returns true on success, false otherwise.
static bool PadRSASHA256Hash(brillo::Blob* hash, size_t rsa_size);
+ // Parses the input as a PEM encoded public string. And creates a
+ // PayloadVerifier with that public key for signature verification.
+ static std::unique_ptr<PayloadVerifier> CreateInstance(
+ const std::string& pem_public_key);
+
+ // Extracts the public keys from the certificates contained in the input
+ // zip file. And creates a PayloadVerifier with these public keys.
+ static std::unique_ptr<PayloadVerifier> CreateInstanceFromZipPath(
+ const std::string& certificate_zip_path);
+
+ // Interprets |signature_proto| as a protocol buffer containing the
+ // |Signatures| message and decrypts each signature data using the stored
+ // public key. Pads the 32 bytes |sha256_hash_data| to 256 or 512 bytes
+ // according to the PKCS#1 v1.5 standard; and returns whether *any* of the
+ // decrypted hashes matches the padded hash data. In case of any error parsing
+ // the signatures, returns false.
+ bool VerifySignature(const std::string& signature_proto,
+ const brillo::Blob& sha256_hash_data) const;
+
+ // Verifies if |sig_data| is a raw signature of the hash |sha256_hash_data|.
+ // If PayloadVerifier is using RSA as the public key, further puts the
+ // decrypted data of |sig_data| into |decrypted_sig_data|.
+ bool VerifyRawSignature(const brillo::Blob& sig_data,
+ const brillo::Blob& sha256_hash_data,
+ brillo::Blob* decrypted_sig_data) const;
+
private:
- // This should never be constructed
- DISALLOW_IMPLICIT_CONSTRUCTORS(PayloadVerifier);
+ explicit PayloadVerifier(
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>&&
+ public_keys)
+ : public_keys_(std::move(public_keys)) {}
+
+ // Decrypts |sig_data| with the given |public_key| and populates
+ // |out_hash_data| with the decoded raw hash. Returns true if successful,
+ // false otherwise.
+ bool GetRawHashFromSignature(const brillo::Blob& sig_data,
+ const EVP_PKEY* public_key,
+ brillo::Blob* out_hash_data) const;
+
+ std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> public_keys_;
};
} // namespace chromeos_update_engine
diff --git a/payload_generator/deflate_utils.cc b/payload_generator/deflate_utils.cc
index a7a0503..01402dd 100644
--- a/payload_generator/deflate_utils.cc
+++ b/payload_generator/deflate_utils.cc
@@ -74,6 +74,15 @@
return false;
}
+bool IsRegularFile(const FilesystemInterface::File& file) {
+ // If inode is 0, then stat information is invalid for some psuedo files
+ if (file.file_stat.st_ino != 0 &&
+ (file.file_stat.st_mode & S_IFMT) == S_IFREG) {
+ return true;
+ }
+ return false;
+}
+
// Realigns subfiles |files| of a splitted file |file| into its correct
// positions. This can be used for squashfs, zip, apk, etc.
bool RealignSplittedFiles(const FilesystemInterface::File& file,
@@ -265,7 +274,9 @@
result_files->reserve(tmp_files.size());
for (auto& file : tmp_files) {
- if (IsSquashfsImage(part.path, file)) {
+ auto is_regular_file = IsRegularFile(file);
+
+ if (is_regular_file && IsSquashfsImage(part.path, file)) {
// Read the image into a file.
base::FilePath path;
TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&path));
@@ -295,7 +306,7 @@
}
}
- if (extract_deflates) {
+ if (is_regular_file && extract_deflates) {
// Search for deflates if the file is in zip or gzip format.
// .zvoice files may eventually move out of rootfs. If that happens,
// remove ".zvoice" (crbug.com/782918).
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 3cb891f..f035ff1 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -54,18 +54,15 @@
namespace {
void ParseSignatureSizes(const string& signature_sizes_flag,
- vector<int>* signature_sizes) {
+ vector<size_t>* signature_sizes) {
signature_sizes->clear();
vector<string> split_strings = base::SplitString(
signature_sizes_flag, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
for (const string& str : split_strings) {
- int size = 0;
- bool parsing_successful = base::StringToInt(str, &size);
+ size_t size = 0;
+ bool parsing_successful = base::StringToSizeT(str, &size);
LOG_IF(FATAL, !parsing_successful) << "Invalid signature size: " << str;
- LOG_IF(FATAL, size != 256 && size != 512)
- << "Only signature sizes of 256 or 512 bytes are supported.";
-
signature_sizes->push_back(size);
}
}
@@ -102,7 +99,7 @@
return true;
}
-void CalculateHashForSigning(const vector<int>& sizes,
+void CalculateHashForSigning(const vector<size_t>& sizes,
const string& out_hash_file,
const string& out_metadata_hash_file,
const string& in_file) {
@@ -138,6 +135,7 @@
void SignPayload(const string& in_file,
const string& out_file,
+ const vector<size_t>& signature_sizes,
const string& payload_signature_file,
const string& metadata_signature_file,
const string& out_metadata_size_file) {
@@ -151,6 +149,7 @@
SignatureFileFlagToBlobs(metadata_signature_file, &metadata_signatures);
uint64_t final_metadata_size;
CHECK(PayloadSigner::AddSignatureToPayload(in_file,
+ signature_sizes,
payload_signatures,
metadata_signatures,
out_file,
@@ -421,6 +420,13 @@
"",
"An info file specifying dynamic partition metadata. "
"Only allowed in major version 2 or newer.");
+ DEFINE_bool(disable_fec_computation,
+ false,
+ "Disables the fec data computation on device.");
+ DEFINE_string(
+ out_maximum_signature_size_file,
+ "",
+ "Path to the output maximum signature size given a private key.");
brillo::FlagHelper::Init(
argc,
@@ -442,8 +448,34 @@
// Initialize the Xz compressor.
XzCompressInit();
- vector<int> signature_sizes;
- ParseSignatureSizes(FLAGS_signature_size, &signature_sizes);
+ if (!FLAGS_out_maximum_signature_size_file.empty()) {
+ LOG_IF(FATAL, FLAGS_private_key.empty())
+ << "Private key is not provided when calculating the maximum signature "
+ "size.";
+
+ size_t maximum_signature_size;
+ if (!PayloadSigner::GetMaximumSignatureSize(FLAGS_private_key,
+ &maximum_signature_size)) {
+ LOG(ERROR) << "Failed to get the maximum signature size of private key: "
+ << FLAGS_private_key;
+ return 1;
+ }
+ // Write the size string to output file.
+ string signature_size_string = std::to_string(maximum_signature_size);
+ if (!utils::WriteFile(FLAGS_out_maximum_signature_size_file.c_str(),
+ signature_size_string.c_str(),
+ signature_size_string.size())) {
+ PLOG(ERROR) << "Failed to write the maximum signature size to "
+ << FLAGS_out_maximum_signature_size_file << ".";
+ return 1;
+ }
+ return 0;
+ }
+
+ vector<size_t> signature_sizes;
+ if (!FLAGS_signature_size.empty()) {
+ ParseSignatureSizes(FLAGS_signature_size, &signature_sizes);
+ }
if (!FLAGS_out_hash_file.empty() || !FLAGS_out_metadata_hash_file.empty()) {
CHECK(FLAGS_out_metadata_size_file.empty());
@@ -456,6 +488,7 @@
if (!FLAGS_payload_signature_file.empty()) {
SignPayload(FLAGS_in_file,
FLAGS_out_file,
+ signature_sizes,
FLAGS_payload_signature_file,
FLAGS_metadata_signature_file,
FLAGS_out_metadata_size_file);
@@ -527,6 +560,8 @@
<< "Partition name can't be empty, see --partition_names.";
payload_config.target.partitions.emplace_back(partition_names[i]);
payload_config.target.partitions.back().path = new_partitions[i];
+ payload_config.target.partitions.back().disable_fec_computation =
+ FLAGS_disable_fec_computation;
if (i < new_mapfiles.size())
payload_config.target.partitions.back().mapfile_path = new_mapfiles[i];
}
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index 648fe8b..2dd2626 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -160,6 +160,11 @@
}
}
}
+
+ bool snapshot_enabled = false;
+ store.GetBoolean("virtual_ab", &snapshot_enabled);
+ metadata->set_snapshot_enabled(snapshot_enabled);
+
dynamic_partition_metadata = std::move(metadata);
return true;
}
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 584ac7b..e90edde 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -116,6 +116,9 @@
PostInstallConfig postinstall;
VerityConfig verity;
+
+ // Enables the on device fec data computation by default.
+ bool disable_fec_computation = false;
};
// The ImageConfig struct describes a pair of binaries kernel and rootfs and the
diff --git a/payload_generator/payload_generation_config_android.cc b/payload_generator/payload_generation_config_android.cc
index 90c053f..d950092 100644
--- a/payload_generator/payload_generation_config_android.cc
+++ b/payload_generator/payload_generation_config_android.cc
@@ -63,11 +63,13 @@
part->verity.hash_tree_extent = ExtentForBytes(
hashtree.hash_block_size, hashtree.tree_offset, hashtree.tree_size);
- part->verity.fec_data_extent =
- ExtentForBytes(hashtree.data_block_size, 0, hashtree.fec_offset);
- part->verity.fec_extent = ExtentForBytes(
- hashtree.data_block_size, hashtree.fec_offset, hashtree.fec_size);
- part->verity.fec_roots = hashtree.fec_num_roots;
+ if (!part->disable_fec_computation) {
+ part->verity.fec_data_extent =
+ ExtentForBytes(hashtree.data_block_size, 0, hashtree.fec_offset);
+ part->verity.fec_extent = ExtentForBytes(
+ hashtree.data_block_size, hashtree.fec_offset, hashtree.fec_size);
+ part->verity.fec_roots = hashtree.fec_num_roots;
+ }
return true;
}
@@ -205,7 +207,8 @@
ExtentForRange(hash_start_block, tree_size / block_size);
}
fec_ecc_metadata ecc_data;
- if (fh.get_ecc_metadata(ecc_data) && ecc_data.valid) {
+ if (!part.disable_fec_computation && fh.get_ecc_metadata(ecc_data) &&
+ ecc_data.valid) {
TEST_AND_RETURN_FALSE(block_size == FEC_BLOCKSIZE);
part.verity.fec_data_extent = ExtentForRange(0, ecc_data.blocks);
part.verity.fec_extent =
diff --git a/payload_generator/payload_generation_config_android_unittest.cc b/payload_generator/payload_generation_config_android_unittest.cc
index 53378c2..44eaf55 100644
--- a/payload_generator/payload_generation_config_android_unittest.cc
+++ b/payload_generator/payload_generation_config_android_unittest.cc
@@ -160,6 +160,24 @@
EXPECT_EQ(2u, verity.fec_roots);
}
+TEST_F(PayloadGenerationConfigAndroidTest, LoadVerityConfigDisableFecTest) {
+ brillo::Blob part = GetAVBPartition();
+ test_utils::WriteFileVector(temp_file_.path(), part);
+ image_config_.partitions[0].disable_fec_computation = true;
+ EXPECT_TRUE(image_config_.LoadImageSize());
+ EXPECT_TRUE(image_config_.partitions[0].OpenFilesystem());
+ EXPECT_TRUE(image_config_.LoadVerityConfig());
+ const VerityConfig& verity = image_config_.partitions[0].verity;
+ EXPECT_FALSE(verity.IsEmpty());
+ EXPECT_EQ(ExtentForRange(0, 2), verity.hash_tree_data_extent);
+ EXPECT_EQ(ExtentForRange(2, 1), verity.hash_tree_extent);
+ EXPECT_EQ("sha1", verity.hash_tree_algorithm);
+ brillo::Blob salt(kHashTreeSalt, std::end(kHashTreeSalt));
+ EXPECT_EQ(salt, verity.hash_tree_salt);
+ EXPECT_EQ(0u, verity.fec_data_extent.num_blocks());
+ EXPECT_EQ(0u, verity.fec_extent.num_blocks());
+}
+
TEST_F(PayloadGenerationConfigAndroidTest,
LoadVerityConfigInvalidHashTreeTest) {
brillo::Blob part = GetAVBPartition();
diff --git a/payload_generator/payload_generation_config_unittest.cc b/payload_generator/payload_generation_config_unittest.cc
index 70a3df3..aca9655 100644
--- a/payload_generator/payload_generation_config_unittest.cc
+++ b/payload_generator/payload_generation_config_unittest.cc
@@ -59,7 +59,7 @@
ASSERT_TRUE(
store.LoadFromString("super_partition_groups=group_a group_b\n"
"group_a_size=3221225472\n"
- "group_a_partition_list=system product_services\n"
+ "group_a_partition_list=system system_ext\n"
"group_b_size=2147483648\n"
"group_b_partition_list=vendor\n"));
EXPECT_TRUE(image_config.LoadDynamicPartitionMetadata(store));
@@ -72,7 +72,7 @@
EXPECT_EQ(3221225472u, group_a.size());
ASSERT_EQ(2, group_a.partition_names_size());
EXPECT_EQ("system", group_a.partition_names(0));
- EXPECT_EQ("product_services", group_a.partition_names(1));
+ EXPECT_EQ("system_ext", group_a.partition_names(1));
const auto& group_b = image_config.dynamic_partition_metadata->groups(1);
EXPECT_EQ("group_b", group_b.name());
@@ -108,17 +108,17 @@
PartitionConfig system("system");
system.size = 2147483648u;
- PartitionConfig product_services("product_services");
- product_services.size = 1073741824u;
+ PartitionConfig system_ext("system_ext");
+ system_ext.size = 1073741824u;
image_config.partitions.push_back(std::move(system));
- image_config.partitions.push_back(std::move(product_services));
+ image_config.partitions.push_back(std::move(system_ext));
brillo::KeyValueStore store;
ASSERT_TRUE(
store.LoadFromString("super_partition_groups=foo\n"
"foo_size=3221225472\n"
- "foo_partition_list=system product_services\n"));
+ "foo_partition_list=system system_ext\n"));
EXPECT_TRUE(image_config.LoadDynamicPartitionMetadata(store));
EXPECT_NE(nullptr, image_config.dynamic_partition_metadata);
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
index 92313dc..72780b1 100644
--- a/payload_generator/payload_signer.cc
+++ b/payload_generator/payload_signer.cc
@@ -18,6 +18,7 @@
#include <endian.h>
+#include <memory>
#include <utility>
#include <base/logging.h>
@@ -46,23 +47,29 @@
namespace chromeos_update_engine {
namespace {
-
-// The payload verifier will check all the signatures included in the payload
-// regardless of the version field. Old version of the verifier require the
-// version field to be included and be 1.
-const uint32_t kSignatureMessageLegacyVersion = 1;
-
// Given raw |signatures|, packs them into a protobuf and serializes it into a
// string. Returns true on success, false otherwise.
bool ConvertSignaturesToProtobuf(const vector<brillo::Blob>& signatures,
+ const vector<size_t>& padded_signature_sizes,
string* out_serialized_signature) {
+ TEST_AND_RETURN_FALSE(signatures.size() == padded_signature_sizes.size());
// Pack it into a protobuf
Signatures out_message;
- for (const brillo::Blob& signature : signatures) {
+ for (size_t i = 0; i < signatures.size(); i++) {
+ const auto& signature = signatures[i];
+ const auto& padded_signature_size = padded_signature_sizes[i];
+ TEST_AND_RETURN_FALSE(padded_signature_size >= signature.size());
Signatures::Signature* sig_message = out_message.add_signatures();
- // Set all the signatures with the same version number.
- sig_message->set_version(kSignatureMessageLegacyVersion);
- sig_message->set_data(signature.data(), signature.size());
+ // Skip assigning the same version number because we don't need to be
+ // compatible with old major version 1 client anymore.
+
+ // TODO(Xunchang) don't need to set the unpadded_signature_size field for
+ // RSA key signed signatures.
+ sig_message->set_unpadded_signature_size(signature.size());
+ brillo::Blob padded_signature = signature;
+ padded_signature.insert(
+ padded_signature.end(), padded_signature_size - signature.size(), 0);
+ sig_message->set_data(padded_signature.data(), padded_signature.size());
}
// Serialize protobuf
@@ -203,8 +210,35 @@
return true;
}
+std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> CreatePrivateKeyFromPath(
+ const string& private_key_path) {
+ FILE* fprikey = fopen(private_key_path.c_str(), "rb");
+ if (!fprikey) {
+ PLOG(ERROR) << "Failed to read " << private_key_path;
+ return {nullptr, nullptr};
+ }
+
+ auto private_key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>(
+ PEM_read_PrivateKey(fprikey, nullptr, nullptr, nullptr), EVP_PKEY_free);
+ fclose(fprikey);
+ return private_key;
+}
+
} // namespace
+bool PayloadSigner::GetMaximumSignatureSize(const string& private_key_path,
+ size_t* signature_size) {
+ *signature_size = 0;
+ auto private_key = CreatePrivateKeyFromPath(private_key_path);
+ if (!private_key) {
+ LOG(ERROR) << "Failed to create private key from " << private_key_path;
+ return false;
+ }
+
+ *signature_size = EVP_PKEY_size(private_key.get());
+ return true;
+}
+
void PayloadSigner::AddSignatureToManifest(uint64_t signature_blob_offset,
uint64_t signature_blob_length,
bool add_dummy_op,
@@ -255,14 +289,18 @@
string public_key;
TEST_AND_RETURN_FALSE(utils::ReadFile(public_key_path, &public_key));
TEST_AND_RETURN_FALSE(payload_hash.size() == kSHA256Size);
+
+ auto payload_verifier = PayloadVerifier::CreateInstance(public_key);
+ TEST_AND_RETURN_FALSE(payload_verifier != nullptr);
+
TEST_AND_RETURN_FALSE(
- PayloadVerifier::VerifySignature(signature, public_key, payload_hash));
+ payload_verifier->VerifySignature(signature, payload_hash));
if (metadata_signature_size) {
signature.assign(payload.begin() + metadata_size,
payload.begin() + metadata_size + metadata_signature_size);
TEST_AND_RETURN_FALSE(metadata_hash.size() == kSHA256Size);
TEST_AND_RETURN_FALSE(
- PayloadVerifier::VerifySignature(signature, public_key, metadata_hash));
+ payload_verifier->VerifySignature(signature, metadata_hash));
}
return true;
}
@@ -278,29 +316,61 @@
// openssl rsautl -raw -sign -inkey |private_key_path|
// -in |padded_hash| -out |out_signature|
- FILE* fprikey = fopen(private_key_path.c_str(), "rb");
- TEST_AND_RETURN_FALSE(fprikey != nullptr);
- RSA* rsa = PEM_read_RSAPrivateKey(fprikey, nullptr, nullptr, nullptr);
- fclose(fprikey);
- TEST_AND_RETURN_FALSE(rsa != nullptr);
-
- brillo::Blob padded_hash = hash;
- PayloadVerifier::PadRSASHA256Hash(&padded_hash, RSA_size(rsa));
-
- brillo::Blob signature(RSA_size(rsa));
- ssize_t signature_size = RSA_private_encrypt(padded_hash.size(),
- padded_hash.data(),
- signature.data(),
- rsa,
- RSA_NO_PADDING);
- RSA_free(rsa);
- if (signature_size < 0) {
- LOG(ERROR) << "Signing hash failed: "
- << ERR_error_string(ERR_get_error(), nullptr);
+ auto private_key = CreatePrivateKeyFromPath(private_key_path);
+ if (!private_key) {
+ LOG(ERROR) << "Failed to create private key from " << private_key_path;
return false;
}
- TEST_AND_RETURN_FALSE(static_cast<size_t>(signature_size) ==
- signature.size());
+
+ int key_type = EVP_PKEY_id(private_key.get());
+ brillo::Blob signature;
+ if (key_type == EVP_PKEY_RSA) {
+ RSA* rsa = EVP_PKEY_get0_RSA(private_key.get());
+ TEST_AND_RETURN_FALSE(rsa != nullptr);
+
+ brillo::Blob padded_hash = hash;
+ PayloadVerifier::PadRSASHA256Hash(&padded_hash, RSA_size(rsa));
+
+ signature.resize(RSA_size(rsa));
+ ssize_t signature_size = RSA_private_encrypt(padded_hash.size(),
+ padded_hash.data(),
+ signature.data(),
+ rsa,
+ RSA_NO_PADDING);
+
+ if (signature_size < 0) {
+ LOG(ERROR) << "Signing hash failed: "
+ << ERR_error_string(ERR_get_error(), nullptr);
+ return false;
+ }
+ TEST_AND_RETURN_FALSE(static_cast<size_t>(signature_size) ==
+ signature.size());
+ } else if (key_type == EVP_PKEY_EC) {
+ EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(private_key.get());
+ TEST_AND_RETURN_FALSE(ec_key != nullptr);
+
+ signature.resize(ECDSA_size(ec_key));
+ unsigned int signature_size;
+ if (ECDSA_sign(0,
+ hash.data(),
+ hash.size(),
+ signature.data(),
+ &signature_size,
+ ec_key) != 1) {
+ LOG(ERROR) << "Signing hash failed: "
+ << ERR_error_string(ERR_get_error(), nullptr);
+ return false;
+ }
+
+ // NIST P-256
+ LOG(ERROR) << "signature max size " << signature.size() << " size "
+ << signature_size;
+ TEST_AND_RETURN_FALSE(signature.size() >= signature_size);
+ signature.resize(signature_size);
+ } else {
+ LOG(ERROR) << "key_type " << key_type << " isn't supported for signing";
+ return false;
+ }
out_signature->swap(signature);
return true;
}
@@ -309,13 +379,19 @@
const vector<string>& private_key_paths,
string* out_serialized_signature) {
vector<brillo::Blob> signatures;
+ vector<size_t> padded_signature_sizes;
for (const string& path : private_key_paths) {
brillo::Blob signature;
TEST_AND_RETURN_FALSE(SignHash(hash_data, path, &signature));
signatures.push_back(signature);
+
+ size_t padded_signature_size;
+ TEST_AND_RETURN_FALSE(
+ GetMaximumSignatureSize(path, &padded_signature_size));
+ padded_signature_sizes.push_back(padded_signature_size);
}
- TEST_AND_RETURN_FALSE(
- ConvertSignaturesToProtobuf(signatures, out_serialized_signature));
+ TEST_AND_RETURN_FALSE(ConvertSignaturesToProtobuf(
+ signatures, padded_signature_sizes, out_serialized_signature));
return true;
}
@@ -352,7 +428,7 @@
}
bool PayloadSigner::HashPayloadForSigning(const string& payload_path,
- const vector<int>& signature_sizes,
+ const vector<size_t>& signature_sizes,
brillo::Blob* out_payload_hash_data,
brillo::Blob* out_metadata_hash) {
// Create a signature blob with signatures filled with 0.
@@ -362,7 +438,8 @@
signatures.emplace_back(signature_size, 0);
}
string signature;
- TEST_AND_RETURN_FALSE(ConvertSignaturesToProtobuf(signatures, &signature));
+ TEST_AND_RETURN_FALSE(
+ ConvertSignaturesToProtobuf(signatures, signature_sizes, &signature));
brillo::Blob payload;
uint64_t metadata_size, signatures_offset;
@@ -386,6 +463,7 @@
bool PayloadSigner::AddSignatureToPayload(
const string& payload_path,
+ const vector<size_t>& padded_signature_sizes,
const vector<brillo::Blob>& payload_signatures,
const vector<brillo::Blob>& metadata_signatures,
const string& signed_payload_path,
@@ -394,11 +472,11 @@
// Loads the payload and adds the signature op to it.
string payload_signature, metadata_signature;
- TEST_AND_RETURN_FALSE(
- ConvertSignaturesToProtobuf(payload_signatures, &payload_signature));
+ TEST_AND_RETURN_FALSE(ConvertSignaturesToProtobuf(
+ payload_signatures, padded_signature_sizes, &payload_signature));
if (!metadata_signatures.empty()) {
- TEST_AND_RETURN_FALSE(
- ConvertSignaturesToProtobuf(metadata_signatures, &metadata_signature));
+ TEST_AND_RETURN_FALSE(ConvertSignaturesToProtobuf(
+ metadata_signatures, padded_signature_sizes, &metadata_signature));
}
brillo::Blob payload;
uint64_t signatures_offset;
diff --git a/payload_generator/payload_signer.h b/payload_generator/payload_signer.h
index 7854e12..bd1e32f 100644
--- a/payload_generator/payload_signer.h
+++ b/payload_generator/payload_signer.h
@@ -91,7 +91,7 @@
//
// The changes to payload are not preserved or written to disk.
static bool HashPayloadForSigning(const std::string& payload_path,
- const std::vector<int>& signature_sizes,
+ const std::vector<size_t>& signature_sizes,
brillo::Blob* out_payload_hash_data,
brillo::Blob* out_metadata_hash);
@@ -105,6 +105,7 @@
// otherwise.
static bool AddSignatureToPayload(
const std::string& payload_path,
+ const std::vector<size_t>& padded_signature_sizes,
const std::vector<brillo::Blob>& payload_signatures,
const std::vector<brillo::Blob>& metadata_signatures,
const std::string& signed_payload_path,
@@ -122,6 +123,13 @@
static bool ExtractPayloadProperties(const std::string& payload_path,
brillo::KeyValueStore* properties);
+ // This function calculates the maximum size, in bytes, of a signature signed
+ // by private_key_path. For an RSA key, this returns the number of bytes
+ // needed to represent the modulus. For an EC key, this returns the maximum
+ // size of a DER-encoded ECDSA signature.
+ static bool GetMaximumSignatureSize(const std::string& private_key_path,
+ size_t* signature_size);
+
private:
// This should never be constructed
DISALLOW_IMPLICIT_CONSTRUCTORS(PayloadSigner);
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
index 75fc694..bf7100b 100644
--- a/payload_generator/payload_signer_unittest.cc
+++ b/payload_generator/payload_signer_unittest.cc
@@ -46,6 +46,8 @@
const char* kUnittestPublicKey2Path = "unittest_key2.pub.pem";
const char* kUnittestPrivateKeyRSA4096Path = "unittest_key_RSA4096.pem";
const char* kUnittestPublicKeyRSA4096Path = "unittest_key_RSA4096.pub.pem";
+const char* kUnittestPrivateKeyECPath = "unittest_key_EC.pem";
+const char* kUnittestPublicKeyECPath = "unittest_key_EC.pub.pem";
// Some data and its corresponding hash and signature:
const char kDataToSign[] = "This is some data to sign.";
@@ -115,7 +117,6 @@
EXPECT_TRUE(signatures.ParseFromString(signature));
EXPECT_EQ(1, signatures.signatures_size());
const Signatures::Signature& sig = signatures.signatures(0);
- EXPECT_EQ(1U, sig.version());
const string& sig_data = sig.data();
ASSERT_EQ(arraysize(kDataSignature), sig_data.size());
for (size_t i = 0; i < arraysize(kDataSignature); i++) {
@@ -128,22 +129,20 @@
SignSampleData(&signature,
{GetBuildArtifactsPath(kUnittestPrivateKeyPath),
GetBuildArtifactsPath(kUnittestPrivateKey2Path),
- GetBuildArtifactsPath(kUnittestPrivateKeyRSA4096Path)});
+ GetBuildArtifactsPath(kUnittestPrivateKeyRSA4096Path),
+ GetBuildArtifactsPath(kUnittestPrivateKeyECPath)});
// Either public key should pass the verification.
- string public_key;
- EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKeyPath),
- &public_key));
- EXPECT_TRUE(
- PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
- EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKey2Path),
- &public_key));
- EXPECT_TRUE(
- PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
- EXPECT_TRUE(utils::ReadFile(
- GetBuildArtifactsPath(kUnittestPublicKeyRSA4096Path), &public_key));
- EXPECT_TRUE(
- PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
+ for (const auto& path : {kUnittestPublicKeyPath,
+ kUnittestPublicKey2Path,
+ kUnittestPublicKeyRSA4096Path,
+ kUnittestPublicKeyECPath}) {
+ string public_key;
+ EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(path), &public_key));
+ auto payload_verifier = PayloadVerifier::CreateInstance(public_key);
+ EXPECT_TRUE(payload_verifier != nullptr);
+ EXPECT_TRUE(payload_verifier->VerifySignature(signature, hash_data_));
+ }
}
TEST_F(PayloadSignerTest, VerifySignatureTest) {
@@ -153,13 +152,17 @@
string public_key;
EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKeyPath),
&public_key));
- EXPECT_TRUE(
- PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
+ auto payload_verifier = PayloadVerifier::CreateInstance(public_key);
+ EXPECT_TRUE(payload_verifier != nullptr);
+ EXPECT_TRUE(payload_verifier->VerifySignature(signature, hash_data_));
+
// Passing the invalid key should fail the verification.
+ public_key.clear();
EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKey2Path),
&public_key));
- EXPECT_TRUE(
- PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
+ payload_verifier = PayloadVerifier::CreateInstance(public_key);
+ EXPECT_TRUE(payload_verifier != nullptr);
+ EXPECT_FALSE(payload_verifier->VerifySignature(signature, hash_data_));
}
TEST_F(PayloadSignerTest, SkipMetadataSignatureTest) {
@@ -171,7 +174,7 @@
uint64_t metadata_size;
EXPECT_TRUE(payload.WritePayload(
payload_file.path(), "/dev/null", "", &metadata_size));
- const vector<int> sizes = {256};
+ const vector<size_t> sizes = {256};
brillo::Blob unsigned_payload_hash, unsigned_metadata_hash;
EXPECT_TRUE(PayloadSigner::HashPayloadForSigning(payload_file.path(),
sizes,
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index f535185..d9c18ff 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -28,12 +28,16 @@
# check verify a payload using paycheck (static testing)
#
# Generate command arguments:
-# --payload generated unsigned payload output file
-# --source_image if defined, generate a delta payload from the specified
-# image to the target_image
-# --target_image the target image that should be sent to clients
-# --metadata_size_file if defined, generate a file containing the size of the
-# payload metadata in bytes to the specified file
+# --payload generated unsigned payload output file
+# --source_image if defined, generate a delta payload from the
+# specified image to the target_image
+# --target_image the target image that should be sent to clients
+# --metadata_size_file if defined, generate a file containing the size
+# of the ayload metadata in bytes to the specified
+# file
+# --disable_fec_computation Disable the on device fec data computation for
+# incremental update. This feature is enabled by
+# default
#
# Hash command arguments:
# --unsigned_payload the input unsigned payload to generate the hash from
@@ -182,6 +186,9 @@
"Optional: The maximum unix timestamp of the OS allowed to apply this \
payload, should be set to a number higher than the build timestamp of the \
system running on the device, 0 if not specified."
+ DEFINE_string disable_fec_computation "" \
+ "Optional: Disables the on device fec data computation for incremental \
+update. This feature is enabled by default."
fi
if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
@@ -274,7 +281,7 @@
local option_key="$2"
local default_value="${3:-}"
local value
- if value=$(look "${option_key}=" "${file_txt}" | tail -n 1); then
+ if value=$(grep "^${option_key}=" "${file_txt}" | tail -n 1); then
if value=$(echo "${value}" | cut -f 2- -d "=" | grep -E "^[0-9]+$"); then
echo "${value}"
return
@@ -327,6 +334,25 @@
trap cleanup_on_error INT TERM ERR
trap cleanup_on_exit EXIT
+# extract_file <zip_file> <entry_name> <destination>
+#
+# Extracts |entry_name| from |zip_file| to |destination|.
+extract_file() {
+ local zip_file="$1"
+ local entry_name="$2"
+ local destination="$3"
+
+ # unzip -p won't report error upon ENOSPC. Therefore, create a temp directory
+ # as the destination of the unzip, and move the file to the intended
+ # destination.
+ local output_directory=$(
+ mktemp --directory --tmpdir="${FLAGS_work_dir}" "TEMP.XXXXXX")
+ unzip "${zip_file}" "${entry_name}" -d "${output_directory}" ||
+ { rm -rf "${output_directory}"; die "Failed to extract ${entry_name}"; }
+
+ mv "${output_directory}/${entry_name}" "${destination}"
+ rm -rf "${output_directory}"
+}
# extract_image <image> <partitions_array> [partitions_order]
#
@@ -417,7 +443,7 @@
fi
done
[[ -n "${path_in_zip}" ]] || die "Failed to find ${part}.img"
- unzip -p "${image}" "${path_in_zip}/${part}.img" >"${part_file}"
+ extract_file "${image}" "${path_in_zip}/${part}.img" "${part_file}"
# If the partition is stored as an Android sparse image file, we need to
# convert them to a raw image for the update.
@@ -431,8 +457,9 @@
fi
# Extract the .map file (if one is available).
- unzip -p "${image}" "${path_in_zip}/${part}.map" >"${part_map_file}" \
- 2>/dev/null || true
+ if unzip -l "${image}" "${path_in_zip}/${part}.map" > /dev/null; then
+ extract_file "${image}" "${path_in_zip}/${part}.map" "${part_map_file}"
+ fi
# delta_generator only supports images multiple of 4 KiB. For target images
# we pad the data with zeros if needed, but for source images we truncate
@@ -466,7 +493,8 @@
local ab_partitions_list
ab_partitions_list=$(create_tempfile "ab_partitions_list.XXXXXX")
CLEANUP_FILES+=("${ab_partitions_list}")
- if unzip -p "${image}" "META/ab_partitions.txt" >"${ab_partitions_list}"; then
+ if unzip -l "${image}" "META/ab_partitions.txt" > /dev/null; then
+ extract_file "${image}" "META/ab_partitions.txt" "${ab_partitions_list}"
if grep -v -E '^[a-zA-Z0-9_-]*$' "${ab_partitions_list}" >&2; then
die "Invalid partition names found in the partition list."
fi
@@ -491,8 +519,9 @@
# Source image
local ue_config=$(create_tempfile "ue_config.XXXXXX")
CLEANUP_FILES+=("${ue_config}")
- if ! unzip -p "${image}" "META/update_engine_config.txt" \
- >"${ue_config}"; then
+ if unzip -l "${image}" "META/update_engine_config.txt" > /dev/null; then
+ extract_file "${image}" "META/update_engine_config.txt" "${ue_config}"
+ else
warn "No update_engine_config.txt found. Assuming pre-release image, \
using payload minor version 2"
fi
@@ -513,14 +542,16 @@
# Target image
local postinstall_config=$(create_tempfile "postinstall_config.XXXXXX")
CLEANUP_FILES+=("${postinstall_config}")
- if unzip -p "${image}" "META/postinstall_config.txt" \
- >"${postinstall_config}"; then
+ if unzip -l "${image}" "META/postinstall_config.txt" > /dev/null; then
+ extract_file "${image}" "META/postinstall_config.txt" \
+ "${postinstall_config}"
POSTINSTALL_CONFIG_FILE="${postinstall_config}"
fi
local dynamic_partitions_info=$(create_tempfile "dynamic_partitions_info.XXXXXX")
CLEANUP_FILES+=("${dynamic_partitions_info}")
- if unzip -p "${image}" "META/dynamic_partitions_info.txt" \
- >"${dynamic_partitions_info}"; then
+ if unzip -l "${image}" "META/dynamic_partitions_info.txt" > /dev/null; then
+ extract_file "${image}" "META/dynamic_partitions_info.txt" \
+ "${dynamic_partitions_info}"
DYNAMIC_PARTITION_INFO_FILE="${dynamic_partitions_info}"
fi
fi
@@ -632,6 +663,10 @@
if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
fi
+ if [[ -n "${FLAGS_disable_fec_computation}" ]]; then
+ GENERATOR_ARGS+=(
+ --disable_fec_computation="${FLAGS_disable_fec_computation}" )
+ fi
fi
if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then
diff --git a/scripts/update_payload/update_metadata_pb2.py b/scripts/update_payload/update_metadata_pb2.py
index 7f1648b..cb8f4c2 100644
--- a/scripts/update_payload/update_metadata_pb2.py
+++ b/scripts/update_payload/update_metadata_pb2.py
@@ -1,19 +1,27 @@
+# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: update_metadata.proto
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
-from google.protobuf import descriptor_pb2
+from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
+_sym_db = _symbol_database.Default()
+
DESCRIPTOR = _descriptor.FileDescriptor(
name='update_metadata.proto',
package='chromeos_update_engine',
- serialized_pb='\n\x15update_metadata.proto\x12\x16\x63hromeos_update_engine\"1\n\x06\x45xtent\x12\x13\n\x0bstart_block\x18\x01 \x01(\x04\x12\x12\n\nnum_blocks\x18\x02 \x01(\x04\"z\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1a*\n\tSignature\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"+\n\rPartitionInfo\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"w\n\tImageInfo\x12\r\n\x05\x62oard\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x15\n\rbuild_channel\x18\x05 \x01(\t\x12\x15\n\rbuild_version\x18\x06 \x01(\t\"\xe6\x03\n\x10InstallOperation\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.chromeos_update_engine.InstallOperation.Type\x12\x13\n\x0b\x64\x61ta_offset\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x61ta_length\x18\x03 \x01(\x04\x12\x33\n\x0bsrc_extents\x18\x04 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\nsrc_length\x18\x05 \x01(\x04\x12\x33\n\x0b\x64st_extents\x18\x06 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\ndst_length\x18\x07 \x01(\x04\x12\x18\n\x10\x64\x61ta_sha256_hash\x18\x08 \x01(\x0c\x12\x17\n\x0fsrc_sha256_hash\x18\t \x01(\x0c\"\xa5\x01\n\x04Type\x12\x0b\n\x07REPLACE\x10\x00\x12\x0e\n\nREPLACE_BZ\x10\x01\x12\x08\n\x04MOVE\x10\x02\x12\n\n\x06\x42SDIFF\x10\x03\x12\x0f\n\x0bSOURCE_COPY\x10\x04\x12\x11\n\rSOURCE_BSDIFF\x10\x05\x12\x0e\n\nREPLACE_XZ\x10\x08\x12\x08\n\x04ZERO\x10\x06\x12\x0b\n\x07\x44ISCARD\x10\x07\x12\x11\n\rBROTLI_BSDIFF\x10\n\x12\x0c\n\x08PUFFDIFF\x10\t\"\xd7\x05\n\x0fPartitionUpdate\x12\x16\n\x0epartition_name\x18\x01 \x02(\t\x12\x17\n\x0frun_postinstall\x18\x02 \x01(\x08\x12\x18\n\x10postinstall_path\x18\x03 \x01(\t\x12\x17\n\x0f\x66ilesystem_type\x18\x04 \x01(\t\x12M\n\x17new_partition_signature\x18\x05 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x12\x41\n\x12old_partition_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x41\n\x12new_partition_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12<\n\noperations\x18\x08 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x1c\n\x14postinstall_optional\x18\t \x01(\x08\x12=\n\x15hash_tree_data_extent\x18\n \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x38\n\x10hash_tree_extent\x18\x0b \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x1b\n\x13hash_tree_algorithm\x18\x0c \x01(\t\x12\x16\n\x0ehash_tree_salt\x18\r \x01(\x0c\x12\x37\n\x0f\x66\x65\x63_data_extent\x18\x0e \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\nfec_extent\x18\x0f \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x14\n\tfec_roots\x18\x10 \x01(\r:\x01\x32\"L\n\x15\x44ynamicPartitionGroup\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fpartition_names\x18\x03 \x03(\t\"Y\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\"\xb1\x06\n\x14\x44\x65ltaArchiveManifest\x12\x44\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12K\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x18\n\nblock_size\x18\x03 \x01(\r:\x04\x34\x30\x39\x36\x12\x19\n\x11signatures_offset\x18\x04 \x01(\x04\x12\x17\n\x0fsignatures_size\x18\x05 \x01(\x04\x12>\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x39\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x39\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x18\n\rminor_version\x18\x0c \x01(\r:\x01\x30\x12;\n\npartitions\x18\r \x03(\x0b\x32\'.chromeos_update_engine.PartitionUpdate\x12\x15\n\rmax_timestamp\x18\x0e \x01(\x03\x12T\n\x1a\x64ynamic_partition_metadata\x18\x0f \x01(\x0b\x32\x30.chromeos_update_engine.DynamicPartitionMetadataB\x02H\x03')
+ syntax='proto2',
+ serialized_options=_b('H\003'),
+ serialized_pb=_b('\n\x15update_metadata.proto\x12\x16\x63hromeos_update_engine\"1\n\x06\x45xtent\x12\x13\n\x0bstart_block\x18\x01 \x01(\x04\x12\x12\n\nnum_blocks\x18\x02 \x01(\x04\"z\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1a*\n\tSignature\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"+\n\rPartitionInfo\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"w\n\tImageInfo\x12\r\n\x05\x62oard\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x15\n\rbuild_channel\x18\x05 \x01(\t\x12\x15\n\rbuild_version\x18\x06 \x01(\t\"\xee\x03\n\x10InstallOperation\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.chromeos_update_engine.InstallOperation.Type\x12\x13\n\x0b\x64\x61ta_offset\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x61ta_length\x18\x03 \x01(\x04\x12\x33\n\x0bsrc_extents\x18\x04 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\nsrc_length\x18\x05 \x01(\x04\x12\x33\n\x0b\x64st_extents\x18\x06 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\ndst_length\x18\x07 \x01(\x04\x12\x18\n\x10\x64\x61ta_sha256_hash\x18\x08 \x01(\x0c\x12\x17\n\x0fsrc_sha256_hash\x18\t \x01(\x0c\"\xad\x01\n\x04Type\x12\x0b\n\x07REPLACE\x10\x00\x12\x0e\n\nREPLACE_BZ\x10\x01\x12\x0c\n\x04MOVE\x10\x02\x1a\x02\x08\x01\x12\x0e\n\x06\x42SDIFF\x10\x03\x1a\x02\x08\x01\x12\x0f\n\x0bSOURCE_COPY\x10\x04\x12\x11\n\rSOURCE_BSDIFF\x10\x05\x12\x0e\n\nREPLACE_XZ\x10\x08\x12\x08\n\x04ZERO\x10\x06\x12\x0b\n\x07\x44ISCARD\x10\x07\x12\x11\n\rBROTLI_BSDIFF\x10\n\x12\x0c\n\x08PUFFDIFF\x10\t\"\xd7\x05\n\x0fPartitionUpdate\x12\x16\n\x0epartition_name\x18\x01 \x02(\t\x12\x17\n\x0frun_postinstall\x18\x02 \x01(\x08\x12\x18\n\x10postinstall_path\x18\x03 \x01(\t\x12\x17\n\x0f\x66ilesystem_type\x18\x04 \x01(\t\x12M\n\x17new_partition_signature\x18\x05 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x12\x41\n\x12old_partition_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x41\n\x12new_partition_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12<\n\noperations\x18\x08 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x1c\n\x14postinstall_optional\x18\t \x01(\x08\x12=\n\x15hash_tree_data_extent\x18\n \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x38\n\x10hash_tree_extent\x18\x0b \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x1b\n\x13hash_tree_algorithm\x18\x0c \x01(\t\x12\x16\n\x0ehash_tree_salt\x18\r \x01(\x0c\x12\x37\n\x0f\x66\x65\x63_data_extent\x18\x0e \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\nfec_extent\x18\x0f \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x14\n\tfec_roots\x18\x10 \x01(\r:\x01\x32\"L\n\x15\x44ynamicPartitionGroup\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fpartition_names\x18\x03 \x03(\t\"s\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\x12\x18\n\x10snapshot_enabled\x18\x02 \x01(\x08\"\xb1\x06\n\x14\x44\x65ltaArchiveManifest\x12\x44\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12K\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x18\n\nblock_size\x18\x03 \x01(\r:\x04\x34\x30\x39\x36\x12\x19\n\x11signatures_offset\x18\x04 \x01(\x04\x12\x17\n\x0fsignatures_size\x18\x05 \x01(\x04\x12>\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12>\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x39\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x39\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfo\x12\x18\n\rminor_version\x18\x0c \x01(\r:\x01\x30\x12;\n\npartitions\x18\r \x03(\x0b\x32\'.chromeos_update_engine.PartitionUpdate\x12\x15\n\rmax_timestamp\x18\x0e \x01(\x03\x12T\n\x1a\x64ynamic_partition_metadata\x18\x0f \x01(\x0b\x32\x30.chromeos_update_engine.DynamicPartitionMetadataB\x02H\x03')
+)
@@ -25,54 +33,55 @@
values=[
_descriptor.EnumValueDescriptor(
name='REPLACE', index=0, number=0,
- options=None,
+ serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='REPLACE_BZ', index=1, number=1,
- options=None,
+ serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MOVE', index=2, number=2,
- options=None,
+ serialized_options=_b('\010\001'),
type=None),
_descriptor.EnumValueDescriptor(
name='BSDIFF', index=3, number=3,
- options=None,
+ serialized_options=_b('\010\001'),
type=None),
_descriptor.EnumValueDescriptor(
name='SOURCE_COPY', index=4, number=4,
- options=None,
+ serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='SOURCE_BSDIFF', index=5, number=5,
- options=None,
+ serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='REPLACE_XZ', index=6, number=8,
- options=None,
+ serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ZERO', index=7, number=6,
- options=None,
+ serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='DISCARD', index=8, number=7,
- options=None,
+ serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='BROTLI_BSDIFF', index=9, number=10,
- options=None,
+ serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PUFFDIFF', index=10, number=9,
- options=None,
+ serialized_options=None,
type=None),
],
containing_type=None,
- options=None,
+ serialized_options=None,
serialized_start=712,
- serialized_end=877,
+ serialized_end=885,
)
+_sym_db.RegisterEnumDescriptor(_INSTALLOPERATION_TYPE)
_EXTENT = _descriptor.Descriptor(
@@ -88,23 +97,26 @@
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='num_blocks', full_name='chromeos_update_engine.Extent.num_blocks', index=1,
number=2, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
+ oneofs=[
+ ],
serialized_start=49,
serialized_end=98,
)
@@ -123,23 +135,26 @@
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='data', full_name='chromeos_update_engine.Signatures.Signature.data', index=1,
number=2, type=12, cpp_type=9, label=1,
- has_default_value=False, default_value="",
+ has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
+ oneofs=[
+ ],
serialized_start=180,
serialized_end=222,
)
@@ -157,16 +172,19 @@
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[_SIGNATURES_SIGNATURE, ],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
+ oneofs=[
+ ],
serialized_start=100,
serialized_end=222,
)
@@ -185,23 +203,26 @@
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='hash', full_name='chromeos_update_engine.PartitionInfo.hash', index=1,
number=2, type=12, cpp_type=9, label=1,
- has_default_value=False, default_value="",
+ has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
+ oneofs=[
+ ],
serialized_start=224,
serialized_end=267,
)
@@ -217,54 +238,57 @@
_descriptor.FieldDescriptor(
name='board', full_name='chromeos_update_engine.ImageInfo.board', index=0,
number=1, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='key', full_name='chromeos_update_engine.ImageInfo.key', index=1,
number=2, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='channel', full_name='chromeos_update_engine.ImageInfo.channel', index=2,
number=3, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='version', full_name='chromeos_update_engine.ImageInfo.version', index=3,
number=4, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='build_channel', full_name='chromeos_update_engine.ImageInfo.build_channel', index=4,
number=5, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='build_version', full_name='chromeos_update_engine.ImageInfo.build_version', index=5,
number=6, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
+ oneofs=[
+ ],
serialized_start=269,
serialized_end=388,
)
@@ -283,63 +307,63 @@
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='data_offset', full_name='chromeos_update_engine.InstallOperation.data_offset', index=1,
number=2, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='data_length', full_name='chromeos_update_engine.InstallOperation.data_length', index=2,
number=3, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='src_extents', full_name='chromeos_update_engine.InstallOperation.src_extents', index=3,
number=4, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='src_length', full_name='chromeos_update_engine.InstallOperation.src_length', index=4,
number=5, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='dst_extents', full_name='chromeos_update_engine.InstallOperation.dst_extents', index=5,
number=6, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='dst_length', full_name='chromeos_update_engine.InstallOperation.dst_length', index=6,
number=7, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='data_sha256_hash', full_name='chromeos_update_engine.InstallOperation.data_sha256_hash', index=7,
number=8, type=12, cpp_type=9, label=1,
- has_default_value=False, default_value="",
+ has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='src_sha256_hash', full_name='chromeos_update_engine.InstallOperation.src_sha256_hash', index=8,
number=9, type=12, cpp_type=9, label=1,
- has_default_value=False, default_value="",
+ has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
@@ -347,11 +371,14 @@
enum_types=[
_INSTALLOPERATION_TYPE,
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
+ oneofs=[
+ ],
serialized_start=391,
- serialized_end=877,
+ serialized_end=885,
)
@@ -365,126 +392,129 @@
_descriptor.FieldDescriptor(
name='partition_name', full_name='chromeos_update_engine.PartitionUpdate.partition_name', index=0,
number=1, type=9, cpp_type=9, label=2,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='run_postinstall', full_name='chromeos_update_engine.PartitionUpdate.run_postinstall', index=1,
number=2, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='postinstall_path', full_name='chromeos_update_engine.PartitionUpdate.postinstall_path', index=2,
number=3, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='filesystem_type', full_name='chromeos_update_engine.PartitionUpdate.filesystem_type', index=3,
number=4, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='new_partition_signature', full_name='chromeos_update_engine.PartitionUpdate.new_partition_signature', index=4,
number=5, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='old_partition_info', full_name='chromeos_update_engine.PartitionUpdate.old_partition_info', index=5,
number=6, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='new_partition_info', full_name='chromeos_update_engine.PartitionUpdate.new_partition_info', index=6,
number=7, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='operations', full_name='chromeos_update_engine.PartitionUpdate.operations', index=7,
number=8, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='postinstall_optional', full_name='chromeos_update_engine.PartitionUpdate.postinstall_optional', index=8,
number=9, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='hash_tree_data_extent', full_name='chromeos_update_engine.PartitionUpdate.hash_tree_data_extent', index=9,
number=10, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='hash_tree_extent', full_name='chromeos_update_engine.PartitionUpdate.hash_tree_extent', index=10,
number=11, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='hash_tree_algorithm', full_name='chromeos_update_engine.PartitionUpdate.hash_tree_algorithm', index=11,
number=12, type=9, cpp_type=9, label=1,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='hash_tree_salt', full_name='chromeos_update_engine.PartitionUpdate.hash_tree_salt', index=12,
number=13, type=12, cpp_type=9, label=1,
- has_default_value=False, default_value="",
+ has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='fec_data_extent', full_name='chromeos_update_engine.PartitionUpdate.fec_data_extent', index=13,
number=14, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='fec_extent', full_name='chromeos_update_engine.PartitionUpdate.fec_extent', index=14,
number=15, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='fec_roots', full_name='chromeos_update_engine.PartitionUpdate.fec_roots', index=15,
number=16, type=13, cpp_type=3, label=1,
has_default_value=True, default_value=2,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
- serialized_start=880,
- serialized_end=1607,
+ oneofs=[
+ ],
+ serialized_start=888,
+ serialized_end=1615,
)
@@ -498,35 +528,38 @@
_descriptor.FieldDescriptor(
name='name', full_name='chromeos_update_engine.DynamicPartitionGroup.name', index=0,
number=1, type=9, cpp_type=9, label=2,
- has_default_value=False, default_value=unicode("", "utf-8"),
+ has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='size', full_name='chromeos_update_engine.DynamicPartitionGroup.size', index=1,
number=2, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='partition_names', full_name='chromeos_update_engine.DynamicPartitionGroup.partition_names', index=2,
number=3, type=9, cpp_type=9, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
- serialized_start=1609,
- serialized_end=1685,
+ oneofs=[
+ ],
+ serialized_start=1617,
+ serialized_end=1693,
)
@@ -543,18 +576,28 @@
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='snapshot_enabled', full_name='chromeos_update_engine.DynamicPartitionMetadata.snapshot_enabled', index=1,
+ number=2, type=8, cpp_type=7, label=1,
+ has_default_value=False, default_value=False,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
- serialized_start=1687,
- serialized_end=1776,
+ oneofs=[
+ ],
+ serialized_start=1695,
+ serialized_end=1810,
)
@@ -571,124 +614,127 @@
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='kernel_install_operations', full_name='chromeos_update_engine.DeltaArchiveManifest.kernel_install_operations', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='block_size', full_name='chromeos_update_engine.DeltaArchiveManifest.block_size', index=2,
number=3, type=13, cpp_type=3, label=1,
has_default_value=True, default_value=4096,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='signatures_offset', full_name='chromeos_update_engine.DeltaArchiveManifest.signatures_offset', index=3,
number=4, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='signatures_size', full_name='chromeos_update_engine.DeltaArchiveManifest.signatures_size', index=4,
number=5, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='old_kernel_info', full_name='chromeos_update_engine.DeltaArchiveManifest.old_kernel_info', index=5,
number=6, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='new_kernel_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_kernel_info', index=6,
number=7, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='old_rootfs_info', full_name='chromeos_update_engine.DeltaArchiveManifest.old_rootfs_info', index=7,
number=8, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='new_rootfs_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_rootfs_info', index=8,
number=9, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='old_image_info', full_name='chromeos_update_engine.DeltaArchiveManifest.old_image_info', index=9,
number=10, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='new_image_info', full_name='chromeos_update_engine.DeltaArchiveManifest.new_image_info', index=10,
number=11, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='minor_version', full_name='chromeos_update_engine.DeltaArchiveManifest.minor_version', index=11,
number=12, type=13, cpp_type=3, label=1,
has_default_value=True, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='partitions', full_name='chromeos_update_engine.DeltaArchiveManifest.partitions', index=12,
number=13, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='max_timestamp', full_name='chromeos_update_engine.DeltaArchiveManifest.max_timestamp', index=13,
number=14, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='dynamic_partition_metadata', full_name='chromeos_update_engine.DeltaArchiveManifest.dynamic_partition_metadata', index=14,
number=15, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
- options=None),
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
- options=None,
+ serialized_options=None,
is_extendable=False,
+ syntax='proto2',
extension_ranges=[],
- serialized_start=1779,
- serialized_end=2596,
+ oneofs=[
+ ],
+ serialized_start=1813,
+ serialized_end=2630,
)
-_SIGNATURES_SIGNATURE.containing_type = _SIGNATURES;
+_SIGNATURES_SIGNATURE.containing_type = _SIGNATURES
_SIGNATURES.fields_by_name['signatures'].message_type = _SIGNATURES_SIGNATURE
_INSTALLOPERATION.fields_by_name['type'].enum_type = _INSTALLOPERATION_TYPE
_INSTALLOPERATION.fields_by_name['src_extents'].message_type = _EXTENT
_INSTALLOPERATION.fields_by_name['dst_extents'].message_type = _EXTENT
-_INSTALLOPERATION_TYPE.containing_type = _INSTALLOPERATION;
+_INSTALLOPERATION_TYPE.containing_type = _INSTALLOPERATION
_PARTITIONUPDATE.fields_by_name['new_partition_signature'].message_type = _SIGNATURES_SIGNATURE
_PARTITIONUPDATE.fields_by_name['old_partition_info'].message_type = _PARTITIONINFO
_PARTITIONUPDATE.fields_by_name['new_partition_info'].message_type = _PARTITIONINFO
@@ -717,68 +763,81 @@
DESCRIPTOR.message_types_by_name['DynamicPartitionGroup'] = _DYNAMICPARTITIONGROUP
DESCRIPTOR.message_types_by_name['DynamicPartitionMetadata'] = _DYNAMICPARTITIONMETADATA
DESCRIPTOR.message_types_by_name['DeltaArchiveManifest'] = _DELTAARCHIVEMANIFEST
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
-class Extent(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _EXTENT
-
+Extent = _reflection.GeneratedProtocolMessageType('Extent', (_message.Message,), {
+ 'DESCRIPTOR' : _EXTENT,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.Extent)
+ })
+_sym_db.RegisterMessage(Extent)
-class Signatures(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
+Signatures = _reflection.GeneratedProtocolMessageType('Signatures', (_message.Message,), {
- class Signature(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _SIGNATURES_SIGNATURE
-
+ 'Signature' : _reflection.GeneratedProtocolMessageType('Signature', (_message.Message,), {
+ 'DESCRIPTOR' : _SIGNATURES_SIGNATURE,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.Signatures.Signature)
- DESCRIPTOR = _SIGNATURES
-
+ })
+ ,
+ 'DESCRIPTOR' : _SIGNATURES,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.Signatures)
+ })
+_sym_db.RegisterMessage(Signatures)
+_sym_db.RegisterMessage(Signatures.Signature)
-class PartitionInfo(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _PARTITIONINFO
-
+PartitionInfo = _reflection.GeneratedProtocolMessageType('PartitionInfo', (_message.Message,), {
+ 'DESCRIPTOR' : _PARTITIONINFO,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.PartitionInfo)
+ })
+_sym_db.RegisterMessage(PartitionInfo)
-class ImageInfo(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _IMAGEINFO
-
+ImageInfo = _reflection.GeneratedProtocolMessageType('ImageInfo', (_message.Message,), {
+ 'DESCRIPTOR' : _IMAGEINFO,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.ImageInfo)
+ })
+_sym_db.RegisterMessage(ImageInfo)
-class InstallOperation(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _INSTALLOPERATION
-
+InstallOperation = _reflection.GeneratedProtocolMessageType('InstallOperation', (_message.Message,), {
+ 'DESCRIPTOR' : _INSTALLOPERATION,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.InstallOperation)
+ })
+_sym_db.RegisterMessage(InstallOperation)
-class PartitionUpdate(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _PARTITIONUPDATE
-
+PartitionUpdate = _reflection.GeneratedProtocolMessageType('PartitionUpdate', (_message.Message,), {
+ 'DESCRIPTOR' : _PARTITIONUPDATE,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.PartitionUpdate)
+ })
+_sym_db.RegisterMessage(PartitionUpdate)
-class DynamicPartitionGroup(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _DYNAMICPARTITIONGROUP
-
+DynamicPartitionGroup = _reflection.GeneratedProtocolMessageType('DynamicPartitionGroup', (_message.Message,), {
+ 'DESCRIPTOR' : _DYNAMICPARTITIONGROUP,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.DynamicPartitionGroup)
+ })
+_sym_db.RegisterMessage(DynamicPartitionGroup)
-class DynamicPartitionMetadata(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _DYNAMICPARTITIONMETADATA
-
+DynamicPartitionMetadata = _reflection.GeneratedProtocolMessageType('DynamicPartitionMetadata', (_message.Message,), {
+ 'DESCRIPTOR' : _DYNAMICPARTITIONMETADATA,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.DynamicPartitionMetadata)
+ })
+_sym_db.RegisterMessage(DynamicPartitionMetadata)
-class DeltaArchiveManifest(_message.Message):
- __metaclass__ = _reflection.GeneratedProtocolMessageType
- DESCRIPTOR = _DELTAARCHIVEMANIFEST
-
+DeltaArchiveManifest = _reflection.GeneratedProtocolMessageType('DeltaArchiveManifest', (_message.Message,), {
+ 'DESCRIPTOR' : _DELTAARCHIVEMANIFEST,
+ '__module__' : 'update_metadata_pb2'
# @@protoc_insertion_point(class_scope:chromeos_update_engine.DeltaArchiveManifest)
+ })
+_sym_db.RegisterMessage(DeltaArchiveManifest)
-DESCRIPTOR.has_options = True
-DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), 'H\003')
+DESCRIPTOR._options = None
+_INSTALLOPERATION_TYPE.values_by_name["MOVE"]._options = None
+_INSTALLOPERATION_TYPE.values_by_name["BSDIFF"]._options = None
# @@protoc_insertion_point(module_scope)
diff --git a/service_delegate_android_interface.h b/service_delegate_android_interface.h
index 5267bb0..6bd75b6 100644
--- a/service_delegate_android_interface.h
+++ b/service_delegate_android_interface.h
@@ -47,6 +47,13 @@
const std::vector<std::string>& key_value_pair_headers,
brillo::ErrorPtr* error) = 0;
+ virtual bool ApplyPayload(
+ int fd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<std::string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) = 0;
+
// Suspend an ongoing update. Returns true if there was an update ongoing and
// it was suspended. In case of failure, it returns false and sets |error|
// accordingly.
diff --git a/test_config.xml b/test_config.xml
new file mode 100644
index 0000000..2639e7f
--- /dev/null
+++ b/test_config.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<configuration description="Config to run update_engine_unittests on device">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="update_engine_unittests->/data/local/tmp/update_engine_unittests" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <!-- The following rules avoid test runner from calling the following helper executables
+ directly as gtests. -->
+ <option name="file-exclusion-filter-regex" value=".*/delta_generator$" />
+ <option name="file-exclusion-filter-regex" value=".*/test_http_server$" />
+ <option name="file-exclusion-filter-regex" value=".*/test_subprocess$" />
+ <option name="module-name" value="update_engine_unittests" />
+ </test>
+</configuration>
diff --git a/unittest_key_EC.pem b/unittest_key_EC.pem
new file mode 100644
index 0000000..9e65a68
--- /dev/null
+++ b/unittest_key_EC.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGaguGj8Yb1KkqKHd
+ISblUsjtOCbzAuVpX81i02sm8FWhRANCAARBnuotwKOsuvjH6iwTDhOAi7Q5pLWz
+xDkZjg2pcfbfi9FFTvLYETas7B2W6fx9PUezUmHTFTDV2JZuMYYFdZOw
+-----END PRIVATE KEY-----
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 1cc8505..5bffc42 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -22,6 +22,7 @@
#include <utility>
#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
#include <base/bind.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
@@ -38,6 +39,7 @@
#include "update_engine/metrics_reporter_interface.h"
#include "update_engine/metrics_utils.h"
#include "update_engine/network_selector.h"
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
#include "update_engine/payload_consumer/delta_performer.h"
#include "update_engine/payload_consumer/download_action.h"
#include "update_engine/payload_consumer/file_descriptor.h"
@@ -45,6 +47,7 @@
#include "update_engine/payload_consumer/filesystem_verifier_action.h"
#include "update_engine/payload_consumer/payload_constants.h"
#include "update_engine/payload_consumer/payload_metadata.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
#include "update_engine/payload_consumer/postinstall_runner_action.h"
#include "update_engine/update_boot_flags_action.h"
#include "update_engine/update_status_utils.h"
@@ -55,6 +58,7 @@
#include "update_engine/libcurl_http_fetcher.h"
#endif
+using android::base::unique_fd;
using base::Bind;
using base::Time;
using base::TimeDelta;
@@ -288,6 +292,19 @@
return true;
}
+bool UpdateAttempterAndroid::ApplyPayload(
+ int fd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const vector<string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) {
+ payload_fd_.reset(dup(fd));
+ const string payload_url = "fd://" + std::to_string(payload_fd_.get());
+
+ return ApplyPayload(
+ payload_url, payload_offset, payload_size, key_value_pair_headers, error);
+}
+
bool UpdateAttempterAndroid::SuspendUpdate(brillo::ErrorPtr* error) {
if (!processor_->IsRunning())
return LogAndSetError(error, FROM_HERE, "No ongoing update to suspend.");
@@ -395,12 +412,16 @@
}
fd->Close();
- string public_key;
- if (!utils::ReadFile(constants::kUpdatePayloadPublicKeyPath, &public_key)) {
- return LogAndSetError(error, FROM_HERE, "Failed to read public key.");
+ auto payload_verifier = PayloadVerifier::CreateInstanceFromZipPath(
+ constants::kUpdateCertificatesPath);
+ if (!payload_verifier) {
+ return LogAndSetError(error,
+ FROM_HERE,
+ "Failed to create the payload verifier from " +
+ std::string(constants::kUpdateCertificatesPath));
}
- errorcode =
- payload_metadata.ValidateMetadataSignature(metadata, "", public_key);
+ errorcode = payload_metadata.ValidateMetadataSignature(
+ metadata, "", *payload_verifier);
if (errorcode != ErrorCode::kSuccess) {
return LogAndSetError(error,
FROM_HERE,
@@ -583,6 +604,7 @@
(error_code == ErrorCode::kSuccess ? UpdateStatus::UPDATED_NEED_REBOOT
: UpdateStatus::IDLE);
SetStatusAndNotify(new_status);
+ payload_fd_.reset();
// The network id is only applicable to one download attempt and once it's
// done the network id should not be re-used anymore.
@@ -734,11 +756,15 @@
total_bytes_downloaded;
int download_overhead_percentage = 0;
- if (current_bytes_downloaded > 0) {
+ if (total_bytes_downloaded >= payload_size) {
+ CHECK_GT(payload_size, 0);
download_overhead_percentage =
- (total_bytes_downloaded - current_bytes_downloaded) * 100ull /
- current_bytes_downloaded;
+ (total_bytes_downloaded - payload_size) * 100ull / payload_size;
+ } else {
+ LOG(WARNING) << "Downloaded bytes " << total_bytes_downloaded
+ << " is smaller than the payload size " << payload_size;
}
+
metrics_reporter_->ReportSuccessfulUpdateMetrics(
static_cast<int>(attempt_number),
0, // update abandoned count
diff --git a/update_attempter_android.h b/update_attempter_android.h
index c4710ad..7e1949d 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -23,6 +23,7 @@
#include <string>
#include <vector>
+#include <android-base/unique_fd.h>
#include <base/time/time.h>
#include "update_engine/client_library/include/update_engine/update_status.h"
@@ -65,6 +66,11 @@
int64_t payload_size,
const std::vector<std::string>& key_value_pair_headers,
brillo::ErrorPtr* error) override;
+ bool ApplyPayload(int fd,
+ int64_t payload_offset,
+ int64_t payload_size,
+ const std::vector<std::string>& key_value_pair_headers,
+ brillo::ErrorPtr* error) override;
bool SuspendUpdate(brillo::ErrorPtr* error) override;
bool ResumeUpdate(brillo::ErrorPtr* error) override;
bool CancelUpdate(brillo::ErrorPtr* error) override;
@@ -191,6 +197,8 @@
std::unique_ptr<MetricsReporterInterface> metrics_reporter_;
+ ::android::base::unique_fd payload_fd_;
+
DISALLOW_COPY_AND_ASSIGN(UpdateAttempterAndroid);
};
diff --git a/update_attempter_android_unittest.cc b/update_attempter_android_unittest.cc
index 3be0b7e..721b735 100644
--- a/update_attempter_android_unittest.cc
+++ b/update_attempter_android_unittest.cc
@@ -18,6 +18,7 @@
#include <memory>
#include <string>
+#include <utility>
#include <android-base/properties.h>
#include <base/time/time.h>
@@ -57,6 +58,11 @@
update_attempter_android_.status_ = status;
}
+ void AddPayload(InstallPlan::Payload&& payload) {
+ update_attempter_android_.install_plan_.payloads.push_back(
+ std::move(payload));
+ }
+
UpdateAttempterAndroid update_attempter_android_{
&daemon_state_, &prefs_, &boot_control_, &hardware_};
@@ -143,9 +149,13 @@
.Times(1);
EXPECT_CALL(*metrics_reporter_,
ReportSuccessfulUpdateMetrics(
- 2, 0, _, _, _, _, duration, duration_uptime, 3, _))
+ 2, 0, _, 50, _, _, duration, duration_uptime, 3, _))
.Times(1);
+ // Adds a payload of 50 bytes to the InstallPlan.
+ InstallPlan::Payload payload;
+ payload.size = 50;
+ AddPayload(std::move(payload));
SetUpdateStatus(UpdateStatus::UPDATE_AVAILABLE);
update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
@@ -179,15 +189,20 @@
_,
_,
_,
- _,
+ 50,
test_utils::DownloadSourceMatcher(total_bytes),
- 125,
+ 80,
_,
_,
_,
_))
.Times(1);
+ // Adds a payload of 50 bytes to the InstallPlan.
+ InstallPlan::Payload payload;
+ payload.size = 50;
+ AddPayload(std::move(payload));
+
// The first update fails after receiving 50 bytes in total.
update_attempter_android_.BytesReceived(30, 50, 200);
update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kError);
@@ -199,7 +214,7 @@
metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, &prefs_));
// The second update succeeds after receiving 40 bytes, which leads to a
- // overhead of 50 / 40 = 125%.
+ // overhead of (90 - 50) / 50 = 80%.
update_attempter_android_.BytesReceived(40, 40, 50);
update_attempter_android_.ProcessingDone(nullptr, ErrorCode::kSuccess);
// Both prefs should be cleared.
diff --git a/update_engine.rc b/update_engine.rc
index 90ca4c6..b9f80fc 100644
--- a/update_engine.rc
+++ b/update_engine.rc
@@ -2,7 +2,7 @@
class late_start
user root
group root system wakelock inet cache media_rw
- writepid /dev/cpuset/system-background/tasks
+ writepid /dev/cpuset/system-background/tasks /dev/blkio/background/tasks
disabled
on property:ro.boot.slot_suffix=*
diff --git a/update_metadata.proto b/update_metadata.proto
index 7e8e7d4..9bc0d8a 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -126,8 +126,17 @@
message Signatures {
message Signature {
- optional uint32 version = 1;
+ optional uint32 version = 1 [deprecated = true];
optional bytes data = 2;
+
+ // The DER encoded signature size of EC keys is nondeterministic for
+ // different input of sha256 hash. However, we need the size of the
+ // serialized signatures protobuf string to be fixed before signing;
+ // because this size is part of the content to be signed. Therefore, we
+ // always pad the signature data to the maximum possible signature size of
+ // a given key. And the payload verifier will truncate the signature to
+ // its correct size based on the value of |unpadded_signature_size|.
+ optional fixed32 unpadded_signature_size = 3;
}
repeated Signature signatures = 1;
}
@@ -302,6 +311,12 @@
// - If an updatable group is in the manifest but not on the device, the group
// is added to the device.
repeated DynamicPartitionGroup groups = 1;
+
+ // Whether dynamic partitions have snapshots during the update. If this is
+ // set to true, the update_engine daemon creates snapshots for all dynamic
+ // partitions if possible. If this is unset, the update_engine daemon MUST
+ // NOT create snapshots for dynamic partitions.
+ optional bool snapshot_enabled = 2;
}
message DeltaArchiveManifest {