diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index 54350a52d..4406696ae 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -253,7 +253,7 @@ MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) { header_.magic = LP_METADATA_HEADER_MAGIC; header_.major_version = LP_METADATA_MAJOR_VERSION; header_.minor_version = LP_METADATA_MINOR_VERSION_MIN; - header_.header_size = sizeof(header_); + header_.header_size = sizeof(LpMetadataHeaderV1_0); header_.partitions.entry_size = sizeof(LpMetadataPartition); header_.extents.entry_size = sizeof(LpMetadataExtent); header_.groups.entry_size = sizeof(LpMetadataPartitionGroup); @@ -264,6 +264,12 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) { geometry_ = metadata.geometry; block_devices_ = metadata.block_devices; + // Bump the version as necessary to copy any newer fields. + if (metadata.header.minor_version >= LP_METADATA_VERSION_FOR_EXPANDED_HEADER) { + RequireExpandedMetadataHeader(); + header_.flags = metadata.header.flags; + } + for (const auto& group : metadata.groups) { std::string group_name = GetPartitionGroupName(group); if (!AddGroup(group_name, group.maximum_size)) { @@ -883,6 +889,14 @@ std::unique_ptr MetadataBuilder::Export() { return metadata; } +void MetadataBuilder::RequireExpandedMetadataHeader() { + if (header_.minor_version >= LP_METADATA_VERSION_FOR_EXPANDED_HEADER) { + return; + } + header_.minor_version = LP_METADATA_VERSION_FOR_EXPANDED_HEADER; + header_.header_size = sizeof(LpMetadataHeaderV1_2); +} + uint64_t MetadataBuilder::AllocatableSpace() const { uint64_t total_size = 0; for (const auto& block_device : block_devices_) { diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp index a67ffa782..ca8df61fd 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -352,6 +352,7 @@ TEST_F(BuilderTest, BuilderExport) { 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_MIN); + EXPECT_EQ(header.header_size, sizeof(LpMetadataHeaderV1_0)); ASSERT_EQ(exported->partitions.size(), 2); ASSERT_EQ(exported->extents.size(), 3); @@ -917,3 +918,22 @@ TEST_F(BuilderTest, Interval) { std::vector{Interval(0, 100, 150)}) .size()); } + +TEST_F(BuilderTest, ExpandedHeader) { + unique_ptr builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + ASSERT_NE(builder, nullptr); + + builder->RequireExpandedMetadataHeader(); + + unique_ptr exported = builder->Export(); + ASSERT_NE(exported, nullptr); + EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2)); + + exported->header.flags = 0x5e5e5e5e; + + builder = MetadataBuilder::New(*exported.get()); + exported = builder->Export(); + ASSERT_NE(exported, nullptr); + EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2)); + EXPECT_EQ(exported->header.flags, 0x5e5e5e5e); +} diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h index 1e9d636ff..7a334fbd9 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -325,6 +325,10 @@ class MetadataBuilder { bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const; bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info); + // Require the expanded metadata header. This is exposed for testing, and + // is normally only called as needed by other methods. + void RequireExpandedMetadataHeader(); + // 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. diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index 6e928b486..26cbf07ba 100644 --- a/fs_mgr/liblp/include/liblp/metadata_format.h +++ b/fs_mgr/liblp/include/liblp/metadata_format.h @@ -40,11 +40,14 @@ extern "C" { /* Current metadata version. */ #define LP_METADATA_MAJOR_VERSION 10 #define LP_METADATA_MINOR_VERSION_MIN 0 -#define LP_METADATA_MINOR_VERSION_MAX 1 +#define LP_METADATA_MINOR_VERSION_MAX 2 /* Metadata version needed to use the UPDATED partition attribute. */ #define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1 +/* Metadata version needed for the new expanded header struct. */ +#define LP_METADATA_VERSION_FOR_EXPANDED_HEADER 2 + /* Attributes for the LpMetadataPartition::attributes field. * * READONLY - The partition should not be considered writable. When used with @@ -212,6 +215,22 @@ typedef struct LpMetadataHeader { LpMetadataTableDescriptor groups; /* 116: Block device table. */ LpMetadataTableDescriptor block_devices; + + /* Everything past here is header version 1.2+, and is only included if + * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must + * zero these additional fields. + */ + + /* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are + * independent of the version number and intended to be informational only. + * New flags can be added without bumping the version. + * + * (Note there are no flags currently defined.) + */ + uint32_t flags; + + /* 132: Reserved (zero), pad to 256 bytes. */ + uint8_t reserved[124]; } __attribute__((packed)) LpMetadataHeader; /* This struct defines a logical partition entry, similar to what would be @@ -351,6 +370,25 @@ typedef struct LpMetadataBlockDevice { */ #define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0) +/* For ease of writing compatibility checks, the original metadata header is + * preserved below, and typedefs are provided for the current version. + */ +typedef struct LpMetadataHeaderV1_0 { + uint32_t magic; + uint16_t major_version; + uint16_t minor_version; + uint32_t header_size; + uint8_t header_checksum[32]; + uint32_t tables_size; + uint8_t tables_checksum[32]; + LpMetadataTableDescriptor partitions; + LpMetadataTableDescriptor extents; + LpMetadataTableDescriptor groups; + LpMetadataTableDescriptor block_devices; +} __attribute__((packed)) LpMetadataHeaderV1_0; + +typedef LpMetadataHeader LpMetadataHeaderV1_2; + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index 22f674678..e67fb33b6 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -372,7 +372,7 @@ TEST_F(LiblpTest, TooManyPartitions) { // Compute the maximum number of partitions we can fit in 512 bytes of // metadata. By default there is the header, one partition group, and a // block device entry. - static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) - + static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeaderV1_0) - sizeof(LpMetadataPartitionGroup) - sizeof(LpMetadataBlockDevice); size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition); @@ -742,3 +742,28 @@ TEST_F(LiblpTest, UpdateVirtualAB) { ASSERT_GE(metadata->partitions.size(), 1); ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0); } + +TEST_F(LiblpTest, ReadExpandedHeader) { + unique_ptr builder = CreateDefaultBuilder(); + ASSERT_NE(builder, nullptr); + ASSERT_TRUE(AddDefaultPartitions(builder.get())); + + builder->RequireExpandedMetadataHeader(); + + unique_fd fd = CreateFakeDisk(); + ASSERT_GE(fd, 0); + + DefaultPartitionOpener opener(fd); + + // Export and flash. + unique_ptr exported = builder->Export(); + ASSERT_NE(exported, nullptr); + exported->header.flags = 0x5e5e5e5e; + ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get())); + + unique_ptr imported = ReadMetadata(opener, "super", 0); + ASSERT_NE(imported, nullptr); + EXPECT_EQ(imported->header.header_size, sizeof(LpMetadataHeaderV1_2)); + EXPECT_EQ(imported->header.header_size, exported->header.header_size); + EXPECT_EQ(imported->header.flags, exported->header.flags); +} diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index aecf6852e..30c17e42a 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -31,6 +31,9 @@ namespace android { namespace fs_mgr { +static_assert(sizeof(LpMetadataHeaderV1_0) == offsetof(LpMetadataHeader, flags), + "Incorrect LpMetadataHeader v0 size"); + // Helper class for reading descriptors and memory buffers in the same manner. class Reader { public: @@ -161,30 +164,59 @@ static bool ValidateTableBounds(const LpMetadataHeader& header, return true; } -static bool ValidateMetadataHeader(const LpMetadataHeader& header) { - // To compute the header's checksum, we have to temporarily set its checksum - // field to 0. - { - LpMetadataHeader temp = header; - memset(&temp.header_checksum, 0, sizeof(temp.header_checksum)); - SHA256(&temp, sizeof(temp), temp.header_checksum); - if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != 0) { - LERROR << "Logical partition metadata has invalid checksum."; - return false; - } +static bool ReadMetadataHeader(Reader* reader, LpMetadata* metadata) { + // Note we zero the struct since older files will result in a partial read. + LpMetadataHeader& header = metadata->header; + memset(&header, 0, sizeof(header)); + + if (!reader->ReadFully(&header, sizeof(LpMetadataHeaderV1_0))) { + PERROR << __PRETTY_FUNCTION__ << " read failed"; + return false; } - // Do basic validation of key metadata bits. + // Do basic sanity checks before computing the checksum. if (header.magic != LP_METADATA_HEADER_MAGIC) { LERROR << "Logical partition metadata has invalid magic value."; return false; } - // Check that the version is compatible. if (header.major_version != LP_METADATA_MAJOR_VERSION || header.minor_version > LP_METADATA_MINOR_VERSION_MAX) { LERROR << "Logical partition metadata has incompatible version."; return false; } + + // Validate the header struct size against the reported version. + uint32_t expected_struct_size = sizeof(header); + if (header.minor_version < LP_METADATA_VERSION_FOR_EXPANDED_HEADER) { + expected_struct_size = sizeof(LpMetadataHeaderV1_0); + } + if (header.header_size != expected_struct_size) { + LERROR << "Invalid partition metadata header struct size."; + return false; + } + + // Read in any remaining fields, the last step needed before checksumming. + if (size_t remaining_bytes = header.header_size - sizeof(LpMetadataHeaderV1_0)) { + uint8_t* offset = reinterpret_cast(&header) + sizeof(LpMetadataHeaderV1_0); + if (!reader->ReadFully(offset, remaining_bytes)) { + PERROR << __PRETTY_FUNCTION__ << " read failed"; + return false; + } + } + + // To compute the header's checksum, we have to temporarily set its checksum + // field to 0. Note that we must only compute up to |header_size|. + { + LpMetadataHeader temp = header; + memset(&temp.header_checksum, 0, sizeof(temp.header_checksum)); + SHA256(&temp, temp.header_size, temp.header_checksum); + if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) != + 0) { + LERROR << "Logical partition metadata has invalid checksum."; + return false; + } + } + if (!ValidateTableBounds(header, header.partitions) || !ValidateTableBounds(header, header.extents) || !ValidateTableBounds(header, header.groups) || @@ -215,19 +247,22 @@ static std::unique_ptr ParseMetadata(const LpMetadataGeometry& geome Reader* reader) { // First read and validate the header. std::unique_ptr metadata = std::make_unique(); - if (!reader->ReadFully(&metadata->header, sizeof(metadata->header))) { - PERROR << __PRETTY_FUNCTION__ << " read " << sizeof(metadata->header) << "bytes failed"; - return nullptr; - } - if (!ValidateMetadataHeader(metadata->header)) { - return nullptr; - } + metadata->geometry = geometry; + if (!ReadMetadataHeader(reader, metadata.get())) { + return nullptr; + } LpMetadataHeader& header = metadata->header; - // Read the metadata payload. Allocation is fallible in case the metadata is - // corrupt and has some huge value. + // Sanity check the table size. + if (header.tables_size > geometry.metadata_max_size) { + LERROR << "Invalid partition metadata header table size."; + return nullptr; + } + + // Read the metadata payload. Allocation is fallible since the table size + // could be large. std::unique_ptr buffer(new (std::nothrow) uint8_t[header.tables_size]); if (!buffer) { LERROR << "Out of memory reading logical partition tables."; diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp index bb24069aa..8bf1ee923 100644 --- a/fs_mgr/liblp/writer.cpp +++ b/fs_mgr/liblp/writer.cpp @@ -74,10 +74,10 @@ std::string SerializeMetadata(const LpMetadata& input) { // Compute header checksum. memset(header.header_checksum, 0, sizeof(header.header_checksum)); - SHA256(&header, sizeof(header), header.header_checksum); + SHA256(&header, header.header_size, header.header_checksum); std::string header_blob = - std::string(reinterpret_cast(&metadata.header), sizeof(metadata.header)); + std::string(reinterpret_cast(&header), header.header_size); return header_blob + tables; }