From ef9740c67a7e373ea9bc8b2ba6481b67ae96a0cc Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 23 Oct 2018 14:43:10 -0700 Subject: [PATCH] liblp: Allow the super partition to span multiple block devices. This patch allows the block device table in LpMetadataHeader to contain additional partitions. MetadataBuilder can now resize partitions such that are allocated across block devices as needed, however, it attempts to minimize this by grouping free regions by device. Bug: 116802789 Test: liblp_test gtests device with super partition flashes and boots Change-Id: I9cf74c8925faf154703eeba2a26546a152efcaa2 --- fs_mgr/fs_mgr_dm_linear.cpp | 9 +- fs_mgr/liblp/builder.cpp | 298 ++++++++++++------ fs_mgr/liblp/builder_test.cpp | 92 ++++-- fs_mgr/liblp/include/liblp/builder.h | 60 ++-- fs_mgr/liblp/include/liblp/metadata_format.h | 14 +- fs_mgr/liblp/include/liblp/partition_opener.h | 10 +- fs_mgr/liblp/io_test.cpp | 4 +- fs_mgr/liblp/partition_opener.cpp | 3 + fs_mgr/liblp/reader.cpp | 6 + 9 files changed, 350 insertions(+), 146 deletions(-) diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp index 6ddd5a895..4dacebfb0 100644 --- a/fs_mgr/fs_mgr_dm_linear.cpp +++ b/fs_mgr/fs_mgr_dm_linear.cpp @@ -75,14 +75,9 @@ static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& target = std::make_unique(sector, extent.num_sectors); break; case LP_TARGET_TYPE_LINEAR: { - auto block_device = GetMetadataSuperBlockDevice(metadata); - if (!block_device) { - LOG(ERROR) << "Could not identify the super block device"; - return false; - } - + const auto& block_device = metadata.block_devices[extent.target_source]; std::string path; - if (!GetPhysicalPartitionDevicePath(*block_device, &path)) { + if (!GetPhysicalPartitionDevicePath(block_device, &path)) { LOG(ERROR) << "Unable to complete device-mapper table, unknown block device"; return false; } diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index 1b8ed5776..3cd33b175 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -29,12 +29,19 @@ namespace android { namespace fs_mgr { -void LinearExtent::AddTo(LpMetadata* out) const { - out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_}); +bool LinearExtent::AddTo(LpMetadata* out) const { + if (device_index_ >= out->block_devices.size()) { + LERROR << "Extent references unknown block device."; + return false; + } + out->extents.emplace_back( + LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_}); + return true; } -void ZeroExtent::AddTo(LpMetadata* out) const { - out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0}); +bool ZeroExtent::AddTo(LpMetadata* out) const { + out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0}); + return true; } Partition::Partition(const std::string& name, const std::string& group_name, uint32_t attributes) @@ -44,15 +51,17 @@ void Partition::AddExtent(std::unique_ptr&& extent) { size_ += extent->num_sectors() * LP_SECTOR_SIZE; if (LinearExtent* new_extent = extent->AsLinearExtent()) { - if (!extents_.empty() && extents_.back()->AsLinearExtent() && - extents_.back()->AsLinearExtent()->end_sector() == new_extent->physical_sector()) { - // If the previous extent can be merged into this new one, do so - // to avoid creating unnecessary extents. + if (!extents_.empty() && extents_.back()->AsLinearExtent()) { LinearExtent* prev_extent = extents_.back()->AsLinearExtent(); - extent = std::make_unique( - prev_extent->num_sectors() + new_extent->num_sectors(), - prev_extent->physical_sector()); - extents_.pop_back(); + if (prev_extent->end_sector() == new_extent->physical_sector() && + prev_extent->device_index() == new_extent->device_index()) { + // If the previous extent can be merged into this new one, do so + // to avoid creating unnecessary extents. + extent = std::make_unique( + prev_extent->num_sectors() + new_extent->num_sectors(), + prev_extent->device_index(), prev_extent->physical_sector()); + extents_.pop_back(); + } } } extents_.push_back(std::move(extent)); @@ -108,9 +117,12 @@ std::unique_ptr MetadataBuilder::New(const IPartitionOpener& op if (!builder) { return nullptr; } - BlockDeviceInfo device_info; - if (opener.GetInfo(super_partition, &device_info)) { - builder->UpdateBlockDeviceInfo(device_info); + for (size_t i = 0; i < builder->block_devices_.size(); i++) { + std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]); + BlockDeviceInfo device_info; + if (opener.GetInfo(partition_name, &device_info)) { + builder->UpdateBlockDeviceInfo(i, device_info); + } } return builder; } @@ -120,11 +132,11 @@ std::unique_ptr MetadataBuilder::New(const std::string& super_p return New(PartitionOpener(), super_partition, slot_number); } -std::unique_ptr MetadataBuilder::New(const BlockDeviceInfo& device_info, - uint32_t metadata_max_size, - uint32_t metadata_slot_count) { +std::unique_ptr MetadataBuilder::New( + const std::vector& block_devices, const std::string& super_partition, + uint32_t metadata_max_size, uint32_t metadata_slot_count) { std::unique_ptr builder(new MetadataBuilder()); - if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) { + if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) { return nullptr; } return builder; @@ -156,6 +168,7 @@ MetadataBuilder::MetadataBuilder() { bool MetadataBuilder::Init(const LpMetadata& metadata) { geometry_ = metadata.geometry; + block_devices_ = metadata.block_devices; for (const auto& group : metadata.groups) { std::string group_name = GetPartitionGroupName(group); @@ -164,10 +177,6 @@ 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 = @@ -179,7 +188,8 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) { for (size_t i = 0; i < partition.num_extents; i++) { const LpMetadataExtent& extent = metadata.extents[partition.first_extent_index + i]; if (extent.target_type == LP_TARGET_TYPE_LINEAR) { - auto copy = std::make_unique(extent.num_sectors, extent.target_data); + auto copy = std::make_unique(extent.num_sectors, extent.target_source, + extent.target_data); builder->AddExtent(std::move(copy)); } else if (extent.target_type == LP_TARGET_TYPE_ZERO) { auto copy = std::make_unique(extent.num_sectors); @@ -190,7 +200,37 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) { return true; } -bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size, +static bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) { + if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) { + LERROR << "Block device " << device_info.partition_name + << " logical block size must be a multiple of 512."; + return false; + } + if (device_info.size % device_info.logical_block_size != 0) { + LERROR << "Block device " << device_info.partition_name + << " size must be a multiple of its block size."; + return false; + } + if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) { + LERROR << "Block device " << device_info.partition_name + << " alignment offset is not sector-aligned."; + return false; + } + if (device_info.alignment % LP_SECTOR_SIZE != 0) { + LERROR << "Block device " << device_info.partition_name + << " partition alignment is not sector-aligned."; + return false; + } + if (device_info.alignment_offset > device_info.alignment) { + LERROR << "Block device " << device_info.partition_name + << " partition alignment offset is greater than its alignment."; + return false; + } + return true; +} + +bool MetadataBuilder::Init(const std::vector& block_devices, + const std::string& super_partition, uint32_t metadata_max_size, uint32_t metadata_slot_count) { if (metadata_max_size < sizeof(LpMetadataHeader)) { LERROR << "Invalid metadata maximum size."; @@ -200,70 +240,102 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata LERROR << "Invalid metadata slot count."; return false; } + if (block_devices.empty()) { + LERROR << "No block devices were specified."; + return false; + } // Align the metadata size up to the nearest sector. metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE); - // Check that device properties are sane. - if (device_info.size % LP_SECTOR_SIZE != 0) { - LERROR << "Block device size must be a multiple of 512."; - return false; - } - if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) { - LERROR << "Logical block size must be a multiple of 512."; - return false; - } - if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) { - LERROR << "Alignment offset is not sector-aligned."; - return false; - } - if (device_info.alignment % LP_SECTOR_SIZE != 0) { - LERROR << "Partition alignment is not sector-aligned."; - return false; - } - if (device_info.alignment_offset > device_info.alignment) { - LERROR << "Partition alignment offset is greater than its alignment."; + // Validate and build the block device list. + uint32_t logical_block_size = 0; + for (const auto& device_info : block_devices) { + if (!VerifyDeviceProperties(device_info)) { + return false; + } + + if (!logical_block_size) { + logical_block_size = device_info.logical_block_size; + } + if (logical_block_size != device_info.logical_block_size) { + LERROR << "All partitions must have the same logical block size."; + return false; + } + + LpMetadataBlockDevice out = {}; + out.alignment = device_info.alignment; + out.alignment_offset = device_info.alignment_offset; + out.size = device_info.size; + if (device_info.partition_name.size() >= sizeof(out.partition_name)) { + LERROR << "Partition name " << device_info.partition_name << " exceeds maximum length."; + return false; + } + strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name)); + + // In the case of the super partition, this field will be adjusted + // later. For all partitions, the first 512 bytes are considered + // untouched to be compatible code that looks for an MBR. Thus we + // start counting free sectors at sector 1, not 0. + uint64_t free_area_start = LP_SECTOR_SIZE; + if (out.alignment || out.alignment_offset) { + free_area_start = AlignTo(free_area_start, out.alignment, out.alignment_offset); + } else { + free_area_start = AlignTo(free_area_start, logical_block_size); + } + out.first_logical_sector = free_area_start / LP_SECTOR_SIZE; + + // There must be one logical block of space available. + uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size; + if (device_info.size < minimum_size) { + LERROR << "Block device " << device_info.partition_name + << " is too small to hold any logical partitions."; + return false; + } + + // The "root" of the super partition is always listed first. + if (device_info.partition_name == super_partition) { + block_devices_.emplace(block_devices_.begin(), out); + } else { + block_devices_.emplace_back(out); + } + } + if (GetBlockDevicePartitionName(block_devices_[0]) != super_partition) { + LERROR << "No super partition was specified."; return false; } + LpMetadataBlockDevice& super = block_devices_[0]; + // 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 total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count); - if (device_info.size < total_reserved) { + if (super.size < total_reserved) { LERROR << "Attempting to create metadata on a block device that is too small."; return false; } // Compute the first free sector, factoring in alignment. uint64_t free_area_start = total_reserved; - if (device_info.alignment || device_info.alignment_offset) { - free_area_start = - AlignTo(free_area_start, device_info.alignment, device_info.alignment_offset); + if (super.alignment || super.alignment_offset) { + free_area_start = AlignTo(free_area_start, super.alignment, super.alignment_offset); } else { - free_area_start = AlignTo(free_area_start, device_info.logical_block_size); + free_area_start = AlignTo(free_area_start, logical_block_size); } - uint64_t first_sector = free_area_start / LP_SECTOR_SIZE; + super.first_logical_sector = free_area_start / LP_SECTOR_SIZE; // There must be one logical block of free space remaining (enough for one partition). - uint64_t minimum_disk_size = (first_sector * LP_SECTOR_SIZE) + device_info.logical_block_size; - if (device_info.size < minimum_disk_size) { + uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size; + if (super.size < minimum_disk_size) { LERROR << "Device must be at least " << minimum_disk_size << " bytes, only has " - << device_info.size; + << super.size; return false; } - 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_.logical_block_size = device_info.logical_block_size; + geometry_.logical_block_size = logical_block_size; if (!AddGroup("default", 0)) { return false; @@ -347,8 +419,9 @@ void MetadataBuilder::ExtentsToFreeList(const std::vector& extents, for (size_t i = 1; i < extents.size(); i++) { const Interval& previous = extents[i - 1]; const Interval& current = extents[i]; + DCHECK(previous.device_index == current.device_index); - uint64_t aligned = AlignSector(previous.end); + uint64_t aligned = AlignSector(block_devices_[current.device_index], previous.end); if (aligned >= current.start) { // There is no gap between these two extents, try the next one. // Note that we check with >= instead of >, since alignment may @@ -358,37 +431,43 @@ void MetadataBuilder::ExtentsToFreeList(const std::vector& extents, // The new interval represents the free space starting at the end of // the previous interval, and ending at the start of the next interval. - free_regions->emplace_back(aligned, current.start); + free_regions->emplace_back(current.device_index, aligned, current.start); } } auto MetadataBuilder::GetFreeRegions() const -> std::vector { std::vector free_regions; - // Collect all extents in the partition table, then sort them by starting - // sector. - std::vector extents; + // Collect all extents in the partition table, per-device, then sort them + // by starting sector. + std::vector> device_extents(block_devices_.size()); for (const auto& partition : partitions_) { for (const auto& extent : partition->extents()) { LinearExtent* linear = extent->AsLinearExtent(); if (!linear) { continue; } - extents.emplace_back(linear->physical_sector(), + CHECK(linear->device_index() < device_extents.size()); + auto& extents = device_extents[linear->device_index()]; + extents.emplace_back(linear->device_index(), linear->physical_sector(), linear->physical_sector() + extent->num_sectors()); } } // Add 0-length intervals for the first and last sectors. This will cause // 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); + for (size_t i = 0; i < device_extents.size(); i++) { + auto& extents = device_extents[i]; + const auto& block_device = block_devices_[i]; - std::sort(extents.begin(), extents.end()); + uint64_t first_sector = block_device.first_logical_sector; + uint64_t last_sector = block_device.size / LP_SECTOR_SIZE; + extents.emplace_back(i, first_sector, first_sector); + extents.emplace_back(i, last_sector, last_sector); - ExtentsToFreeList(extents, &free_regions); + std::sort(extents.begin(), extents.end()); + ExtentsToFreeList(extents, &free_regions); + } return free_regions; } @@ -443,7 +522,7 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) uint64_t sectors = std::min(sectors_needed, region.length()); CHECK(sectors % sectors_per_block == 0); - auto extent = std::make_unique(sectors, region.start); + auto extent = std::make_unique(sectors, region.device_index, region.start); new_extents.push_back(std::move(extent)); sectors_needed -= sectors; if (!sectors_needed) { @@ -471,6 +550,9 @@ std::unique_ptr MetadataBuilder::Export() { metadata->header = header_; metadata->geometry = geometry_; + // Assign this early so the extent table can read it. + metadata->block_devices = block_devices_; + std::map group_indices; for (const auto& group : groups_) { LpMetadataPartitionGroup out = {}; @@ -515,13 +597,13 @@ std::unique_ptr MetadataBuilder::Export() { part.group_index = iter->second; for (const auto& extent : partition->extents()) { - extent->AddTo(metadata.get()); + if (!extent->AddTo(metadata.get())) { + return nullptr; + } } 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()); @@ -531,7 +613,11 @@ std::unique_ptr MetadataBuilder::Export() { } uint64_t MetadataBuilder::AllocatableSpace() const { - return super_device().size - (super_device().first_logical_sector * LP_SECTOR_SIZE); + uint64_t total_size = 0; + for (const auto& block_device : block_devices_) { + total_size += block_device.size - (block_device.first_logical_sector * LP_SECTOR_SIZE); + } + return total_size; } uint64_t MetadataBuilder::UsedSpace() const { @@ -542,26 +628,58 @@ uint64_t MetadataBuilder::UsedSpace() const { return size; } -uint64_t MetadataBuilder::AlignSector(uint64_t sector) const { +uint64_t MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, + 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, super_device().alignment, super_device().alignment_offset); + uint64_t aligned = AlignTo(lba, block_device.alignment, block_device.alignment_offset); return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE; } -bool MetadataBuilder::GetBlockDeviceInfo(BlockDeviceInfo* info) const { - info->size = super_device().size; - info->alignment = super_device().alignment; - info->alignment_offset = super_device().alignment_offset; +bool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name, + uint32_t* index) const { + for (size_t i = 0; i < block_devices_.size(); i++) { + if (GetBlockDevicePartitionName(block_devices_[i]) == partition_name) { + *index = i; + return true; + } + } + return false; +} + +bool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name, + BlockDeviceInfo* info) const { + uint32_t index; + if (!FindBlockDeviceByName(partition_name, &index)) { + LERROR << "No device named " << partition_name; + return false; + } + info->size = block_devices_[index].size; + info->alignment = block_devices_[index].alignment; + info->alignment_offset = block_devices_[index].alignment_offset; info->logical_block_size = geometry_.logical_block_size; + info->partition_name = partition_name; return true; } -bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) { - if (device_info.size != super_device().size) { +bool MetadataBuilder::UpdateBlockDeviceInfo(const std::string& partition_name, + const BlockDeviceInfo& device_info) { + uint32_t index; + if (!FindBlockDeviceByName(partition_name, &index)) { + LERROR << "No device named " << partition_name; + return false; + } + return UpdateBlockDeviceInfo(index, device_info); +} + +bool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) { + CHECK(index < block_devices_.size()); + + LpMetadataBlockDevice& block_device = block_devices_[index]; + if (device_info.size != block_device.size) { LERROR << "Device size does not match (got " << device_info.size << ", expected " - << super_device().size << ")"; + << block_device.size << ")"; return false; } if (device_info.logical_block_size != geometry_.logical_block_size) { @@ -573,10 +691,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) { - super_device().alignment = device_info.alignment; + block_device.alignment = device_info.alignment; } if (device_info.alignment_offset) { - super_device().alignment_offset = device_info.alignment_offset; + block_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 c02242ab2..c27e30025 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -27,6 +27,7 @@ using ::testing::ElementsAre; TEST(liblp, BuildBasic) { unique_ptr builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + ASSERT_NE(builder, nullptr); Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY); ASSERT_NE(partition, nullptr); @@ -41,6 +42,7 @@ TEST(liblp, BuildBasic) { TEST(liblp, ResizePartition) { unique_ptr builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + ASSERT_NE(builder, nullptr); Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY); ASSERT_NE(system, nullptr); @@ -94,6 +96,7 @@ TEST(liblp, ResizePartition) { TEST(liblp, PartitionAlignment) { unique_ptr builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + ASSERT_NE(builder, nullptr); // Test that we align up to one sector. Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY); @@ -120,6 +123,7 @@ TEST(liblp, DiskAlignment) { TEST(liblp, MetadataAlignment) { // Make sure metadata sizes get aligned up. unique_ptr builder = MetadataBuilder::New(1024 * 1024, 1000, 2); + ASSERT_NE(builder, nullptr); unique_ptr exported = builder->Export(); ASSERT_NE(exported, nullptr); EXPECT_EQ(exported->geometry.metadata_max_size, 1024); @@ -127,7 +131,7 @@ TEST(liblp, MetadataAlignment) { TEST(liblp, InternalAlignment) { // Test the metadata fitting within alignment. - BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0, 4096); + BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 1024, 2); ASSERT_NE(builder, nullptr); unique_ptr exported = builder->Export(); @@ -174,7 +178,7 @@ TEST(liblp, InternalAlignment) { } TEST(liblp, InternalPartitionAlignment) { - BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664, 4096); + BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 32 * 1024, 2); Partition* a = builder->AddPartition("a", 0); @@ -394,7 +398,7 @@ TEST(liblp, MetadataTooLarge) { static const size_t kMetadataSize = 64 * 1024; // No space to store metadata + geometry. - BlockDeviceInfo device_info(kDiskSize, 0, 0, 4096); + BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 4096); unique_ptr builder = MetadataBuilder::New(device_info, kMetadataSize, 1); EXPECT_EQ(builder, nullptr); @@ -441,12 +445,12 @@ TEST(liblp, block_device_info) { } TEST(liblp, UpdateBlockDeviceInfo) { - BlockDeviceInfo device_info(1024 * 1024, 4096, 1024, 4096); + BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 1024, 1); ASSERT_NE(builder, nullptr); BlockDeviceInfo new_info; - ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info)); EXPECT_EQ(new_info.size, device_info.size); EXPECT_EQ(new_info.alignment, device_info.alignment); @@ -455,37 +459,37 @@ TEST(liblp, UpdateBlockDeviceInfo) { device_info.alignment = 0; device_info.alignment_offset = 2048; - ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info)); - ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info)); EXPECT_EQ(new_info.alignment, 4096); EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset); device_info.alignment = 8192; device_info.alignment_offset = 0; - ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info)); - ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + ASSERT_TRUE(builder->UpdateBlockDeviceInfo("super", device_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info)); EXPECT_EQ(new_info.alignment, 8192); EXPECT_EQ(new_info.alignment_offset, 2048); new_info.size += 4096; - ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info)); - ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info)); EXPECT_EQ(new_info.size, 1024 * 1024); new_info.logical_block_size = 512; - ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info)); - ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + ASSERT_FALSE(builder->UpdateBlockDeviceInfo("super", new_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo("super", &new_info)); EXPECT_EQ(new_info.logical_block_size, 4096); } TEST(liblp, InvalidBlockSize) { - BlockDeviceInfo device_info(1024 * 1024, 0, 0, 513); + BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513); unique_ptr builder = MetadataBuilder::New(device_info, 1024, 1); EXPECT_EQ(builder, nullptr); } TEST(liblp, AlignedExtentSize) { - BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096); + BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 1024, 1); ASSERT_NE(builder, nullptr); @@ -497,13 +501,13 @@ TEST(liblp, AlignedExtentSize) { TEST(liblp, AlignedFreeSpace) { // Only one sector free - at least one block is required. - BlockDeviceInfo device_info(10240, 0, 0, 4096); + BlockDeviceInfo device_info("super", 10240, 0, 0, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 512, 1); ASSERT_EQ(builder, nullptr); } TEST(liblp, HasDefaultGroup) { - BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096); + BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 1024, 1); ASSERT_NE(builder, nullptr); @@ -511,7 +515,7 @@ TEST(liblp, HasDefaultGroup) { } TEST(liblp, GroupSizeLimits) { - BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096); + BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 1024, 1); ASSERT_NE(builder, nullptr); @@ -530,6 +534,9 @@ TEST(liblp, GroupSizeLimits) { constexpr unsigned long long operator"" _GiB(unsigned long long x) { // NOLINT return x << 30; } +constexpr unsigned long long operator"" _MiB(unsigned long long x) { // NOLINT + return x << 20; +} TEST(liblp, RemoveAndAddFirstPartition) { auto builder = MetadataBuilder::New(10_GiB, 65536, 2); @@ -555,7 +562,7 @@ TEST(liblp, RemoveAndAddFirstPartition) { } TEST(liblp, ListGroups) { - BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096); + BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 1024, 1); ASSERT_NE(builder, nullptr); ASSERT_TRUE(builder->AddGroup("example", 0)); @@ -565,7 +572,7 @@ TEST(liblp, ListGroups) { } TEST(liblp, RemoveGroupAndPartitions) { - BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096); + BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096); unique_ptr builder = MetadataBuilder::New(device_info, 1024, 1); ASSERT_NE(builder, nullptr); ASSERT_TRUE(builder->AddGroup("example", 0)); @@ -580,3 +587,48 @@ TEST(liblp, RemoveGroupAndPartitions) { builder->RemoveGroupAndPartitions("default"); ASSERT_NE(builder->FindPartition("system"), nullptr); } + +TEST(liblp, MultipleBlockDevices) { + std::vector partitions = { + BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096), + BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096), + BlockDeviceInfo("product_a", 64_MiB, 786432, 753664, 4096), + }; + unique_ptr builder = MetadataBuilder::New(partitions, "system_a", 65536, 2); + ASSERT_NE(builder, nullptr); + EXPECT_EQ(builder->AllocatableSpace(), 467238912); + + // Create a partition that spans 3 devices. + Partition* p = builder->AddPartition("system_a", 0); + ASSERT_NE(p, nullptr); + ASSERT_TRUE(builder->ResizePartition(p, 466976768)); + + unique_ptr metadata = builder->Export(); + ASSERT_NE(metadata, nullptr); + ASSERT_EQ(metadata->block_devices.size(), 3); + EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "system_a"); + EXPECT_EQ(metadata->block_devices[0].size, 256_MiB); + EXPECT_EQ(metadata->block_devices[0].alignment, 786432); + EXPECT_EQ(metadata->block_devices[0].alignment_offset, 229376); + EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[1]), "vendor_a"); + EXPECT_EQ(metadata->block_devices[1].size, 128_MiB); + EXPECT_EQ(metadata->block_devices[1].alignment, 786432); + EXPECT_EQ(metadata->block_devices[1].alignment_offset, 753664); + EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[2]), "product_a"); + EXPECT_EQ(metadata->block_devices[2].size, 64_MiB); + EXPECT_EQ(metadata->block_devices[2].alignment, 786432); + EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664); + ASSERT_EQ(metadata->extents.size(), 3); + EXPECT_EQ(metadata->extents[0].num_sectors, 522304); + EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR); + EXPECT_EQ(metadata->extents[0].target_data, 1984); + EXPECT_EQ(metadata->extents[0].target_source, 0); + EXPECT_EQ(metadata->extents[1].num_sectors, 260672); + EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR); + EXPECT_EQ(metadata->extents[1].target_data, 1472); + EXPECT_EQ(metadata->extents[1].target_source, 1); + EXPECT_EQ(metadata->extents[2].num_sectors, 129088); + EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR); + EXPECT_EQ(metadata->extents[2].target_data, 1472); + EXPECT_EQ(metadata->extents[2].target_source, 2); +} diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h index a0908895a..f9de106e6 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -41,7 +41,7 @@ class Extent { explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {} virtual ~Extent() {} - virtual void AddTo(LpMetadata* out) const = 0; + virtual bool AddTo(LpMetadata* out) const = 0; virtual LinearExtent* AsLinearExtent() { return nullptr; } uint64_t num_sectors() const { return num_sectors_; } @@ -54,16 +54,18 @@ class Extent { // This corresponds to a dm-linear target. class LinearExtent final : public Extent { public: - LinearExtent(uint64_t num_sectors, uint64_t physical_sector) - : Extent(num_sectors), physical_sector_(physical_sector) {} + LinearExtent(uint64_t num_sectors, uint32_t device_index, uint64_t physical_sector) + : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {} - void AddTo(LpMetadata* metadata) const override; + bool AddTo(LpMetadata* metadata) const override; LinearExtent* AsLinearExtent() override { return this; } uint64_t physical_sector() const { return physical_sector_; } uint64_t end_sector() const { return physical_sector_ + num_sectors_; } + uint32_t device_index() const { return device_index_; } private: + uint32_t device_index_; uint64_t physical_sector_; }; @@ -72,7 +74,7 @@ class ZeroExtent final : public Extent { public: explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {} - void AddTo(LpMetadata* out) const override; + bool AddTo(LpMetadata* out) const override; }; class PartitionGroup final { @@ -122,15 +124,17 @@ class Partition final { class MetadataBuilder { public: - // Construct an empty logical partition table builder. The block device size - // and maximum metadata size must be specified, as this will determine which - // areas of the physical partition can be flashed for metadata vs for logical - // partitions. + // Construct an empty logical partition table builder given the specified + // map of partitions that are available for storing logical partitions. + // + // At least one partition in the list must be the "super" device, where + // metadata will be stored. // // If the parameters would yield invalid metadata, nullptr is returned. This - // could happen if the block device size is too small to store the metadata - // and backup copies. - static std::unique_ptr New(const BlockDeviceInfo& device_info, + // could happen if the super device is too small to store all required + // metadata. + static std::unique_ptr New(const std::vector& block_devices, + const std::string& super_partition, uint32_t metadata_max_size, uint32_t metadata_slot_count); @@ -150,11 +154,20 @@ class MetadataBuilder { // This method is for testing or changing off-line tables. static std::unique_ptr New(const LpMetadata& metadata); + // Helper function for a single super partition, for tests. + static std::unique_ptr New(const BlockDeviceInfo& device_info, + uint32_t metadata_max_size, + uint32_t metadata_slot_count) { + return New({device_info}, device_info.partition_name, metadata_max_size, + metadata_slot_count); + } + // Wrapper around New() with a BlockDeviceInfo that only specifies a device // size. This is a convenience method for tests. static std::unique_ptr New(uint64_t blockdev_size, uint32_t metadata_max_size, uint32_t metadata_slot_count) { - BlockDeviceInfo device_info(blockdev_size, 0, 0, kDefaultBlockSize); + BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0, + kDefaultBlockSize); return New(device_info, metadata_max_size, metadata_slot_count); } @@ -209,8 +222,8 @@ class MetadataBuilder { // Remove all partitions belonging to a group, then remove the group. void RemoveGroupAndPartitions(const std::string& group_name); - bool GetBlockDeviceInfo(BlockDeviceInfo* info) const; - bool UpdateBlockDeviceInfo(const BlockDeviceInfo& info); + bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const; + bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info); private: MetadataBuilder(); @@ -218,19 +231,27 @@ class MetadataBuilder { MetadataBuilder(MetadataBuilder&&) = delete; MetadataBuilder& operator=(const MetadataBuilder&) = delete; MetadataBuilder& operator=(MetadataBuilder&&) = delete; - bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count); + bool Init(const std::vector& block_devices, const std::string& super_partition, + uint32_t metadata_max_size, uint32_t metadata_slot_count); bool Init(const LpMetadata& metadata); bool GrowPartition(Partition* partition, uint64_t aligned_size); void ShrinkPartition(Partition* partition, uint64_t aligned_size); - uint64_t AlignSector(uint64_t sector) const; + uint64_t AlignSector(const LpMetadataBlockDevice& device, uint64_t sector) const; uint64_t TotalSizeOfGroup(PartitionGroup* group) const; + bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info); + bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const; struct Interval { + uint32_t device_index; uint64_t start; uint64_t end; - Interval(uint64_t start, uint64_t end) : start(start), end(end) {} + Interval(uint32_t device_index, uint64_t start, uint64_t end) + : device_index(device_index), start(start), end(end) {} uint64_t length() const { return end - start; } + + // Note: the device index is not included in sorting (intervals are + // sorted in per-device lists). bool operator<(const Interval& other) const { return (start == other.start) ? end < other.end : start < other.start; } @@ -239,9 +260,6 @@ 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_; diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index 8a309becc..1e40df3cf 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 7 +#define LP_METADATA_MAJOR_VERSION 8 #define LP_METADATA_MINOR_VERSION 0 /* Attributes for the LpMetadataPartition::attributes field. @@ -240,6 +240,13 @@ typedef struct LpMetadataExtent { * ZERO: This field must be 0. */ uint64_t target_data; + + /* 20: Contents depends on target_type. + * + * LINEAR: Must be an index into the block devices table. + * ZERO: This field must be 0. + */ + uint32_t target_source; } __attribute__((packed)) LpMetadataExtent; /* This struct defines an entry in the groups table. Each group has a maximum @@ -255,8 +262,9 @@ 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. +/* This struct defines an entry in the block_devices table. There must be at + * least one device, and the first device must represent the partition holding + * the super metadata. */ typedef struct LpMetadataBlockDevice { /* 0: First usable sector for allocating logical partitions. this will be diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h index fe61b9c20..e506bd5c2 100644 --- a/fs_mgr/liblp/include/liblp/partition_opener.h +++ b/fs_mgr/liblp/include/liblp/partition_opener.h @@ -27,12 +27,13 @@ namespace fs_mgr { struct BlockDeviceInfo { BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {} - BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset, - uint32_t logical_block_size) + BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment, + uint32_t alignment_offset, uint32_t logical_block_size) : size(size), alignment(alignment), alignment_offset(alignment_offset), - logical_block_size(logical_block_size) {} + logical_block_size(logical_block_size), + partition_name(partition_name) {} // Size of the block device, in bytes. uint64_t size; // Optimal target alignment, in bytes. Partition extents will be aligned to @@ -44,6 +45,9 @@ struct BlockDeviceInfo { uint32_t alignment_offset; // Block size, for aligning extent sizes and partition sizes. uint32_t logical_block_size; + // The physical partition name for this block device, as it would appear in + // the GPT or under /dev/block/by-name. + std::string partition_name; }; // Test-friendly interface for interacting with partitions. diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index 9c675feba..603e5c046 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -128,7 +128,7 @@ TEST(liblp, CreateFakeDisk) { // Flashing metadata should not work if the metadata was created for a larger // disk than the destination disk. TEST(liblp, ExportDiskTooSmall) { - unique_ptr builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2); + unique_ptr builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2); ASSERT_NE(builder, nullptr); unique_ptr exported = builder->Export(); ASSERT_NE(exported, nullptr); @@ -581,7 +581,7 @@ TEST(liblp, FlashSparseImage) { unique_fd fd = CreateFakeDisk(); ASSERT_GE(fd, 0); - BlockDeviceInfo device_info(kDiskSize, 0, 0, 512); + BlockDeviceInfo device_info("super", kDiskSize, 0, 0, 512); unique_ptr builder = MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots); ASSERT_NE(builder, nullptr); diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp index 7381eed84..77b0e622d 100644 --- a/fs_mgr/liblp/partition_opener.cpp +++ b/fs_mgr/liblp/partition_opener.cpp @@ -24,6 +24,8 @@ #include #include +#include + #include "utility.h" namespace android { @@ -68,6 +70,7 @@ bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device device_info->alignment_offset = static_cast(alignment_offset); device_info->logical_block_size = static_cast(logical_block_size); + device_info->partition_name = android::base::Basename(block_device); return true; #else (void)block_device; diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index 070573ce3..a02e746aa 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -274,6 +274,12 @@ static std::unique_ptr ParseMetadata(const LpMetadataGeometry& geome memcpy(&extent, cursor, sizeof(extent)); cursor += header.extents.entry_size; + if (extent.target_type == LP_TARGET_TYPE_LINEAR && + extent.target_source >= header.block_devices.num_entries) { + LERROR << "Logical partition extent has invalid block device."; + return nullptr; + } + metadata->extents.push_back(extent); }