fastboot: Fix flashing both slots with dynamic partitions.
When updating the super partition, attempt to preserve partitions from
the other slot. If any partition can't be preserved, fail and require a
wipe (-w) to proceed. This allows two bootable builds to be flashed to
both slots.
The preserve operation can fail if the metadata is not compatible with
the old partition layout. For example, if the partition references a
group that no longer exists, or a group changed its capacity, or the
metadata's block device list or list contents changed.
Bug: N/A
Test: liblp_test gtest
fastboot flashall --skip-secondary
Change-Id: I53fdd29bc1f0ef132005a93d3cf1cdcd7f2fc05f
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 66b90bf..7c9e1d0 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -148,13 +148,40 @@
// image.
std::string slot_suffix = device->GetCurrentSlot();
uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
- if (wipe || !ReadMetadata(super_name, slot_number)) {
+ std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);
+ if (wipe || !old_metadata) {
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
return device->WriteOkay("Successfully flashed partition table");
}
+ std::set<std::string> partitions_to_keep;
+ for (const auto& partition : old_metadata->partitions) {
+ // Preserve partitions in the other slot, but not the current slot.
+ std::string partition_name = GetPartitionName(partition);
+ if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
+ continue;
+ }
+ partitions_to_keep.emplace(partition_name);
+ }
+
+ // Do not preserve the scratch partition.
+ partitions_to_keep.erase("scratch");
+
+ if (!partitions_to_keep.empty()) {
+ std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*new_metadata.get());
+ if (!builder->ImportPartitions(*old_metadata.get(), partitions_to_keep)) {
+ return device->WriteFail(
+ "Old partitions are not compatible with the new super layout; wipe needed");
+ }
+
+ new_metadata = builder->Export();
+ if (!new_metadata) {
+ return device->WriteFail("Unable to build new partition table; wipe needed");
+ }
+ }
+
// Write the new table to every metadata slot.
bool ok = true;
for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {