Merge "liblp: Allow the super partition to span multiple block devices."

This commit is contained in:
David Anderson 2018-10-29 20:57:06 +00:00 committed by Gerrit Code Review
commit 2cda71c9f6
9 changed files with 350 additions and 146 deletions

View File

@ -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;
}

View File

@ -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;
}
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<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;
}

View File

@ -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);
}

View File

@ -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_;

View File

@ -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

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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);
}