Support updateable groups.
Adds updateable group support to OTA.
* DeltaPerformer combines partition sizes with
dynamic_partition_metadata to
BootControlInterface::PartitionMetadata.
* BootControlAndroid::InitPartitionMetadata:
* Copy all groups / partitions from source metadata slot
* Remove all groups / partitions mentioned in the manifest (of the
target slot)
* Re-add all groups / partitions mentioned in the manifest.
* BootControlAndroid::InitPartitionMetadata can check
the incoming PartitionMetadata to see if a partition is dynamic
or not. The guessing logic is completely removed.
* Because a partition is removed then re-added, there is no need
for preserving the entry with size 0 to indicate that a partition
is removed. When update_engine sees a partition in a group "foo" on
the device, but manifest contains group "foo" without the partition,
it removes the partition.
* Hence, Removing a partition does NOT require keeping the entry (i.e.
RemovePartition is used instead of ShrinkPartition(0) ). This makes
retrofitting dynamic partitions on older devices easier.
The following is now allowed:
- Adding / removing / resizing partitions
- Adding / resizing groups
It is not allowed to remove a group, but a group can always be resized
to zero to deprecate it.
Test: update_engine_unittests
Bug: 117182932
Change-Id: I39d77f1d1d1fc52fc245f3de699635e6a429015e
diff --git a/boot_control_android.cc b/boot_control_android.cc
index 61711d2..8eafdce 100644
--- a/boot_control_android.cc
+++ b/boot_control_android.cc
@@ -18,9 +18,11 @@
#include <memory>
#include <utility>
+#include <vector>
#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>
@@ -31,9 +33,7 @@
using std::string;
using android::dm::DmDeviceState;
-using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
-using android::fs_mgr::UpdatePartitionTable;
using android::hardware::hidl_string;
using android::hardware::Return;
using android::hardware::boot::V1_0::BoolResult;
@@ -225,117 +225,103 @@
namespace {
-// Resize |partition_name|_|slot| to the given |size|.
-bool ResizePartition(MetadataBuilder* builder,
- const string& target_partition_name,
- uint64_t size) {
- Partition* partition = builder->FindPartition(target_partition_name);
- if (partition == nullptr) {
- LOG(ERROR) << "Cannot find " << target_partition_name << " in metadata.";
+bool InitPartitionMetadataInternal(
+ DynamicPartitionControlInterface* dynamic_control,
+ const string& super_device,
+ Slot source_slot,
+ Slot target_slot,
+ const string& target_suffix,
+ const PartitionMetadata& partition_metadata) {
+ auto builder =
+ dynamic_control->LoadMetadataBuilder(super_device, source_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;
}
- uint64_t old_size = partition->size();
- const string action = "resize " + target_partition_name + " in super (" +
- std::to_string(old_size) + " -> " +
- std::to_string(size) + " bytes)";
- if (!builder->ResizePartition(partition, size)) {
- LOG(ERROR) << "Cannot " << action << "; see previous log messages.";
+ 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;
+ }
+
+ if (total_size > (builder->AllocatableSpace() / 2)) {
+ LOG(ERROR)
+ << "The maximum size of all groups with suffix " << target_suffix
+ << " (" << total_size
+ << ") has exceeded half of allocatable space for dynamic partitions "
+ << (builder->AllocatableSpace() / 2) << ".";
return false;
}
- if (partition->size() != size) {
- LOG(ERROR) << "Cannot " << action
- << "; value is misaligned and partition should have been "
- << partition->size();
- 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 parition_name_suffix = partition.name + target_suffix;
+ Partition* p = builder->AddPartition(
+ parition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
+ if (!p) {
+ LOG(ERROR) << "Cannot add partition " << parition_name_suffix
+ << " to group " << group_name_suffix;
+ return false;
+ }
+ if (!builder->ResizePartition(p, partition.size)) {
+ LOG(ERROR) << "Cannot resize partition " << parition_name_suffix
+ << " to size " << partition.size << ". Not enough space?";
+ return false;
+ }
+ LOG(INFO) << "Added partition " << parition_name_suffix << " to group "
+ << group_name_suffix << " with size " << partition.size;
+ }
}
- LOG(INFO) << "Successfully " << action;
-
- return true;
+ return dynamic_control->StoreMetadata(
+ super_device, builder.get(), target_slot);
}
-bool ResizePartitions(DynamicPartitionControlInterface* dynamic_control,
- const string& super_device,
- Slot target_slot,
- const string& target_suffix,
- const PartitionMetadata& logical_sizes,
- MetadataBuilder* builder) {
- // Delete all extents to ensure that each partition has enough space to
- // grow.
- for (const auto& pair : logical_sizes) {
- const string target_partition_name = pair.first + target_suffix;
- if (builder->FindPartition(target_partition_name) == nullptr) {
- // Use constant GUID because it is unused.
- LOG(INFO) << "Adding partition " << target_partition_name << " to slot "
- << BootControlInterface::SlotName(target_slot) << " in "
- << super_device;
- if (builder->AddPartition(target_partition_name,
- LP_PARTITION_ATTR_READONLY) == nullptr) {
- LOG(ERROR) << "Cannot add partition " << target_partition_name;
+// Unmap all partitions, and remap partitions.
+bool Remap(DynamicPartitionControlInterface* dynamic_control,
+ const string& super_device,
+ Slot target_slot,
+ 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;
+ }
+ if (partition.size == 0) {
+ continue;
+ }
+ string map_path;
+ if (!dynamic_control->MapPartitionOnDeviceMapper(
+ super_device,
+ partition.name + target_suffix,
+ target_slot,
+ &map_path)) {
return false;
}
}
- if (!ResizePartition(builder, pair.first + target_suffix, 0 /* size */)) {
- return false;
- }
- }
-
- for (const auto& pair : logical_sizes) {
- if (!ResizePartition(builder, pair.first + target_suffix, pair.second)) {
- LOG(ERROR) << "Not enough space?";
- return false;
- }
- }
-
- if (!dynamic_control->StoreMetadata(super_device, builder, target_slot)) {
- return false;
- }
- return true;
-}
-
-// Assume upgrading from slot A to B. A partition foo is considered dynamic
-// iff one of the following:
-// 1. foo_a exists as a dynamic partition (so it should continue to be a
-// dynamic partition)
-// 2. foo_b does not exist as a static partition (in which case we may be
-// adding a new partition).
-bool IsDynamicPartition(DynamicPartitionControlInterface* dynamic_control,
- const base::FilePath& device_dir,
- MetadataBuilder* source_metadata,
- const string& partition_name,
- const string& source_suffix,
- const string& target_suffix) {
- bool dynamic_source_exist =
- source_metadata->FindPartition(partition_name + source_suffix) != nullptr;
- bool static_target_exist = dynamic_control->DeviceExists(
- device_dir.Append(partition_name + target_suffix).value());
-
- return dynamic_source_exist || !static_target_exist;
-}
-
-bool FilterPartitionSizes(DynamicPartitionControlInterface* dynamic_control,
- const base::FilePath& device_dir,
- const PartitionMetadata& partition_metadata,
- MetadataBuilder* source_metadata,
- const string& source_suffix,
- const string& target_suffix,
- PartitionMetadata* logical_sizes) {
- for (const auto& pair : partition_metadata) {
- if (!IsDynamicPartition(dynamic_control,
- device_dir,
- source_metadata,
- pair.first,
- source_suffix,
- target_suffix)) {
- // In the future we can check static partition sizes, but skip for now.
- LOG(INFO) << pair.first << " is static; assume its size is "
- << pair.second << " bytes.";
- continue;
- }
-
- logical_sizes->insert(pair);
}
return true;
}
@@ -362,60 +348,28 @@
return false;
}
- string current_suffix;
- if (!GetSuffix(current_slot, ¤t_suffix)) {
- return false;
- }
-
string target_suffix;
if (!GetSuffix(target_slot, &target_suffix)) {
return false;
}
- auto builder =
- dynamic_control_->LoadMetadataBuilder(super_device, current_slot);
- if (builder == nullptr) {
+ if (!InitPartitionMetadataInternal(dynamic_control_.get(),
+ super_device,
+ current_slot,
+ target_slot,
+ target_suffix,
+ partition_metadata)) {
return false;
}
- // Read metadata from current slot to determine which partitions are logical
- // and may be resized. Do not read from target slot because metadata at
- // target slot may be corrupted.
- PartitionMetadata logical_sizes;
- if (!FilterPartitionSizes(dynamic_control_.get(),
- device_dir,
- partition_metadata,
- builder.get() /* source metadata */,
- current_suffix,
- target_suffix,
- &logical_sizes)) {
+ if (!Remap(dynamic_control_.get(),
+ super_device,
+ target_slot,
+ target_suffix,
+ partition_metadata)) {
return false;
}
- if (!ResizePartitions(dynamic_control_.get(),
- super_device,
- target_slot,
- target_suffix,
- logical_sizes,
- builder.get())) {
- return false;
- }
-
- // Unmap all partitions, and remap partitions if size is non-zero.
- for (const auto& pair : logical_sizes) {
- if (!dynamic_control_->UnmapPartitionOnDeviceMapper(
- pair.first + target_suffix, true /* wait */)) {
- return false;
- }
- if (pair.second == 0) {
- continue;
- }
- string map_path;
- if (!dynamic_control_->MapPartitionOnDeviceMapper(
- super_device, pair.first + target_suffix, target_slot, &map_path)) {
- return false;
- }
- }
return true;
}