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
This commit is contained in:
David Anderson 2019-08-22 14:55:03 -07:00
parent 9966699af7
commit cd7a635dc7
6 changed files with 65 additions and 7 deletions

View file

@ -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());

View file

@ -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);

View file

@ -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:

View file

@ -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);
}

View file

@ -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;
}

View file

@ -269,6 +269,7 @@ bool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot
<< "; this partition should not belong to this group!";
continue; // not adding to new_partition_ptrs
}
partition.attributes |= LP_PARTITION_ATTR_UPDATED;
partition.group_index = std::distance(new_group_ptrs.begin(), it);
new_partition_ptrs.push_back(&partition);
}