Merge "liblp: Expand the metadata header for future use." am: 18bb214b3f am: c229e1facc

Change-Id: I8db97450adb46cfca6bf1866c3fef79cb5fb8f73
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 54350a5..4406696 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -253,7 +253,7 @@
     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 @@
     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 @@
     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 a67ffa7..ca8df61 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -352,6 +352,7 @@
     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 @@
                                       std::vector<Interval>{Interval(0, 100, 150)})
                           .size());
 }
+
+TEST_F(BuilderTest, ExpandedHeader) {
+    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
+    ASSERT_NE(builder, nullptr);
+
+    builder->RequireExpandedMetadataHeader();
+
+    unique_ptr<LpMetadata> 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 1e9d636..7a334fb 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -325,6 +325,10 @@
     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 6e928b4..26cbf07 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -40,11 +40,14 @@
 /* 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 @@
     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 @@
  */
 #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 22f6746..e67fb33 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -372,7 +372,7 @@
     // 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 @@
     ASSERT_GE(metadata->partitions.size(), 1);
     ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);
 }
+
+TEST_F(LiblpTest, ReadExpandedHeader) {
+    unique_ptr<MetadataBuilder> 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<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    exported->header.flags = 0x5e5e5e5e;
+    ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
+
+    unique_ptr<LpMetadata> 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 aecf685..30c17e4 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 @@
     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<uint8_t*>(&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 @@
                                                  Reader* reader) {
     // First read and validate the header.
     std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();
-    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<uint8_t[]> 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 bb24069..8bf1ee9 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -74,10 +74,10 @@
 
     // 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<const char*>(&metadata.header), sizeof(metadata.header));
+            std::string(reinterpret_cast<const char*>(&header), header.header_size);
     return header_blob + tables;
 }