diff options
-rw-r--r-- | fs_mgr/fs_mgr_dm_linear.cpp | 9 | ||||
-rw-r--r-- | fs_mgr/liblp/builder.cpp | 296 | ||||
-rw-r--r-- | fs_mgr/liblp/builder_test.cpp | 92 | ||||
-rw-r--r-- | fs_mgr/liblp/include/liblp/builder.h | 60 | ||||
-rw-r--r-- | fs_mgr/liblp/include/liblp/metadata_format.h | 14 | ||||
-rw-r--r-- | fs_mgr/liblp/include/liblp/partition_opener.h | 10 | ||||
-rw-r--r-- | fs_mgr/liblp/io_test.cpp | 4 | ||||
-rw-r--r-- | fs_mgr/liblp/partition_opener.cpp | 3 | ||||
-rw-r--r-- | fs_mgr/liblp/reader.cpp | 6 |
9 files changed, 349 insertions, 145 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<DmTargetZero>(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>&& 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<LinearExtent>( - 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<LinearExtent>( + 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> 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> MetadataBuilder::New(const std::string& super_p return New(PartitionOpener(), super_partition, slot_number); } -std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info, - uint32_t metadata_max_size, - uint32_t metadata_slot_count) { +std::unique_ptr<MetadataBuilder> MetadataBuilder::New( + const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition, + uint32_t metadata_max_size, uint32_t metadata_slot_count) { std::unique_ptr<MetadataBuilder> 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<LinearExtent>(extent.num_sectors, extent.target_data); + auto copy = std::make_unique<LinearExtent>(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<ZeroExtent>(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<BlockDeviceInfo>& 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; + // 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 (device_info.alignment_offset > device_info.alignment) { - LERROR << "Partition alignment offset is greater than its alignment."; + 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<Interval>& 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<Interval>& 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<Interval> { std::vector<Interval> free_regions; - // Collect all extents in the partition table, then sort them by starting - // sector. - std::vector<Interval> extents; + // Collect all extents in the partition table, per-device, then sort them + // by starting sector. + std::vector<std::vector<Interval>> 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<LinearExtent>(sectors, region.start); + auto extent = std::make_unique<LinearExtent>(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<LpMetadata> 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<std::string, size_t> group_indices; for (const auto& group : groups_) { LpMetadataPartitionGroup out = {}; @@ -515,13 +597,13 @@ std::unique_ptr<LpMetadata> 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<uint32_t>(metadata->partitions.size()); metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size()); metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size()); @@ -531,7 +613,11 @@ std::unique_ptr<LpMetadata> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2); + ASSERT_NE(builder, nullptr); unique_ptr<LpMetadata> 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<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2); ASSERT_NE(builder, nullptr); unique_ptr<LpMetadata> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<MetadataBuilder> 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<BlockDeviceInfo> 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<MetadataBuilder> 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<LpMetadata> 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<MetadataBuilder> New(const BlockDeviceInfo& device_info, + // could happen if the super device is too small to store all required + // metadata. + static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& 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<MetadataBuilder> New(const LpMetadata& metadata); + // Helper function for a single super partition, for tests. + static std::unique_ptr<MetadataBuilder> 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<MetadataBuilder> 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<BlockDeviceInfo>& 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<Interval>& extents, std::vector<Interval>* 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<std::unique_ptr<Partition>> 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<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2); + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2); ASSERT_NE(builder, nullptr); unique_ptr<LpMetadata> 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<MetadataBuilder> 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 <sys/types.h> #include <unistd.h> +#include <android-base/file.h> + #include "utility.h" namespace android { @@ -68,6 +70,7 @@ bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device device_info->alignment_offset = static_cast<uint32_t>(alignment_offset); device_info->logical_block_size = static_cast<uint32_t>(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<LpMetadata> 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); } |