Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7caae0a authored by David Anderson's avatar David Anderson Committed by android-build-merger
Browse files

Merge "fastboot: Fix flashing both slots with dynamic partitions."

am: d016c751

Change-Id: I139b7f506aee6554d134ba30f543b880334253e7
parents 6fc0396f d016c751
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -148,13 +148,40 @@ bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wip
    // 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++) {
+10 −7
Original line number Diff line number Diff line
@@ -1200,6 +1200,15 @@ FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_ov
void FlashAllTool::Flash() {
    DumpInfo();
    CheckRequirements();

    // Change the slot first, so we boot into the correct recovery image when
    // using fastbootd.
    if (slot_override_ == "all") {
        set_active("a");
    } else {
        set_active(slot_override_);
    }

    DetermineSecondarySlot();
    CollectImages();

@@ -1223,12 +1232,6 @@ void FlashAllTool::Flash() {

    // Flash OS images, resizing logical partitions as needed.
    FlashImages(os_images_);

    if (slot_override_ == "all") {
        set_active("a");
    } else {
        set_active(slot_override_);
    }
}

void FlashAllTool::CheckRequirements() {
@@ -1243,7 +1246,7 @@ void FlashAllTool::DetermineSecondarySlot() {
    if (skip_secondary_) {
        return;
    }
    if (slot_override_ != "") {
    if (slot_override_ != "" && slot_override_ != "all") {
        secondary_slot_ = get_other_slot(slot_override_);
    } else {
        secondary_slot_ = get_other_slot();
+99 −13
Original line number Diff line number Diff line
@@ -184,21 +184,25 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) {
        if (!builder) {
            return false;
        }
        ImportExtents(builder, metadata, partition);
    }
    return true;
}

        for (size_t i = 0; i < partition.num_extents; i++) {
            const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i];
void MetadataBuilder::ImportExtents(Partition* dest, const LpMetadata& metadata,
                                    const LpMetadataPartition& source) {
    for (size_t i = 0; i < source.num_extents; i++) {
        const LpMetadataExtent& extent = metadata.extents[source.first_extent_index + i];
        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
            auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,
                                                       extent.target_data);
                builder->AddExtent(std::move(copy));
            dest->AddExtent(std::move(copy));
        } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {
            auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);
                builder->AddExtent(std::move(copy));
            dest->AddExtent(std::move(copy));
        }
    }
}
    return true;
}

static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {
    if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {
@@ -471,13 +475,18 @@ auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
    return free_regions;
}

bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
bool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,
                                                  uint64_t new_size) {
    PartitionGroup* group = FindGroup(partition->group_name());
    CHECK(group);

    if (new_size <= old_size) {
        return true;
    }

    // Figure out how much we need to allocate, and whether our group has
    // enough space remaining.
    uint64_t space_needed = aligned_size - partition->size();
    uint64_t space_needed = new_size - old_size;
    if (group->maximum_size() > 0) {
        uint64_t group_size = TotalSizeOfGroup(group);
        if (group_size >= group->maximum_size() ||
@@ -488,7 +497,11 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size)
            return false;
        }
    }
    return true;
}

bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
    uint64_t space_needed = aligned_size - partition->size();
    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
    DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);

@@ -704,6 +717,10 @@ bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_s
    uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size);
    uint64_t old_size = partition->size();

    if (!ValidatePartitionSizeChange(partition, old_size, aligned_size)) {
        return false;
    }

    if (aligned_size > old_size) {
        if (!GrowPartition(partition, aligned_size)) {
            return false;
@@ -750,5 +767,74 @@ void MetadataBuilder::RemoveGroupAndPartitions(const std::string& group_name) {
    }
}

static bool CompareBlockDevices(const LpMetadataBlockDevice& first,
                                const LpMetadataBlockDevice& second) {
    // Note: we don't compare alignment, since it's a performance thing and
    // won't affect whether old extents continue to work.
    return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&
           GetBlockDevicePartitionName(first) == GetBlockDevicePartitionName(second);
}

bool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,
                                       const std::set<std::string>& partition_names) {
    // The block device list must be identical. We do not try to be clever and
    // allow ordering changes or changes that don't affect partitions. This
    // process is designed to allow the most common flashing scenarios and more
    // complex ones should require a wipe.
    if (metadata.block_devices.size() != block_devices_.size()) {
        LINFO << "Block device tables does not match.";
        return false;
    }
    for (size_t i = 0; i < metadata.block_devices.size(); i++) {
        const LpMetadataBlockDevice& old_device = metadata.block_devices[i];
        const LpMetadataBlockDevice& new_device = block_devices_[i];
        if (!CompareBlockDevices(old_device, new_device)) {
            LINFO << "Block device tables do not match";
            return false;
        }
    }

    // Import named partitions. Note that we do not attempt to merge group
    // information here. If the device changed its group names, the old
    // partitions will fail to merge. The same could happen if the group
    // allocation sizes change.
    for (const auto& partition : metadata.partitions) {
        std::string partition_name = GetPartitionName(partition);
        if (partition_names.find(partition_name) == partition_names.end()) {
            continue;
        }
        if (!ImportPartition(metadata, partition)) {
            return false;
        }
    }
    return true;
}

