From cd7a635dc7efeb3368886e7521e641ec7cdc0129 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 22 Aug 2019 14:55:03 -0700 Subject: [PATCH] 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 --- fs_mgr/liblp/builder.cpp | 7 ++++- fs_mgr/liblp/builder_test.cpp | 2 +- fs_mgr/liblp/include/liblp/metadata_format.h | 22 +++++++++++++-- fs_mgr/liblp/io_test.cpp | 29 ++++++++++++++++++++ fs_mgr/liblp/reader.cpp | 11 ++++++-- fs_mgr/liblp/utility.cpp | 1 + 6 files changed, 65 insertions(+), 7 deletions(-) diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index 77c9c2878..8a74745dd 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -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 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(metadata->extents.size()); part.num_extents = static_cast(partition->extents().size()); diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp index dde6d07c9..c5b404766 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -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); diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index 8934aaff2..6e928b486 100644 --- a/fs_mgr/liblp/include/liblp/metadata_format.h +++ b/fs_mgr/liblp/include/liblp/metadata_format.h @@ -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: diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index df89e9c90..22f674678 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -713,3 +713,32 @@ TEST_F(LiblpTest, UpdateNonRetrofit) { ASSERT_EQ(updated->block_devices.size(), static_cast(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); +} diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index 8dbe955fa..aecf6852e 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -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 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 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; } diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp index 17f05aa7c..afcce8f0a 100644 --- a/fs_mgr/liblp/utility.cpp +++ b/fs_mgr/liblp/utility.cpp @@ -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); }