Add dynamic_partition_metadata to update manifest
At build time, it will be filled with BOARD_SUPER_PARTITION_GROUPS,
BOARD_*_SIZE, and BOARD_*_PARTITION_LIST.
Only the information from the new target_files package is used. The
META/dynamic_partition_info.txt from old target_files is ignored.
Test: builds and manual OTA
Bug: 117182932
Change-Id: I02ce99caaf7d01cec1470f7262c45490c15dfcb7
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 8240518..a7ec6da 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -404,6 +404,10 @@
DEFINE_string(new_postinstall_config_file, "",
"A config file specifying postinstall related metadata. "
"Only allowed in major version 2 or newer.");
+ DEFINE_string(dynamic_partition_info_file,
+ "",
+ "An info file specifying dynamic partition metadata. "
+ "Only allowed in major version 2 or newer.");
brillo::FlagHelper::Init(argc, argv,
"Generates a payload to provide to ChromeOS' update_engine.\n\n"
@@ -553,6 +557,16 @@
}
CHECK(payload_config.target.LoadImageSize());
+ if (!FLAGS_dynamic_partition_info_file.empty()) {
+ LOG_IF(FATAL, FLAGS_major_version == kChromeOSMajorPayloadVersion)
+ << "Dynamic partition info is only allowed in major version 2 or "
+ "newer.";
+ brillo::KeyValueStore store;
+ CHECK(store.Load(base::FilePath(FLAGS_dynamic_partition_info_file)));
+ CHECK(payload_config.target.LoadDynamicPartitionMetadata(store));
+ CHECK(payload_config.target.ValidateDynamicPartitionMetadata());
+ }
+
CHECK(!FLAGS_out_file.empty());
// Ignore failures. These are optional arguments.
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index 13cf489..8f4399e 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -75,6 +75,13 @@
manifest_.set_block_size(config.block_size);
manifest_.set_max_timestamp(config.max_timestamp);
+
+ if (major_version_ == kBrilloMajorPayloadVersion) {
+ if (config.target.dynamic_partition_metadata != nullptr)
+ *(manifest_.mutable_dynamic_partition_metadata()) =
+ *(config.target.dynamic_partition_metadata);
+ }
+
return true;
}
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index c49fdb5..e7d8ae5 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -16,7 +16,13 @@
#include "update_engine/payload_generator/payload_generation_config.h"
+#include <algorithm>
+#include <map>
+#include <utility>
+
#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <brillo/strings/string_utils.h>
#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/delta_performer.h"
@@ -27,6 +33,8 @@
#include "update_engine/payload_generator/mapfile_filesystem.h"
#include "update_engine/payload_generator/raw_filesystem.h"
+using std::string;
+
namespace chromeos_update_engine {
bool PostInstallConfig::IsEmpty() const {
@@ -121,6 +129,74 @@
return true;
}
+bool ImageConfig::LoadDynamicPartitionMetadata(
+ const brillo::KeyValueStore& store) {
+ auto metadata = std::make_unique<DynamicPartitionMetadata>();
+ string buf;
+ if (!store.GetString("super_partition_groups", &buf)) {
+ LOG(ERROR) << "Dynamic partition info missing super_partition_groups.";
+ return false;
+ }
+ auto group_names = brillo::string_utils::Split(buf, " ");
+ for (const auto& group_name : group_names) {
+ DynamicPartitionGroup* group = metadata->add_groups();
+ group->set_name(group_name);
+ if (!store.GetString(group_name + "_size", &buf)) {
+ LOG(ERROR) << "Missing " << group_name + "_size.";
+ return false;
+ }
+
+ uint64_t max_size;
+ if (!base::StringToUint64(buf, &max_size)) {
+ LOG(ERROR) << group_name << "_size=" << buf << " is not an integer.";
+ return false;
+ }
+ group->set_size(max_size);
+
+ if (store.GetString(group_name + "_partition_list", &buf)) {
+ auto partition_names = brillo::string_utils::Split(buf, " ");
+ for (const auto& partition_name : partition_names) {
+ group->add_partition_names()->assign(partition_name);
+ }
+ }
+ }
+ dynamic_partition_metadata = std::move(metadata);
+ return true;
+}
+
+bool ImageConfig::ValidateDynamicPartitionMetadata() const {
+ if (dynamic_partition_metadata == nullptr) {
+ LOG(ERROR) << "dynamic_partition_metadata is not loaded.";
+ return false;
+ }
+
+ for (const auto& group : dynamic_partition_metadata->groups()) {
+ uint64_t sum_size = 0;
+ for (const auto& partition_name : group.partition_names()) {
+ auto partition_config = std::find_if(partitions.begin(),
+ partitions.end(),
+ [&partition_name](const auto& e) {
+ return e.name == partition_name;
+ });
+
+ if (partition_config == partitions.end()) {
+ LOG(ERROR) << "Cannot find partition " << partition_name
+ << " which is in " << group.name() << "_partition_list";
+ return false;
+ }
+ sum_size += partition_config->size;
+ }
+
+ if (sum_size > group.size()) {
+ LOG(ERROR) << "Sum of sizes in " << group.name() << "_partition_list is "
+ << sum_size << ", which is greater than " << group.name()
+ << "_size (" << group.size() << ")";
+ return false;
+ }
+ }
+ return true;
+}
+
bool ImageConfig::ImageInfoIsEmpty() const {
return image_info.board().empty()
&& image_info.key().empty()
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 38e7b10..2153ab0 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -137,6 +137,12 @@
// Load verity config by parsing the partition images.
bool LoadVerityConfig();
+ // Load dynamic partition info from a key value store.
+ bool LoadDynamicPartitionMetadata(const brillo::KeyValueStore& store);
+
+ // Validate |dynamic_partition_metadata| against |partitions|.
+ bool ValidateDynamicPartitionMetadata() const;
+
// Returns whether the |image_info| field is empty.
bool ImageInfoIsEmpty() const;
@@ -146,6 +152,9 @@
// The updated partitions.
std::vector<PartitionConfig> partitions;
+
+ // The super partition metadata.
+ std::unique_ptr<DynamicPartitionMetadata> dynamic_partition_metadata;
};
struct PayloadVersion {
diff --git a/payload_generator/payload_generation_config_unittest.cc b/payload_generator/payload_generation_config_unittest.cc
index 3545056..70a3df3 100644
--- a/payload_generator/payload_generation_config_unittest.cc
+++ b/payload_generator/payload_generation_config_unittest.cc
@@ -16,6 +16,8 @@
#include "update_engine/payload_generator/payload_generation_config.h"
+#include <utility>
+
#include <gtest/gtest.h>
namespace chromeos_update_engine {
@@ -51,4 +53,93 @@
EXPECT_TRUE(image_config.partitions[0].postinstall.IsEmpty());
}
+TEST_F(PayloadGenerationConfigTest, LoadDynamicPartitionMetadataTest) {
+ ImageConfig image_config;
+ brillo::KeyValueStore store;
+ 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_b_size=2147483648\n"
+ "group_b_partition_list=vendor\n"));
+ EXPECT_TRUE(image_config.LoadDynamicPartitionMetadata(store));
+ ASSERT_NE(nullptr, image_config.dynamic_partition_metadata);
+
+ ASSERT_EQ(2, image_config.dynamic_partition_metadata->groups_size());
+
+ const auto& group_a = image_config.dynamic_partition_metadata->groups(0);
+ EXPECT_EQ("group_a", group_a.name());
+ 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));
+
+ const auto& group_b = image_config.dynamic_partition_metadata->groups(1);
+ EXPECT_EQ("group_b", group_b.name());
+ EXPECT_EQ(2147483648u, group_b.size());
+ ASSERT_EQ(1, group_b.partition_names_size());
+ EXPECT_EQ("vendor", group_b.partition_names(0));
+}
+
+TEST_F(PayloadGenerationConfigTest,
+ LoadDynamicPartitionMetadataMissingSizeTest) {
+ ImageConfig image_config;
+ brillo::KeyValueStore store;
+ ASSERT_TRUE(
+ store.LoadFromString("super_partition_groups=foo\n"
+ "foo_partition_list=baz\n"));
+ EXPECT_FALSE(image_config.LoadDynamicPartitionMetadata(store));
+ EXPECT_EQ(nullptr, image_config.dynamic_partition_metadata);
+}
+
+TEST_F(PayloadGenerationConfigTest, LoadDynamicPartitionMetadataBadSizeTest) {
+ ImageConfig image_config;
+ brillo::KeyValueStore store;
+ ASSERT_TRUE(
+ store.LoadFromString("super_partition_groups=foo\n"
+ "foo_size=bar\n"
+ "foo_partition_list=baz\n"));
+ EXPECT_FALSE(image_config.LoadDynamicPartitionMetadata(store));
+ EXPECT_EQ(nullptr, image_config.dynamic_partition_metadata);
+}
+
+TEST_F(PayloadGenerationConfigTest, ValidateDynamicPartitionMetadata) {
+ ImageConfig image_config;
+
+ PartitionConfig system("system");
+ system.size = 2147483648u;
+ PartitionConfig product_services("product_services");
+ product_services.size = 1073741824u;
+
+ image_config.partitions.push_back(std::move(system));
+ image_config.partitions.push_back(std::move(product_services));
+
+ brillo::KeyValueStore store;
+ ASSERT_TRUE(
+ store.LoadFromString("super_partition_groups=foo\n"
+ "foo_size=3221225472\n"
+ "foo_partition_list=system product_services\n"));
+ EXPECT_TRUE(image_config.LoadDynamicPartitionMetadata(store));
+ EXPECT_NE(nullptr, image_config.dynamic_partition_metadata);
+
+ EXPECT_TRUE(image_config.ValidateDynamicPartitionMetadata());
+}
+
+TEST_F(PayloadGenerationConfigTest, ValidateDynamicPartitionMetadataTooBig) {
+ ImageConfig image_config;
+
+ PartitionConfig system("system");
+ system.size = 4294967296u;
+ image_config.partitions.push_back(std::move(system));
+
+ brillo::KeyValueStore store;
+ ASSERT_TRUE(
+ store.LoadFromString("super_partition_groups=foo\n"
+ "foo_size=3221225472\n"
+ "foo_partition_list=system\n"));
+ EXPECT_TRUE(image_config.LoadDynamicPartitionMetadata(store));
+ EXPECT_NE(nullptr, image_config.dynamic_partition_metadata);
+
+ EXPECT_FALSE(image_config.ValidateDynamicPartitionMetadata());
+}
} // namespace chromeos_update_engine
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 47ac830..e66edae 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -258,6 +258,9 @@
# Path to the postinstall config file in target image if exists.
POSTINSTALL_CONFIG_FILE=""
+# Path to the dynamic partition info file in target image if exists.
+DYNAMIC_PARTITION_INFO_FILE=""
+
# read_option_int <file.txt> <option_key> [default_value]
#
# Reads the unsigned integer value associated with |option_key| in a key=value
@@ -512,6 +515,12 @@
>"${postinstall_config}"; then
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
+ DYNAMIC_PARTITION_INFO_FILE="${dynamic_partitions_info}"
+ fi
fi
local part
@@ -636,6 +645,12 @@
)
fi
+ if [[ -n "{DYNAMIC_PARTITION_INFO_FILE}" ]]; then
+ GENERATOR_ARGS+=(
+ --dynamic_partition_info_file="${DYNAMIC_PARTITION_INFO_FILE}"
+ )
+ fi
+
echo "Running delta_generator with args: ${GENERATOR_ARGS[@]}"
"${GENERATOR}" "${GENERATOR_ARGS[@]}"
diff --git a/update_metadata.proto b/update_metadata.proto
index f90ec3c..42e7654 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -273,6 +273,29 @@
optional uint32 fec_roots = 16 [default = 2];
}
+message DynamicPartitionGroup {
+ // Name of the group.
+ required string name = 1;
+
+ // Maximum size of the group. The sum of sizes of all partitions in the group
+ // must not exceed the maximum size of the group.
+ optional uint64 size = 2;
+
+ // A list of partitions that belong to the group.
+ repeated string partition_names = 3;
+}
+
+// Metadata related to all dynamic partitions.
+message DynamicPartitionMetadata {
+ // All updateable groups present in |partitions| of this DeltaArchiveManifest.
+ // - If an updatable group is on the device but not in the manifest, it is
+ // not updated. Hence, the group will not be resized, and partitions cannot
+ // be added to or removed from the group.
+ // - If an updatable group is in the manifest but not on the device, the group
+ // is added to the device.
+ repeated DynamicPartitionGroup groups = 1;
+}
+
message DeltaArchiveManifest {
// Only present in major version = 1. List of install operations for the
// kernel and rootfs partitions. For major version = 2 see the |partitions|
@@ -317,4 +340,7 @@
// The maximum timestamp of the OS allowed to apply this payload.
// Can be used to prevent downgrading the OS.
optional int64 max_timestamp = 14;
+
+ // Metadata related to all dynamic partitions.
+ optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
}