bool MetadataBuilder::ImportPartition(const LpMetadata& metadata,
                                      const LpMetadataPartition& source) {
    std::string partition_name = GetPartitionName(source);
    Partition* partition = FindPartition(partition_name);
    if (!partition) {
        std::string group_name = GetPartitionGroupName(metadata.groups[source.group_index]);
        partition = AddPartition(partition_name, group_name, source.attributes);
        if (!partition) {
            return false;
        }
    }
    if (partition->size() > 0) {
        LINFO << "Importing partition table would overwrite non-empty partition: "
              << partition_name;
        return false;
    }

    ImportExtents(partition, metadata, source);

    if (!ValidatePartitionSizeChange(partition, 0, partition->size())) {
        partition->RemoveExtents();
        return false;
    }
    return true;
}

}  // namespace fs_mgr
}  // namespace android
+61 −0
Original line number Diff line number Diff line
@@ -632,3 +632,64 @@ TEST(liblp, MultipleBlockDevices) {
    EXPECT_EQ(metadata->extents[2].target_data, 1472);
    EXPECT_EQ(metadata->extents[2].target_source, 2);
}

TEST(liblp, ImportPartitionsOk) {
    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
    ASSERT_NE(builder, nullptr);

    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
    ASSERT_NE(system, nullptr);
    ASSERT_NE(vendor, nullptr);
    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
    EXPECT_EQ(builder->ResizePartition(system, 98304), true);

    unique_ptr<LpMetadata> exported = builder->Export();
    ASSERT_NE(exported, nullptr);

    builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
    ASSERT_NE(builder, nullptr);

    ASSERT_TRUE(builder->ImportPartitions(*exported.get(), {"vendor"}));
    EXPECT_NE(builder->FindPartition("vendor"), nullptr);
    EXPECT_EQ(builder->FindPartition("system"), nullptr);

    unique_ptr<LpMetadata> new_metadata = builder->Export();
    ASSERT_NE(new_metadata, nullptr);

    ASSERT_EQ(exported->partitions.size(), static_cast<size_t>(2));
    ASSERT_EQ(GetPartitionName(exported->partitions[1]), "vendor");
    ASSERT_EQ(new_metadata->partitions.size(), static_cast<size_t>(1));
    ASSERT_EQ(GetPartitionName(new_metadata->partitions[0]), "vendor");

    const LpMetadataExtent& extent_a =
            exported->extents[exported->partitions[1].first_extent_index];
    const LpMetadataExtent& extent_b =
            new_metadata->extents[new_metadata->partitions[0].first_extent_index];
    EXPECT_EQ(extent_a.num_sectors, extent_b.num_sectors);
    EXPECT_EQ(extent_a.target_type, extent_b.target_type);
    EXPECT_EQ(extent_a.target_data, extent_b.target_data);
    EXPECT_EQ(extent_a.target_source, extent_b.target_source);
}

TEST(liblp, ImportPartitionsFail) {
    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
    ASSERT_NE(builder, nullptr);

    Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
    Partition* vendor = builder->AddPartition("vendor", LP_PARTITION_ATTR_READONLY);
    ASSERT_NE(system, nullptr);
    ASSERT_NE(vendor, nullptr);
    EXPECT_EQ(builder->ResizePartition(system, 65536), true);
    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);
    EXPECT_EQ(builder->ResizePartition(system, 98304), true);

    unique_ptr<LpMetadata> exported = builder->Export();
    ASSERT_NE(exported, nullptr);

    // Different device size.
    builder = MetadataBuilder::New(1024 * 2048, 1024, 2);
    ASSERT_NE(builder, nullptr);
    EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {"system"}));
}
+10 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@

#include <map>
#include <memory>
#include <set>

#include "liblp.h"
#include "partition_opener.h"
@@ -225,6 +226,11 @@ class MetadataBuilder {
    bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
    bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);

    // Attempt to preserve the named partitions from an older metadata. If this
    // is not possible (for example, the block device list has changed) then
    // false is returned.
    bool ImportPartitions(const LpMetadata& metadata, const std::set<std::string>& partition_names);

  private:
    MetadataBuilder();
    MetadataBuilder(const MetadataBuilder&) = delete;
@@ -240,6 +246,10 @@ class MetadataBuilder {
    uint64_t TotalSizeOfGroup(PartitionGroup* group) const;
    bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);
    bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;
    bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size);
    void ImportExtents(Partition* dest, const LpMetadata& metadata,
                       const LpMetadataPartition& source);
    bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);

    struct Interval {
        uint32_t device_index;
Loading