From 9a5324178e72ae416e6953481ae571353591943e Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 12 Oct 2018 13:51:52 -0700 Subject: [PATCH] liblp: Store device information in a new block device table. This patch removes the alignment, block device size, and starting sector fields from LpGeometry into a new LpMetadataBlockDevice struct. The metadata now contains a table of these structs, and the table will have exactly one entry representing the super partition. This refactoring will make it easier to have logical partitions span multiple physical partitions. When that happens, the table will be allowed to have more than one entry, and the first entry of the table will be considered the "root" of the super partition. Bug: 116802789 Test: liblp_test gtest device with logical partitions flashes and boots Change-Id: I97f23beac0363182cb6ae78ba2595860950afcf0 --- fs_mgr/fs_mgr_dm_linear.cpp | 48 ++++++++--- fs_mgr/liblp/builder.cpp | 50 +++++++----- fs_mgr/liblp/builder_test.cpp | 23 ++++-- fs_mgr/liblp/images.cpp | 19 ++--- fs_mgr/liblp/include/liblp/builder.h | 4 + fs_mgr/liblp/include/liblp/liblp.h | 9 +++ fs_mgr/liblp/include/liblp/metadata_format.h | 85 +++++++++++--------- fs_mgr/liblp/io_test.cpp | 29 +++++-- fs_mgr/liblp/reader.cpp | 38 ++++++--- fs_mgr/liblp/utility.cpp | 23 +++++- fs_mgr/liblp/utility.h | 6 +- fs_mgr/liblp/utility_test.cpp | 4 - fs_mgr/liblp/writer.cpp | 78 +++++++++++------- 13 files changed, 278 insertions(+), 138 deletions(-) diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp index 804069a3c..6ddd5a895 100644 --- a/fs_mgr/fs_mgr_dm_linear.cpp +++ b/fs_mgr/fs_mgr_dm_linear.cpp @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -50,8 +51,21 @@ using DmTarget = android::dm::DmTarget; using DmTargetZero = android::dm::DmTargetZero; using DmTargetLinear = android::dm::DmTargetLinear; -static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata, - const LpMetadataPartition& partition, DmTable* table) { +bool GetPhysicalPartitionDevicePath(const LpMetadataBlockDevice& block_device, + std::string* result) { + // Note: device-mapper will not accept symlinks, so we must use realpath + // here. + std::string name = GetBlockDevicePartitionName(block_device); + std::string path = "/dev/block/by-name/" + name; + if (!android::base::Realpath(path, result)) { + PERROR << "realpath: " << path; + return false; + } + return true; +} + +static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition, + DmTable* table) { uint64_t sector = 0; for (size_t i = 0; i < partition.num_extents; i++) { const auto& extent = metadata.extents[partition.first_extent_index + i]; @@ -60,10 +74,22 @@ static bool CreateDmTable(const std::string& block_device, const LpMetadata& met case LP_TARGET_TYPE_ZERO: target = std::make_unique(sector, extent.num_sectors); break; - case LP_TARGET_TYPE_LINEAR: - target = std::make_unique(sector, extent.num_sectors, block_device, + case LP_TARGET_TYPE_LINEAR: { + auto block_device = GetMetadataSuperBlockDevice(metadata); + if (!block_device) { + LOG(ERROR) << "Could not identify the super block device"; + return false; + } + + std::string path; + if (!GetPhysicalPartitionDevicePath(*block_device, &path)) { + LOG(ERROR) << "Unable to complete device-mapper table, unknown block device"; + return false; + } + target = std::make_unique(sector, extent.num_sectors, path, extent.target_data); break; + } default: LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type; return false; @@ -79,13 +105,13 @@ static bool CreateDmTable(const std::string& block_device, const LpMetadata& met return true; } -static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata, - const LpMetadataPartition& partition, bool force_writable, - const std::chrono::milliseconds& timeout_ms, std::string* path) { +static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition, + bool force_writable, const std::chrono::milliseconds& timeout_ms, + std::string* path) { DeviceMapper& dm = DeviceMapper::Instance(); DmTable table; - if (!CreateDmTable(block_device, metadata, partition, &table)) { + if (!CreateDmTable(metadata, partition, &table)) { return false; } if (force_writable) { @@ -122,7 +148,7 @@ bool CreateLogicalPartitions(const std::string& block_device) { continue; } std::string path; - if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, {}, &path)) { + if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) { LERROR << "Could not create logical partition: " << GetPartitionName(partition); return false; } @@ -140,8 +166,8 @@ bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_s } for (const auto& partition : metadata->partitions) { if (GetPartitionName(partition) == partition_name) { - return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable, - timeout_ms, path); + return CreateLogicalPartition(*metadata.get(), partition, force_writable, timeout_ms, + path); } } LERROR << "Could not find any partition with name: " << partition_name; diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index 2c57a35b9..4dd60e98b 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -186,6 +186,7 @@ MetadataBuilder::MetadataBuilder() { header_.partitions.entry_size = sizeof(LpMetadataPartition); header_.extents.entry_size = sizeof(LpMetadataExtent); header_.groups.entry_size = sizeof(LpMetadataPartitionGroup); + header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice); } bool MetadataBuilder::Init(const LpMetadata& metadata) { @@ -198,6 +199,10 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) { } } + for (const auto& block_device : metadata.block_devices) { + block_devices_.push_back(block_device); + } + for (const auto& partition : metadata.partitions) { std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]); Partition* builder = @@ -259,9 +264,7 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata // We reserve a geometry block (4KB) plus space for each copy of the // maximum size of a metadata blob. Then, we double that space since // we store a backup copy of everything. - uint64_t reserved = - LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count); - uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved * 2; + uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count); if (device_info.size < total_reserved) { LERROR << "Attempting to create metadata on a block device that is too small."; return false; @@ -285,12 +288,16 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata return false; } - geometry_.first_logical_sector = first_sector; + block_devices_.push_back(LpMetadataBlockDevice{ + first_sector, + device_info.alignment, + device_info.alignment_offset, + device_info.size, + "super", + }); + geometry_.metadata_max_size = metadata_max_size; geometry_.metadata_slot_count = metadata_slot_count; - geometry_.alignment = device_info.alignment; - geometry_.alignment_offset = device_info.alignment_offset; - geometry_.block_device_size = device_info.size; geometry_.logical_block_size = device_info.logical_block_size; if (!AddGroup("default", 0)) { @@ -408,9 +415,10 @@ auto MetadataBuilder::GetFreeRegions() const -> std::vector { } // Add 0-length intervals for the first and last sectors. This will cause - // ExtentsToFreeList() to treat the space in between as available. - uint64_t last_sector = geometry_.block_device_size / LP_SECTOR_SIZE; - extents.emplace_back(geometry_.first_logical_sector, geometry_.first_logical_sector); + // ExtentToFreeList() to treat the space in between as available. + uint64_t first_sector = super_device().first_logical_sector; + uint64_t last_sector = super_device().size / LP_SECTOR_SIZE; + extents.emplace_back(first_sector, first_sector); extents.emplace_back(last_sector, last_sector); std::sort(extents.begin(), extents.end()); @@ -547,14 +555,18 @@ std::unique_ptr MetadataBuilder::Export() { metadata->partitions.push_back(part); } + metadata->block_devices = block_devices_; + metadata->header.partitions.num_entries = static_cast(metadata->partitions.size()); metadata->header.extents.num_entries = static_cast(metadata->extents.size()); metadata->header.groups.num_entries = static_cast(metadata->groups.size()); + metadata->header.block_devices.num_entries = + static_cast(metadata->block_devices.size()); return metadata; } uint64_t MetadataBuilder::AllocatableSpace() const { - return geometry_.block_device_size - (geometry_.first_logical_sector * LP_SECTOR_SIZE); + return super_device().size - (super_device().first_logical_sector * LP_SECTOR_SIZE); } uint64_t MetadataBuilder::UsedSpace() const { @@ -569,22 +581,22 @@ uint64_t MetadataBuilder::AlignSector(uint64_t sector) const { // Note: when reading alignment info from the Kernel, we don't assume it // is aligned to the sector size, so we round up to the nearest sector. uint64_t lba = sector * LP_SECTOR_SIZE; - uint64_t aligned = AlignTo(lba, geometry_.alignment, geometry_.alignment_offset); + uint64_t aligned = AlignTo(lba, super_device().alignment, super_device().alignment_offset); return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE; } bool MetadataBuilder::GetBlockDeviceInfo(BlockDeviceInfo* info) const { - info->size = geometry_.block_device_size; - info->alignment = geometry_.alignment; - info->alignment_offset = geometry_.alignment_offset; + info->size = super_device().size; + info->alignment = super_device().alignment; + info->alignment_offset = super_device().alignment_offset; info->logical_block_size = geometry_.logical_block_size; return true; } bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) { - if (device_info.size != geometry_.block_device_size) { + if (device_info.size != super_device().size) { LERROR << "Device size does not match (got " << device_info.size << ", expected " - << geometry_.block_device_size << ")"; + << super_device().size << ")"; return false; } if (device_info.logical_block_size != geometry_.logical_block_size) { @@ -596,10 +608,10 @@ bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) // The kernel does not guarantee these values are present, so we only // replace existing values if the new values are non-zero. if (device_info.alignment) { - geometry_.alignment = device_info.alignment; + super_device().alignment = device_info.alignment; } if (device_info.alignment_offset) { - geometry_.alignment_offset = device_info.alignment_offset; + super_device().alignment_offset = device_info.alignment_offset; } return true; } diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp index 27ad25082..c3a5ffe92 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -132,7 +132,9 @@ TEST(liblp, InternalAlignment) { ASSERT_NE(builder, nullptr); unique_ptr exported = builder->Export(); ASSERT_NE(exported, nullptr); - EXPECT_EQ(exported->geometry.first_logical_sector, 1536); + auto super_device = GetMetadataSuperBlockDevice(*exported.get()); + ASSERT_NE(super_device, nullptr); + EXPECT_EQ(super_device->first_logical_sector, 1536); // Test a large alignment offset thrown in. device_info.alignment_offset = 753664; @@ -140,7 +142,9 @@ TEST(liblp, InternalAlignment) { ASSERT_NE(builder, nullptr); exported = builder->Export(); ASSERT_NE(exported, nullptr); - EXPECT_EQ(exported->geometry.first_logical_sector, 1472); + super_device = GetMetadataSuperBlockDevice(*exported.get()); + ASSERT_NE(super_device, nullptr); + EXPECT_EQ(super_device->first_logical_sector, 1472); // Alignment offset without alignment doesn't mean anything. device_info.alignment = 0; @@ -154,7 +158,9 @@ TEST(liblp, InternalAlignment) { ASSERT_NE(builder, nullptr); exported = builder->Export(); ASSERT_NE(exported, nullptr); - EXPECT_EQ(exported->geometry.first_logical_sector, 174); + super_device = GetMetadataSuperBlockDevice(*exported.get()); + ASSERT_NE(super_device, nullptr); + EXPECT_EQ(super_device->first_logical_sector, 174); // Test a small alignment with no alignment offset. device_info.alignment = 11 * 1024; @@ -162,7 +168,9 @@ TEST(liblp, InternalAlignment) { ASSERT_NE(builder, nullptr); exported = builder->Export(); ASSERT_NE(exported, nullptr); - EXPECT_EQ(exported->geometry.first_logical_sector, 160); + super_device = GetMetadataSuperBlockDevice(*exported.get()); + ASSERT_NE(super_device, nullptr); + EXPECT_EQ(super_device->first_logical_sector, 160); } TEST(liblp, InternalPartitionAlignment) { @@ -292,6 +300,9 @@ TEST(liblp, BuilderExport) { unique_ptr exported = builder->Export(); EXPECT_NE(exported, nullptr); + auto super_device = GetMetadataSuperBlockDevice(*exported.get()); + ASSERT_NE(super_device, nullptr); + // Verify geometry. Some details of this may change if we change the // metadata structures. So in addition to checking the exact values, we // also check that they are internally consistent after. @@ -300,11 +311,11 @@ TEST(liblp, BuilderExport) { EXPECT_EQ(geometry.struct_size, sizeof(geometry)); EXPECT_EQ(geometry.metadata_max_size, 1024); EXPECT_EQ(geometry.metadata_slot_count, 2); - EXPECT_EQ(geometry.first_logical_sector, 32); + EXPECT_EQ(super_device->first_logical_sector, 32); static const size_t kMetadataSpace = ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2; - EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace); + EXPECT_GE(super_device->first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace); // Verify header. const LpMetadataHeader& header = exported->header; diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp index dfa37fe77..46bdfa4d9 100644 --- a/fs_mgr/liblp/images.cpp +++ b/fs_mgr/liblp/images.cpp @@ -99,11 +99,12 @@ SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size, block_size_(block_size), file_(nullptr, sparse_file_destroy), images_(images) { + uint64_t total_size = GetTotalSuperPartitionSize(metadata); if (block_size % LP_SECTOR_SIZE != 0) { LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE; return; } - if (metadata.geometry.block_device_size % block_size != 0) { + if (total_size % block_size != 0) { LERROR << "Device size must be a multiple of the block size, " << block_size; return; } @@ -120,7 +121,7 @@ SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size, return; } - uint64_t num_blocks = metadata.geometry.block_device_size % block_size; + uint64_t num_blocks = total_size % block_size; if (num_blocks >= UINT_MAX) { // libsparse counts blocks in unsigned 32-bit integers, so we check to // make sure we're not going to overflow. @@ -128,7 +129,10 @@ SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size, return; } - file_.reset(sparse_file_new(block_size_, geometry_.block_device_size)); + file_.reset(sparse_file_new(block_size_, total_size)); + if (!file_) { + LERROR << "Could not allocate sparse file of size " << total_size; + } } bool SparseBuilder::Export(const char* file) { @@ -333,14 +337,7 @@ int SparseBuilder::OpenImageFile(const std::string& file) { bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size, const std::map& images) { SparseBuilder builder(metadata, block_size, images); - if (!builder.IsValid()) { - LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size; - return false; - } - if (!builder.Build()) { - return false; - } - return builder.Export(file); + return builder.IsValid() && builder.Build() && builder.Export(file); } } // namespace fs_mgr diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h index 7e07df464..6d7324d4d 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -254,10 +254,14 @@ class MetadataBuilder { void ExtentsToFreeList(const std::vector& extents, std::vector* free_regions) const; + const LpMetadataBlockDevice& super_device() const { return block_devices_[0]; } + LpMetadataBlockDevice& super_device() { return block_devices_[0]; } + LpMetadataGeometry geometry_; LpMetadataHeader header_; std::vector> partitions_; std::vector> groups_; + std::vector block_devices_; }; // Read BlockDeviceInfo for a given block device. This always returns false diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h index 5f95dca6d..15fcd43ac 100644 --- a/fs_mgr/liblp/include/liblp/liblp.h +++ b/fs_mgr/liblp/include/liblp/liblp.h @@ -37,6 +37,7 @@ struct LpMetadata { std::vector partitions; std::vector extents; std::vector groups; + std::vector block_devices; }; // Place an initial partition table on the device. This will overwrite the @@ -69,6 +70,14 @@ std::unique_ptr ReadFromImageBlob(const void* data, size_t bytes); // Helper to extract safe C++ strings from partition info. std::string GetPartitionName(const LpMetadataPartition& partition); std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group); +std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device); + +// Return the block device that houses the super partition metadata; returns +// null on failure. +const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata); + +// Return the total size of all partitions comprising the super partition. +uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata); // Helper to return a slot number for a slot suffix. uint32_t SlotNumberForSlotSuffix(const std::string& suffix); diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index 89b219c09..8a309becc 100644 --- a/fs_mgr/liblp/include/liblp/metadata_format.h +++ b/fs_mgr/liblp/include/liblp/metadata_format.h @@ -38,7 +38,7 @@ extern "C" { #define LP_METADATA_HEADER_MAGIC 0x414C5030 /* Current metadata version. */ -#define LP_METADATA_MAJOR_VERSION 6 +#define LP_METADATA_MAJOR_VERSION 7 #define LP_METADATA_MINOR_VERSION 0 /* Attributes for the LpMetadataPartition::attributes field. @@ -103,42 +103,10 @@ typedef struct LpMetadataGeometry { */ uint32_t metadata_slot_count; - /* 48: First usable sector for allocating logical partitions. this will be - * the first sector after the initial geometry blocks, followed by the - * space consumed by metadata_max_size*metadata_slot_count*2. - */ - uint64_t first_logical_sector; - - /* 64: Alignment for defining partitions or partition extents. For example, - * an alignment of 1MiB will require that all partitions have a size evenly - * divisible by 1MiB, and that the smallest unit the partition can grow by - * is 1MiB. - * - * Alignment is normally determined at runtime when growing or adding - * partitions. If for some reason the alignment cannot be determined, then - * this predefined alignment in the geometry is used instead. By default - * it is set to 1MiB. - */ - uint32_t alignment; - - /* 68: Alignment offset for "stacked" devices. For example, if the "super" - * partition itself is not aligned within the parent block device's - * partition table, then we adjust for this in deciding where to place - * |first_logical_sector|. - * - * Similar to |alignment|, this will be derived from the operating system. - * If it cannot be determined, it is assumed to be 0. - */ - uint32_t alignment_offset; - - /* 72: Block device size, as specified when the metadata was created. This - * can be used to verify the geometry against a target device. - */ - uint64_t block_device_size; - - /* 76: Logical block size of the super partition block device. This is the - * minimal alignment for partition and extent sizes, and it must be a - * multiple of LP_SECTOR_SIZE. + /* 48: Logical block size. This is the minimal alignment for partition and + * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that + * this must be equal across all LUNs that comprise the super partition, + * and thus this field is stored in the geometry, not per-device. */ uint32_t logical_block_size; } __attribute__((packed)) LpMetadataGeometry; @@ -217,6 +185,8 @@ typedef struct LpMetadataHeader { LpMetadataTableDescriptor extents; /* 104: Updateable group descriptor. */ LpMetadataTableDescriptor groups; + /* 116: Block device table. */ + LpMetadataTableDescriptor block_devices; } __attribute__((packed)) LpMetadataHeader; /* This struct defines a logical partition entry, similar to what would be @@ -285,6 +255,47 @@ typedef struct LpMetadataPartitionGroup { uint64_t maximum_size; } LpMetadataPartitionGroup; +/* This struct defines an entry in the block_devices table. There must be + * exactly one device, corresponding to the super partition. + */ +typedef struct LpMetadataBlockDevice { + /* 0: First usable sector for allocating logical partitions. this will be + * the first sector after the initial geometry blocks, followed by the + * space consumed by metadata_max_size*metadata_slot_count*2. + */ + uint64_t first_logical_sector; + + /* 8: Alignment for defining partitions or partition extents. For example, + * an alignment of 1MiB will require that all partitions have a size evenly + * divisible by 1MiB, and that the smallest unit the partition can grow by + * is 1MiB. + * + * Alignment is normally determined at runtime when growing or adding + * partitions. If for some reason the alignment cannot be determined, then + * this predefined alignment in the geometry is used instead. By default + * it is set to 1MiB. + */ + uint32_t alignment; + + /* 12: Alignment offset for "stacked" devices. For example, if the "super" + * partition itself is not aligned within the parent block device's + * partition table, then we adjust for this in deciding where to place + * |first_logical_sector|. + * + * Similar to |alignment|, this will be derived from the operating system. + * If it cannot be determined, it is assumed to be 0. + */ + uint32_t alignment_offset; + + /* 16: Block device size, as specified when the metadata was created. This + * can be used to verify the geometry against a target device. + */ + uint64_t size; + + /* 24: Partition name in the GPT. Any unused characters must be 0. */ + char partition_name[36]; +} LpMetadataBlockDevice; + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index 2aa41f32e..3889e87f2 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -160,7 +160,6 @@ TEST(liblp, FlashAndReadback) { // Check geometry and header. EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size); EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count); - EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector); EXPECT_EQ(exported->header.major_version, imported->header.major_version); EXPECT_EQ(exported->header.minor_version, imported->header.minor_version); EXPECT_EQ(exported->header.header_size, imported->header.header_size); @@ -178,6 +177,11 @@ TEST(liblp, FlashAndReadback) { EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors); EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type); EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data); + + // Check block devices table. + ASSERT_EQ(exported->block_devices.size(), imported->block_devices.size()); + EXPECT_EQ(exported->block_devices[0].first_logical_sector, + imported->block_devices[0].first_logical_sector); } // Test that we can update metadata slots without disturbing others. @@ -206,14 +210,17 @@ TEST(liblp, UpdateAnyMetadataSlot) { ASSERT_EQ(imported->partitions.size(), 1); EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor"); - uint64_t last_sector = imported->geometry.block_device_size / LP_SECTOR_SIZE; + auto super_device = GetMetadataSuperBlockDevice(*imported.get()); + ASSERT_NE(super_device, nullptr); + + uint64_t last_sector = super_device->size / LP_SECTOR_SIZE; // Verify that we didn't overwrite anything in the logical paritition area. // We expect the disk to be filled with 0xcc on creation so we can read // this back and compare it. char expected[LP_SECTOR_SIZE]; memset(expected, 0xcc, sizeof(expected)); - for (uint64_t i = imported->geometry.first_logical_sector; i < last_sector; i++) { + for (uint64_t i = super_device->first_logical_sector; i < last_sector; i++) { char buffer[LP_SECTOR_SIZE]; ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0); ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer))); @@ -256,7 +263,8 @@ TEST(liblp, NoChangingGeometry) { imported = ReadMetadata(fd, 0); ASSERT_NE(imported, nullptr); - imported->geometry.first_logical_sector++; + ASSERT_EQ(imported->block_devices.size(), 1); + imported->block_devices[0].first_logical_sector++; ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1)); imported = ReadMetadata(fd, 0); @@ -329,9 +337,11 @@ TEST(liblp, TooManyPartitions) { ASSERT_NE(builder, nullptr); // Compute the maximum number of partitions we can fit in 512 bytes of - // metadata. By default there is the header, and one partition group. - static const size_t kMaxPartitionTableSize = - kMetadataSize - sizeof(LpMetadataHeader) - sizeof(LpMetadataPartitionGroup); + // metadata. By default there is the header, one partition group, and a + // block device entry. + static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) - + sizeof(LpMetadataPartitionGroup) - + sizeof(LpMetadataBlockDevice); size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition); // Add this number of partitions. @@ -360,12 +370,15 @@ TEST(liblp, TooManyPartitions) { // The new table should be too large to be written. ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1)); + auto super_device = GetMetadataSuperBlockDevice(*exported.get()); + ASSERT_NE(super_device, nullptr); + // Check that the first and last logical sectors weren't touched when we // wrote this almost-full metadata. char expected[LP_SECTOR_SIZE]; memset(expected, 0xcc, sizeof(expected)); char buffer[LP_SECTOR_SIZE]; - ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0); + ASSERT_GE(lseek(fd, super_device->first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0); ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer))); EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0); } diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index 43d807677..c34b1382d 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -108,15 +108,6 @@ bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) { LERROR << "Metadata max size is not sector-aligned."; return false; } - - // Check that the metadata area and logical partition areas don't overlap. - int64_t end_of_metadata = - GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) + - geometry->metadata_max_size; - if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) { - LERROR << "Logical partition metadata overlaps with logical partition contents."; - return false; - } return true; } @@ -195,7 +186,8 @@ static bool ValidateMetadataHeader(const LpMetadataHeader& header) { } if (!ValidateTableBounds(header, header.partitions) || !ValidateTableBounds(header, header.extents) || - !ValidateTableBounds(header, header.groups)) { + !ValidateTableBounds(header, header.groups) || + !ValidateTableBounds(header, header.block_devices)) { LERROR << "Logical partition metadata has invalid table bounds."; return false; } @@ -294,6 +286,28 @@ static std::unique_ptr ParseMetadata(const LpMetadataGeometry& geome metadata->groups.push_back(group); } + cursor = buffer.get() + header.block_devices.offset; + for (size_t i = 0; i < header.block_devices.num_entries; i++) { + LpMetadataBlockDevice device = {}; + memcpy(&device, cursor, sizeof(device)); + cursor += header.block_devices.entry_size; + + metadata->block_devices.push_back(device); + } + + const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get()); + if (!super_device) { + LERROR << "Metadata does not specify a super device."; + return nullptr; + } + + // Check that the metadata area and logical partition areas don't overlap. + uint64_t metadata_region = + GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count); + if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) { + LERROR << "Logical partition metadata overlaps with logical partition contents."; + return nullptr; + } return metadata; } @@ -374,5 +388,9 @@ std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) { return NameFromFixedArray(group.name, sizeof(group.name)); } +std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) { + return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name)); +} + } // namespace fs_mgr } // namespace android diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp index 0556833ff..518920d26 100644 --- a/fs_mgr/liblp/utility.cpp +++ b/fs_mgr/liblp/utility.cpp @@ -66,11 +66,8 @@ int64_t GetBackupGeometryOffset() { int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) { CHECK(slot_number < geometry.metadata_slot_count); - int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) + geometry.metadata_max_size * slot_number; - CHECK(offset + geometry.metadata_max_size <= - int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE)); return offset; } @@ -81,6 +78,18 @@ int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slo return start + int64_t(geometry.metadata_max_size * slot_number); } +uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) { + return LP_PARTITION_RESERVED_BYTES + + (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2; +} + +const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) { + if (metadata.block_devices.empty()) { + return nullptr; + } + return &metadata.block_devices[0]; +} + void SHA256(const void* data, size_t length, uint8_t out[32]) { SHA256_CTX c; SHA256_Init(&c); @@ -100,5 +109,13 @@ uint32_t SlotNumberForSlotSuffix(const std::string& suffix) { return suffix[1] - 'a'; } +uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) { + uint64_t size = 0; + for (const auto& block_device : metadata.block_devices) { + size += block_device.size; + } + return size; +} + } // namespace fs_mgr } // namespace android diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h index 61e7d316c..65e643b70 100644 --- a/fs_mgr/liblp/utility.h +++ b/fs_mgr/liblp/utility.h @@ -23,7 +23,7 @@ #include -#include "liblp/metadata_format.h" +#include "liblp/liblp.h" #define LP_TAG "[liblp]" #define LWARN LOG(WARNING) << LP_TAG @@ -50,6 +50,10 @@ int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t sl // device. int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number); +// Return the total space at the start of the super partition that must be set +// aside from headers/metadata and backups. +uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots); + // Cross-platform helper for lseek64(). int64_t SeekFile64(int fd, int64_t offset, int whence); diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp index 8baf9e730..bdf6dfda9 100644 --- a/fs_mgr/liblp/utility_test.cpp +++ b/fs_mgr/liblp/utility_test.cpp @@ -36,10 +36,6 @@ TEST(liblp, GetMetadataOffset) { {0}, 16384, 4, - 10000, - 0, - 0, - 1024 * 1024, 4096}; static const uint64_t start = LP_PARTITION_RESERVED_BYTES; EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192); diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp index f857d8cc4..ddae8420a 100644 --- a/fs_mgr/liblp/writer.cpp +++ b/fs_mgr/liblp/writer.cpp @@ -43,9 +43,7 @@ std::string SerializeGeometry(const LpMetadataGeometry& input) { static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) { return g1.metadata_max_size == g2.metadata_max_size && g1.metadata_slot_count == g2.metadata_slot_count && - g1.block_device_size == g2.block_device_size && - g1.logical_block_size == g2.logical_block_size && - g1.first_logical_sector == g2.first_logical_sector; + g1.logical_block_size == g2.logical_block_size; } std::string SerializeMetadata(const LpMetadata& input) { @@ -59,15 +57,18 @@ std::string SerializeMetadata(const LpMetadata& input) { metadata.extents.size() * sizeof(LpMetadataExtent)); std::string groups(reinterpret_cast(metadata.groups.data()), metadata.groups.size() * sizeof(LpMetadataPartitionGroup)); + std::string block_devices(reinterpret_cast(metadata.block_devices.data()), + metadata.block_devices.size() * sizeof(LpMetadataBlockDevice)); // Compute positions of tables. header.partitions.offset = 0; header.extents.offset = header.partitions.offset + partitions.size(); header.groups.offset = header.extents.offset + extents.size(); - header.tables_size = header.groups.offset + groups.size(); + header.block_devices.offset = header.groups.offset + groups.size(); + header.tables_size = header.block_devices.offset + block_devices.size(); // Compute payload checksum. - std::string tables = partitions + extents + groups; + std::string tables = partitions + extents + groups + block_devices; SHA256(tables.data(), tables.size(), header.tables_checksum); // Compute header checksum. @@ -105,14 +106,20 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count; uint64_t total_reserved = reserved_size * 2; + const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata); + if (!super_device) { + LERROR << "Logical partition metadata does not have a super block device."; + return false; + } + if (total_reserved > blockdevice_size || - total_reserved > geometry.first_logical_sector * LP_SECTOR_SIZE) { + total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) { LERROR << "Not enough space to store all logical partition metadata slots."; return false; } - if (blockdevice_size != metadata.geometry.block_device_size) { + if (blockdevice_size != super_device->size) { LERROR << "Block device size " << blockdevice_size - << " does not match metadata requested size " << metadata.geometry.block_device_size; + << " does not match metadata requested size " << super_device->size; return false; } @@ -125,11 +132,11 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std } // Make sure all linear extents have a valid range. - uint64_t last_sector = geometry.block_device_size / LP_SECTOR_SIZE; + uint64_t last_sector = super_device->size / LP_SECTOR_SIZE; for (const auto& extent : metadata.extents) { if (extent.target_type == LP_TARGET_TYPE_LINEAR) { uint64_t physical_sector = extent.target_data; - if (physical_sector < geometry.first_logical_sector || + if (physical_sector < super_device->first_logical_sector || physical_sector + extent.num_sectors > last_sector) { LERROR << "Extent table entry is out of bounds."; return false; @@ -139,10 +146,28 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std return true; } -static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, +// Check that the given region is within metadata bounds. +static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) { + const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata); + if (!super_device) { + LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata"; + return false; + } + if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) { + LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start + << " overlaps with logical partition contents"; + return false; + } + return true; +} + +static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number, const std::string& blob, const std::function& writer) { - int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number); + int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number); + if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) { + return false; + } if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset; return false; @@ -154,18 +179,15 @@ static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uin return true; } -static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, +static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number, const std::string& blob, const std::function& writer) { - int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number); - int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_SET); - if (abs_offset == (int64_t)-1) { - PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset; + int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number); + if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) { return false; } - if (abs_offset >= int64_t(geometry.first_logical_sector) * LP_SECTOR_SIZE) { - PERROR << __PRETTY_FUNCTION__ << " backup offset " << abs_offset - << " is within logical partition bounds, sector " << geometry.first_logical_sector; + if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) { + PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset; return false; } if (!writer(fd, blob)) { @@ -175,18 +197,18 @@ static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint return true; } -static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, +static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number, const std::string& blob, const std::function& writer) { // Make sure we're writing to a valid metadata slot. - if (slot_number >= geometry.metadata_slot_count) { + if (slot_number >= metadata.geometry.metadata_slot_count) { LERROR << "Invalid logical partition metadata slot number."; return false; } - if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) { + if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) { return false; } - if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) { + if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) { return false; } return true; @@ -237,7 +259,7 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata) { bool ok = true; for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) { - ok &= WriteMetadata(fd, metadata.geometry, i, metadata_blob, DefaultWriter); + ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter); } return ok; } @@ -289,7 +311,7 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb LERROR << "Error serializing primary metadata to repair corrupted backup"; return false; } - if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) { + if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) { LERROR << "Error writing primary metadata to repair corrupted backup"; return false; } @@ -301,14 +323,14 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb LERROR << "Error serializing primary metadata to repair corrupted backup"; return false; } - if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) { + if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) { LERROR << "Error writing primary metadata to repair corrupted backup"; return false; } } // Both copies should now be in sync, so we can continue the update. - return WriteMetadata(fd, geometry, slot_number, blob, writer); + return WriteMetadata(fd, metadata, slot_number, blob, writer); } bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {