Merge "liblp: Expand the metadata header for future use."
This commit is contained in:
commit
18bb214b3f
|
@ -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_) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<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 @@ static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geome
|
|||
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.";
|
||||
|
|
|
@ -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<const char*>(&metadata.header), sizeof(metadata.header));
|
||||
std::string(reinterpret_cast<const char*>(&header), header.header_size);
|
||||
return header_blob + tables;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue