Merge "liblp: Allow the super partition to span multiple block devices."
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 6ddd5a8..4dacebf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -75,14 +75,9 @@
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 1b8ed57..3cd33b1 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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.";
+ // 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;
}
- 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.";
- 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
// 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 c02242a..c27e300 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -27,6 +27,7 @@
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, 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, 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, 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, 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, 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 @@
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, 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 @@
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, 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, 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 @@
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, 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, 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 @@
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 a090889..f9de106 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -41,7 +41,7 @@
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 @@
// 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 @@
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 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 @@
// 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 @@
// 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 @@
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 @@
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 8a309be..1e40df3 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
#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 @@
* 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 @@
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 fe61b9c..e506bd5 100644
--- a/fs_mgr/liblp/include/liblp/partition_opener.h
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -27,12 +27,13 @@
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 @@
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 9c675fe..603e5c0 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -128,7 +128,7 @@
// 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 @@
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 7381eed..77b0e62 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 @@
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 070573c..a02e746 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -274,6 +274,12 @@
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);
}