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

Commit 9f75098c authored by David Anderson's avatar David Anderson
Browse files

liblp: Expand the metadata header for future use.

A few times we have wanted to stash small bits of information in the
super header, but we haven't had any bits to do so. This patch addresses
future needs in two ways:

  1. A "flags" field has been added for miscellanious bits that do not
     need a version bump.
  2. The header struct has been padded to 256 bytes to allow for future
     expansion without complicating the struct-parsing code.

This is the first time we've materially changed the format, so this
patch needs some extra explanation.

In all the places we rely on sizeof(LpMetadataHeader), we now need to
use the |header_size| field instead. To make newer versions of liblp
compatible with older headers, we read the minimum required header size
and fill in the extra bytes as needed. To make the validation and
reading logic more clear, it is now combined into a single function,
ReadMetdataHeader.

MetadataBuilder will still emit 1.0-compatible headers, to avoid
changing the on-disk format of existing devices. The new header will
only be emitted as-needed.

Bug: 134949511
Test: liblp_test gtest
      retrofit DAP device boots
      launch DAP device boots

Change-Id: I6221123768ff0057a73967ecb2ff9b006c17af88
parent 0231f4fc
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -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<LpMetadata> 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_) {
+20 −0
Original line number Diff line number Diff line
@@ -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>{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);
}
+4 −0
Original line number Diff line number Diff line
@@ -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.
+39 −1
Original line number Diff line number Diff line
@@ -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
+26 −1
Original line number Diff line number Diff line
@@ -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<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);
}
Loading