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

Commit cd7a635d authored by David Anderson's avatar David Anderson
Browse files

liblp: Add an attribute to indicate that a partition has been updated.

When the bootloader (or fastbootd) flashes the super partition, we need
to make sure that init doesn't re-map any snapshot or snapshot-merge targets.
A simple way to do this is to introduce an attribute that is only added
by update_engine. When this flag is present, we know the partition has
not been flashed.

This bumps the minor version of LpMetadata. To make this as uninvasive
as possible, the new minor version is only used when MetadataBuilder
detects the new attribute. The new liblp can read older metadata, but will
reject it if it contains an illegal attribute set.

Bug: 139154795
Test: liblp_test gtest
Change-Id: I5ae15d11219b41575a9f71d7dbdb43cbf07a3529
parent 9966699a
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -232,7 +232,7 @@ MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
    memset(&header_, 0, sizeof(header_));
    header_.magic = LP_METADATA_HEADER_MAGIC;
    header_.major_version = LP_METADATA_MAJOR_VERSION;
    header_.minor_version = LP_METADATA_MINOR_VERSION;
    header_.minor_version = LP_METADATA_MINOR_VERSION_MIN;
    header_.header_size = sizeof(header_);
    header_.partitions.entry_size = sizeof(LpMetadataPartition);
    header_.extents.entry_size = sizeof(LpMetadataExtent);
@@ -796,6 +796,11 @@ std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
            return nullptr;
        }

        if (partition->attributes() & LP_PARTITION_ATTR_UPDATED) {
            static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;
            metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);
        }

        strncpy(part.name, partition->name().c_str(), sizeof(part.name));
        part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());
        part.num_extents = static_cast<uint32_t>(partition->extents().size());
+1 −1
Original line number Diff line number Diff line
@@ -349,7 +349,7 @@ TEST_F(BuilderTest, BuilderExport) {
    const LpMetadataHeader& header = exported->header;
    EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);
    EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);
    EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION);
    EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION_MIN);

    ASSERT_EQ(exported->partitions.size(), 2);
    ASSERT_EQ(exported->extents.size(), 3);
+19 −3
Original line number Diff line number Diff line
@@ -39,7 +39,11 @@ extern "C" {

/* Current metadata version. */
#define LP_METADATA_MAJOR_VERSION 10
#define LP_METADATA_MINOR_VERSION 0
#define LP_METADATA_MINOR_VERSION_MIN 0
#define LP_METADATA_MINOR_VERSION_MAX 1

/* Metadata version needed to use the UPDATED partition attribute. */
#define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1

/* Attributes for the LpMetadataPartition::attributes field.
 *
@@ -58,8 +62,20 @@ extern "C" {
 */
#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)

/* Mask that defines all valid attributes. */
#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
/* This flag is applied automatically when using MetadataBuilder::NewForUpdate.
 * It signals that the partition was created (or modified) for a snapshot-based
 * update. If this flag is not present, the partition was likely flashed via
 * fastboot.
 */
#define LP_PARTITION_ATTR_UPDATED (1 << 2)

/* Mask that defines all valid attributes. When changing this, make sure to
 * update ParseMetadata().
 */
#define LP_PARTITION_ATTRIBUTE_MASK_V0 \
    (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)
#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTR_UPDATED)
#define LP_PARTITION_ATTRIBUTE_MASK LP_PARTITION_ATTRIBUTE_MASK_V1

/* Default name of the physical partition that holds logical partition entries.
 * The layout of this partition will look like:
+29 −0
Original line number Diff line number Diff line
@@ -713,3 +713,32 @@ TEST_F(LiblpTest, UpdateNonRetrofit) {
    ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
    EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
}

TEST_F(LiblpTest, UpdateVirtualAB) {
    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
            .WillByDefault(Return(true));

    unique_fd fd = CreateFlashedDisk();
    ASSERT_GE(fd, 0);

    DefaultPartitionOpener opener(fd);
    auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
    ASSERT_NE(builder, nullptr);
    auto updated = builder->Export();
    ASSERT_NE(updated, nullptr);
    ASSERT_TRUE(UpdatePartitionTable(opener, "super", *updated.get(), 1));

    // Validate old slot.
    auto metadata = ReadMetadata(opener, "super", 0);
    ASSERT_NE(metadata, nullptr);
    ASSERT_EQ(metadata->header.minor_version, 0);
    ASSERT_GE(metadata->partitions.size(), 1);
    ASSERT_EQ(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);

    // Validate new slot.
    metadata = ReadMetadata(opener, "super", 1);
    ASSERT_NE(metadata, nullptr);
    ASSERT_EQ(metadata->header.minor_version, 1);
    ASSERT_GE(metadata->partitions.size(), 1);
    ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
}
+9 −2
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
    }
    // Check that the version is compatible.
    if (header.major_version != LP_METADATA_MAJOR_VERSION ||
        header.minor_version > LP_METADATA_MINOR_VERSION) {
        header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {
        LERROR << "Logical partition metadata has incompatible version.";
        return false;
    }
@@ -245,6 +245,13 @@ static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geome
        return nullptr;
    }

    uint32_t valid_attributes = 0;
    if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {
        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V1;
    } else {
        valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;
    }

    // ValidateTableSize ensured that |cursor| is valid for the number of
    // entries in the table.
    uint8_t* cursor = buffer.get() + header.partitions.offset;
@@ -253,7 +260,7 @@ static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geome
        memcpy(&partition, cursor, sizeof(partition));
        cursor += header.partitions.entry_size;

        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK) {
        if (partition.attributes & ~valid_attributes) {
            LERROR << "Logical partition has invalid attribute set.";
            return nullptr;
        }
Loading