Merge "liblp: Store device information in a new block device table."

This commit is contained in:
David Anderson 2018-10-25 18:54:55 +00:00 committed by Gerrit Code Review
commit 13e160e09f
13 changed files with 278 additions and 138 deletions

View File

@ -33,6 +33,7 @@
#include <sstream>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@ -50,8 +51,21 @@ using DmTarget = android::dm::DmTarget;
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
static bool CreateDmTable(const std::string& block_device, const LpMetadata& metadata,
const LpMetadataPartition& partition, DmTable* table) {
bool GetPhysicalPartitionDevicePath(const LpMetadataBlockDevice& block_device,
std::string* result) {
// Note: device-mapper will not accept symlinks, so we must use realpath
// here.
std::string name = GetBlockDevicePartitionName(block_device);
std::string path = "/dev/block/by-name/" + name;
if (!android::base::Realpath(path, result)) {
PERROR << "realpath: " << path;
return false;
}
return true;
}
static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
DmTable* table) {
uint64_t sector = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = metadata.extents[partition.first_extent_index + i];
@ -60,10 +74,22 @@ static bool CreateDmTable(const std::string& block_device, const LpMetadata& met
case LP_TARGET_TYPE_ZERO:
target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
break;
case LP_TARGET_TYPE_LINEAR:
target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, block_device,
case LP_TARGET_TYPE_LINEAR: {
auto block_device = GetMetadataSuperBlockDevice(metadata);
if (!block_device) {
LOG(ERROR) << "Could not identify the super block device";
return false;
}
std::string path;
if (!GetPhysicalPartitionDevicePath(*block_device, &path)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
extent.target_data);
break;
}
default:
LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
return false;
@ -79,13 +105,13 @@ static bool CreateDmTable(const std::string& block_device, const LpMetadata& met
return true;
}
static bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata,
const LpMetadataPartition& partition, bool force_writable,
const std::chrono::milliseconds& timeout_ms, std::string* path) {
static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition,
bool force_writable, const std::chrono::milliseconds& timeout_ms,
std::string* path) {
DeviceMapper& dm = DeviceMapper::Instance();
DmTable table;
if (!CreateDmTable(block_device, metadata, partition, &table)) {
if (!CreateDmTable(metadata, partition, &table)) {
return false;
}
if (force_writable) {
@ -122,7 +148,7 @@ bool CreateLogicalPartitions(const std::string& block_device) {
continue;
}
std::string path;
if (!CreateLogicalPartition(block_device, *metadata.get(), partition, false, {}, &path)) {
if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
@ -140,8 +166,8 @@ bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_s
}
for (const auto& partition : metadata->partitions) {
if (GetPartitionName(partition) == partition_name) {
return CreateLogicalPartition(block_device, *metadata.get(), partition, force_writable,
timeout_ms, path);
return CreateLogicalPartition(*metadata.get(), partition, force_writable, timeout_ms,
path);
}
}
LERROR << "Could not find any partition with name: " << partition_name;

View File

@ -186,6 +186,7 @@ MetadataBuilder::MetadataBuilder() {
header_.partitions.entry_size = sizeof(LpMetadataPartition);
header_.extents.entry_size = sizeof(LpMetadataExtent);
header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice);
}
bool MetadataBuilder::Init(const LpMetadata& metadata) {
@ -198,6 +199,10 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) {
}
}
for (const auto& block_device : metadata.block_devices) {
block_devices_.push_back(block_device);
}
for (const auto& partition : metadata.partitions) {
std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
Partition* builder =
@ -259,9 +264,7 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata
// We reserve a geometry block (4KB) plus space for each copy of the
// maximum size of a metadata blob. Then, we double that space since
// we store a backup copy of everything.
uint64_t reserved =
LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved * 2;
uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);
if (device_info.size < total_reserved) {
LERROR << "Attempting to create metadata on a block device that is too small.";
return false;
@ -285,12 +288,16 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata
return false;
}
geometry_.first_logical_sector = first_sector;
block_devices_.push_back(LpMetadataBlockDevice{
first_sector,
device_info.alignment,
device_info.alignment_offset,
device_info.size,
"super",
});
geometry_.metadata_max_size = metadata_max_size;
geometry_.metadata_slot_count = metadata_slot_count;
geometry_.alignment = device_info.alignment;
geometry_.alignment_offset = device_info.alignment_offset;
geometry_.block_device_size = device_info.size;
geometry_.logical_block_size = device_info.logical_block_size;
if (!AddGroup("default", 0)) {
@ -408,9 +415,10 @@ auto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {
}
// Add 0-length intervals for the first and last sectors. This will cause
// ExtentsToFreeList() to treat the space in between as available.
uint64_t last_sector = geometry_.block_device_size / LP_SECTOR_SIZE;
extents.emplace_back(geometry_.first_logical_sector, geometry_.first_logical_sector);
// ExtentToFreeList() to treat the space in between as available.
uint64_t first_sector = super_device().first_logical_sector;
uint64_t last_sector = super_device().size / LP_SECTOR_SIZE;
extents.emplace_back(first_sector, first_sector);
extents.emplace_back(last_sector, last_sector);
std::sort(extents.begin(), extents.end());
@ -547,14 +555,18 @@ std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
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());
metadata->header.block_devices.num_entries =
static_cast<uint32_t>(metadata->block_devices.size());
return metadata;
}
uint64_t MetadataBuilder::AllocatableSpace() const {
return geometry_.block_device_size - (geometry_.first_logical_sector * LP_SECTOR_SIZE);
return super_device().size - (super_device().first_logical_sector * LP_SECTOR_SIZE);
}
uint64_t MetadataBuilder::UsedSpace() const {
@ -569,22 +581,22 @@ uint64_t MetadataBuilder::AlignSector(uint64_t sector) const {
// Note: when reading alignment info from the Kernel, we don't assume it
// is aligned to the sector size, so we round up to the nearest sector.
uint64_t lba = sector * LP_SECTOR_SIZE;
uint64_t aligned = AlignTo(lba, geometry_.alignment, geometry_.alignment_offset);
uint64_t aligned = AlignTo(lba, super_device().alignment, super_device().alignment_offset);
return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
}
bool MetadataBuilder::GetBlockDeviceInfo(BlockDeviceInfo* info) const {
info->size = geometry_.block_device_size;
info->alignment = geometry_.alignment;
info->alignment_offset = geometry_.alignment_offset;
info->size = super_device().size;
info->alignment = super_device().alignment;
info->alignment_offset = super_device().alignment_offset;
info->logical_block_size = geometry_.logical_block_size;
return true;
}
bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) {
if (device_info.size != geometry_.block_device_size) {
if (device_info.size != super_device().size) {
LERROR << "Device size does not match (got " << device_info.size << ", expected "
<< geometry_.block_device_size << ")";
<< super_device().size << ")";
return false;
}
if (device_info.logical_block_size != geometry_.logical_block_size) {
@ -596,10 +608,10 @@ bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info)
// The kernel does not guarantee these values are present, so we only
// replace existing values if the new values are non-zero.
if (device_info.alignment) {
geometry_.alignment = device_info.alignment;
super_device().alignment = device_info.alignment;
}
if (device_info.alignment_offset) {
geometry_.alignment_offset = device_info.alignment_offset;
super_device().alignment_offset = device_info.alignment_offset;
}
return true;
}

View File

@ -132,7 +132,9 @@ TEST(liblp, InternalAlignment) {
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
auto super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
EXPECT_EQ(super_device->first_logical_sector, 1536);
// Test a large alignment offset thrown in.
device_info.alignment_offset = 753664;
@ -140,7 +142,9 @@ TEST(liblp, InternalAlignment) {
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
EXPECT_EQ(super_device->first_logical_sector, 1472);
// Alignment offset without alignment doesn't mean anything.
device_info.alignment = 0;
@ -154,7 +158,9 @@ TEST(liblp, InternalAlignment) {
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.first_logical_sector, 174);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
EXPECT_EQ(super_device->first_logical_sector, 174);
// Test a small alignment with no alignment offset.
device_info.alignment = 11 * 1024;
@ -162,7 +168,9 @@ TEST(liblp, InternalAlignment) {
ASSERT_NE(builder, nullptr);
exported = builder->Export();
ASSERT_NE(exported, nullptr);
EXPECT_EQ(exported->geometry.first_logical_sector, 160);
super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
EXPECT_EQ(super_device->first_logical_sector, 160);
}
TEST(liblp, InternalPartitionAlignment) {
@ -292,6 +300,9 @@ TEST(liblp, BuilderExport) {
unique_ptr<LpMetadata> exported = builder->Export();
EXPECT_NE(exported, nullptr);
auto super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
// Verify geometry. Some details of this may change if we change the
// metadata structures. So in addition to checking the exact values, we
// also check that they are internally consistent after.
@ -300,11 +311,11 @@ TEST(liblp, BuilderExport) {
EXPECT_EQ(geometry.struct_size, sizeof(geometry));
EXPECT_EQ(geometry.metadata_max_size, 1024);
EXPECT_EQ(geometry.metadata_slot_count, 2);
EXPECT_EQ(geometry.first_logical_sector, 32);
EXPECT_EQ(super_device->first_logical_sector, 32);
static const size_t kMetadataSpace =
((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;
EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
EXPECT_GE(super_device->first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);
// Verify header.
const LpMetadataHeader& header = exported->header;

View File

@ -99,11 +99,12 @@ SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
block_size_(block_size),
file_(nullptr, sparse_file_destroy),
images_(images) {
uint64_t total_size = GetTotalSuperPartitionSize(metadata);
if (block_size % LP_SECTOR_SIZE != 0) {
LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
return;
}
if (metadata.geometry.block_device_size % block_size != 0) {
if (total_size % block_size != 0) {
LERROR << "Device size must be a multiple of the block size, " << block_size;
return;
}
@ -120,7 +121,7 @@ SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
return;
}
uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
uint64_t num_blocks = total_size % block_size;
if (num_blocks >= UINT_MAX) {
// libsparse counts blocks in unsigned 32-bit integers, so we check to
// make sure we're not going to overflow.
@ -128,7 +129,10 @@ SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
return;
}
file_.reset(sparse_file_new(block_size_, geometry_.block_device_size));
file_.reset(sparse_file_new(block_size_, total_size));
if (!file_) {
LERROR << "Could not allocate sparse file of size " << total_size;
}
}
bool SparseBuilder::Export(const char* file) {
@ -333,14 +337,7 @@ int SparseBuilder::OpenImageFile(const std::string& file) {
bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
const std::map<std::string, std::string>& images) {
SparseBuilder builder(metadata, block_size, images);
if (!builder.IsValid()) {
LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
return false;
}
if (!builder.Build()) {
return false;
}
return builder.Export(file);
return builder.IsValid() && builder.Build() && builder.Export(file);
}
} // namespace fs_mgr

View File

@ -254,10 +254,14 @@ 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_;
std::vector<std::unique_ptr<PartitionGroup>> groups_;
std::vector<LpMetadataBlockDevice> block_devices_;
};
// Read BlockDeviceInfo for a given block device. This always returns false

View File

@ -37,6 +37,7 @@ struct LpMetadata {
std::vector<LpMetadataPartition> partitions;
std::vector<LpMetadataExtent> extents;
std::vector<LpMetadataPartitionGroup> groups;
std::vector<LpMetadataBlockDevice> block_devices;
};
// Place an initial partition table on the device. This will overwrite the
@ -69,6 +70,14 @@ std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);
// Return the block device that houses the super partition metadata; returns
// null on failure.
const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);
// Return the total size of all partitions comprising the super partition.
uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
// Helper to return a slot number for a slot suffix.
uint32_t SlotNumberForSlotSuffix(const std::string& suffix);

View File

@ -38,7 +38,7 @@ extern "C" {
#define LP_METADATA_HEADER_MAGIC 0x414C5030
/* Current metadata version. */
#define LP_METADATA_MAJOR_VERSION 6
#define LP_METADATA_MAJOR_VERSION 7
#define LP_METADATA_MINOR_VERSION 0
/* Attributes for the LpMetadataPartition::attributes field.
@ -103,42 +103,10 @@ typedef struct LpMetadataGeometry {
*/
uint32_t metadata_slot_count;
/* 48: First usable sector for allocating logical partitions. this will be
* the first sector after the initial geometry blocks, followed by the
* space consumed by metadata_max_size*metadata_slot_count*2.
*/
uint64_t first_logical_sector;
/* 64: Alignment for defining partitions or partition extents. For example,
* an alignment of 1MiB will require that all partitions have a size evenly
* divisible by 1MiB, and that the smallest unit the partition can grow by
* is 1MiB.
*
* Alignment is normally determined at runtime when growing or adding
* partitions. If for some reason the alignment cannot be determined, then
* this predefined alignment in the geometry is used instead. By default
* it is set to 1MiB.
*/
uint32_t alignment;
/* 68: Alignment offset for "stacked" devices. For example, if the "super"
* partition itself is not aligned within the parent block device's
* partition table, then we adjust for this in deciding where to place
* |first_logical_sector|.
*
* Similar to |alignment|, this will be derived from the operating system.
* If it cannot be determined, it is assumed to be 0.
*/
uint32_t alignment_offset;
/* 72: Block device size, as specified when the metadata was created. This
* can be used to verify the geometry against a target device.
*/
uint64_t block_device_size;
/* 76: Logical block size of the super partition block device. This is the
* minimal alignment for partition and extent sizes, and it must be a
* multiple of LP_SECTOR_SIZE.
/* 48: Logical block size. This is the minimal alignment for partition and
* extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
* this must be equal across all LUNs that comprise the super partition,
* and thus this field is stored in the geometry, not per-device.
*/
uint32_t logical_block_size;
} __attribute__((packed)) LpMetadataGeometry;
@ -217,6 +185,8 @@ typedef struct LpMetadataHeader {
LpMetadataTableDescriptor extents;
/* 104: Updateable group descriptor. */
LpMetadataTableDescriptor groups;
/* 116: Block device table. */
LpMetadataTableDescriptor block_devices;
} __attribute__((packed)) LpMetadataHeader;
/* This struct defines a logical partition entry, similar to what would be
@ -285,6 +255,47 @@ typedef struct LpMetadataPartitionGroup {
uint64_t maximum_size;
} LpMetadataPartitionGroup;
/* This struct defines an entry in the block_devices table. There must be
* exactly one device, corresponding to the super partition.
*/
typedef struct LpMetadataBlockDevice {
/* 0: First usable sector for allocating logical partitions. this will be
* the first sector after the initial geometry blocks, followed by the
* space consumed by metadata_max_size*metadata_slot_count*2.
*/
uint64_t first_logical_sector;
/* 8: Alignment for defining partitions or partition extents. For example,
* an alignment of 1MiB will require that all partitions have a size evenly
* divisible by 1MiB, and that the smallest unit the partition can grow by
* is 1MiB.
*
* Alignment is normally determined at runtime when growing or adding
* partitions. If for some reason the alignment cannot be determined, then
* this predefined alignment in the geometry is used instead. By default
* it is set to 1MiB.
*/
uint32_t alignment;
/* 12: Alignment offset for "stacked" devices. For example, if the "super"
* partition itself is not aligned within the parent block device's
* partition table, then we adjust for this in deciding where to place
* |first_logical_sector|.
*
* Similar to |alignment|, this will be derived from the operating system.
* If it cannot be determined, it is assumed to be 0.
*/
uint32_t alignment_offset;
/* 16: Block device size, as specified when the metadata was created. This
* can be used to verify the geometry against a target device.
*/
uint64_t size;
/* 24: Partition name in the GPT. Any unused characters must be 0. */
char partition_name[36];
} LpMetadataBlockDevice;
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@ -160,7 +160,6 @@ TEST(liblp, FlashAndReadback) {
// Check geometry and header.
EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
EXPECT_EQ(exported->header.major_version, imported->header.major_version);
EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
EXPECT_EQ(exported->header.header_size, imported->header.header_size);
@ -178,6 +177,11 @@ TEST(liblp, FlashAndReadback) {
EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
// Check block devices table.
ASSERT_EQ(exported->block_devices.size(), imported->block_devices.size());
EXPECT_EQ(exported->block_devices[0].first_logical_sector,
imported->block_devices[0].first_logical_sector);
}
// Test that we can update metadata slots without disturbing others.
@ -206,14 +210,17 @@ TEST(liblp, UpdateAnyMetadataSlot) {
ASSERT_EQ(imported->partitions.size(), 1);
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
uint64_t last_sector = imported->geometry.block_device_size / LP_SECTOR_SIZE;
auto super_device = GetMetadataSuperBlockDevice(*imported.get());
ASSERT_NE(super_device, nullptr);
uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
// Verify that we didn't overwrite anything in the logical paritition area.
// We expect the disk to be filled with 0xcc on creation so we can read
// this back and compare it.
char expected[LP_SECTOR_SIZE];
memset(expected, 0xcc, sizeof(expected));
for (uint64_t i = imported->geometry.first_logical_sector; i < last_sector; i++) {
for (uint64_t i = super_device->first_logical_sector; i < last_sector; i++) {
char buffer[LP_SECTOR_SIZE];
ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
@ -256,7 +263,8 @@ TEST(liblp, NoChangingGeometry) {
imported = ReadMetadata(fd, 0);
ASSERT_NE(imported, nullptr);
imported->geometry.first_logical_sector++;
ASSERT_EQ(imported->block_devices.size(), 1);
imported->block_devices[0].first_logical_sector++;
ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
imported = ReadMetadata(fd, 0);
@ -329,9 +337,11 @@ TEST(liblp, TooManyPartitions) {
ASSERT_NE(builder, nullptr);
// Compute the maximum number of partitions we can fit in 512 bytes of
// metadata. By default there is the header, and one partition group.
static const size_t kMaxPartitionTableSize =
kMetadataSize - sizeof(LpMetadataHeader) - sizeof(LpMetadataPartitionGroup);
// metadata. By default there is the header, one partition group, and a
// block device entry.
static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeader) -
sizeof(LpMetadataPartitionGroup) -
sizeof(LpMetadataBlockDevice);
size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);
// Add this number of partitions.
@ -360,12 +370,15 @@ TEST(liblp, TooManyPartitions) {
// The new table should be too large to be written.
ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
auto super_device = GetMetadataSuperBlockDevice(*exported.get());
ASSERT_NE(super_device, nullptr);
// Check that the first and last logical sectors weren't touched when we
// wrote this almost-full metadata.
char expected[LP_SECTOR_SIZE];
memset(expected, 0xcc, sizeof(expected));
char buffer[LP_SECTOR_SIZE];
ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
ASSERT_GE(lseek(fd, super_device->first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
}

View File

@ -108,15 +108,6 @@ bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
LERROR << "Metadata max size is not sector-aligned.";
return false;
}
// Check that the metadata area and logical partition areas don't overlap.
int64_t end_of_metadata =
GetPrimaryMetadataOffset(*geometry, geometry->metadata_slot_count - 1) +
geometry->metadata_max_size;
if (uint64_t(end_of_metadata) > geometry->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Logical partition metadata overlaps with logical partition contents.";
return false;
}
return true;
}
@ -195,7 +186,8 @@ static bool ValidateMetadataHeader(const LpMetadataHeader& header) {
}
if (!ValidateTableBounds(header, header.partitions) ||
!ValidateTableBounds(header, header.extents) ||
!ValidateTableBounds(header, header.groups)) {
!ValidateTableBounds(header, header.groups) ||
!ValidateTableBounds(header, header.block_devices)) {
LERROR << "Logical partition metadata has invalid table bounds.";
return false;
}
@ -294,6 +286,28 @@ static std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geome
metadata->groups.push_back(group);
}
cursor = buffer.get() + header.block_devices.offset;
for (size_t i = 0; i < header.block_devices.num_entries; i++) {
LpMetadataBlockDevice device = {};
memcpy(&device, cursor, sizeof(device));
cursor += header.block_devices.entry_size;
metadata->block_devices.push_back(device);
}
const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());
if (!super_device) {
LERROR << "Metadata does not specify a super device.";
return nullptr;
}
// Check that the metadata area and logical partition areas don't overlap.
uint64_t metadata_region =
GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);
if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Logical partition metadata overlaps with logical partition contents.";
return nullptr;
}
return metadata;
}
@ -374,5 +388,9 @@ std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {
return NameFromFixedArray(group.name, sizeof(group.name));
}
std::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {
return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name));
}
} // namespace fs_mgr
} // namespace android

View File

@ -66,11 +66,8 @@ int64_t GetBackupGeometryOffset() {
int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {
CHECK(slot_number < geometry.metadata_slot_count);
int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +
geometry.metadata_max_size * slot_number;
CHECK(offset + geometry.metadata_max_size <=
int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE));
return offset;
}
@ -81,6 +78,18 @@ int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slo
return start + int64_t(geometry.metadata_max_size * slot_number);
}
uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {
return LP_PARTITION_RESERVED_BYTES +
(LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;
}
const LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {
if (metadata.block_devices.empty()) {
return nullptr;
}
return &metadata.block_devices[0];
}
void SHA256(const void* data, size_t length, uint8_t out[32]) {
SHA256_CTX c;
SHA256_Init(&c);
@ -100,5 +109,13 @@ uint32_t SlotNumberForSlotSuffix(const std::string& suffix) {
return suffix[1] - 'a';
}
uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {
uint64_t size = 0;
for (const auto& block_device : metadata.block_devices) {
size += block_device.size;
}
return size;
}
} // namespace fs_mgr
} // namespace android

View File

@ -23,7 +23,7 @@
#include <android-base/logging.h>
#include "liblp/metadata_format.h"
#include "liblp/liblp.h"
#define LP_TAG "[liblp]"
#define LWARN LOG(WARNING) << LP_TAG
@ -50,6 +50,10 @@ int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t sl
// device.
int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);
// Return the total space at the start of the super partition that must be set
// aside from headers/metadata and backups.
uint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);
// Cross-platform helper for lseek64().
int64_t SeekFile64(int fd, int64_t offset, int whence);

View File

@ -36,10 +36,6 @@ TEST(liblp, GetMetadataOffset) {
{0},
16384,
4,
10000,
0,
0,
1024 * 1024,
4096};
static const uint64_t start = LP_PARTITION_RESERVED_BYTES;
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192);

View File

@ -43,9 +43,7 @@ std::string SerializeGeometry(const LpMetadataGeometry& input) {
static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {
return g1.metadata_max_size == g2.metadata_max_size &&
g1.metadata_slot_count == g2.metadata_slot_count &&
g1.block_device_size == g2.block_device_size &&
g1.logical_block_size == g2.logical_block_size &&
g1.first_logical_sector == g2.first_logical_sector;
g1.logical_block_size == g2.logical_block_size;
}
std::string SerializeMetadata(const LpMetadata& input) {
@ -59,15 +57,18 @@ std::string SerializeMetadata(const LpMetadata& input) {
metadata.extents.size() * sizeof(LpMetadataExtent));
std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),
metadata.groups.size() * sizeof(LpMetadataPartitionGroup));
std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),
metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));
// Compute positions of tables.
header.partitions.offset = 0;
header.extents.offset = header.partitions.offset + partitions.size();
header.groups.offset = header.extents.offset + extents.size();
header.tables_size = header.groups.offset + groups.size();
header.block_devices.offset = header.groups.offset + groups.size();
header.tables_size = header.block_devices.offset + block_devices.size();
// Compute payload checksum.
std::string tables = partitions + extents + groups;
std::string tables = partitions + extents + groups + block_devices;
SHA256(tables.data(), tables.size(), header.tables_checksum);
// Compute header checksum.
@ -105,14 +106,20 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std
uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
uint64_t total_reserved = reserved_size * 2;
const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
if (!super_device) {
LERROR << "Logical partition metadata does not have a super block device.";
return false;
}
if (total_reserved > blockdevice_size ||
total_reserved > geometry.first_logical_sector * LP_SECTOR_SIZE) {
total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Not enough space to store all logical partition metadata slots.";
return false;
}
if (blockdevice_size != metadata.geometry.block_device_size) {
if (blockdevice_size != super_device->size) {
LERROR << "Block device size " << blockdevice_size
<< " does not match metadata requested size " << metadata.geometry.block_device_size;
<< " does not match metadata requested size " << super_device->size;
return false;
}
@ -125,11 +132,11 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std
}
// Make sure all linear extents have a valid range.
uint64_t last_sector = geometry.block_device_size / LP_SECTOR_SIZE;
uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;
for (const auto& extent : metadata.extents) {
if (extent.target_type == LP_TARGET_TYPE_LINEAR) {
uint64_t physical_sector = extent.target_data;
if (physical_sector < geometry.first_logical_sector ||
if (physical_sector < super_device->first_logical_sector ||
physical_sector + extent.num_sectors > last_sector) {
LERROR << "Extent table entry is out of bounds.";
return false;
@ -139,10 +146,28 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std
return true;
}
static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
// Check that the given region is within metadata bounds.
static bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {
const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
if (!super_device) {
LERROR << __PRETTY_FUNCTION__ << " could not locate super block device in metadata";
return false;
}
if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << __PRETTY_FUNCTION__ << " write of " << size << " bytes at " << start
<< " overlaps with logical partition contents";
return false;
}
return true;
}
static bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);
if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {
return false;
}
if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << primary_offset;
return false;
@ -154,18 +179,15 @@ static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uin
return true;
}
static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
static bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number);
int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_SET);
if (abs_offset == (int64_t)-1) {
PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);
if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {
return false;
}
if (abs_offset >= int64_t(geometry.first_logical_sector) * LP_SECTOR_SIZE) {
PERROR << __PRETTY_FUNCTION__ << " backup offset " << abs_offset
<< " is within logical partition bounds, sector " << geometry.first_logical_sector;
if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {
PERROR << __PRETTY_FUNCTION__ << " lseek failed: offset " << backup_offset;
return false;
}
if (!writer(fd, blob)) {
@ -175,18 +197,18 @@ static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint
return true;
}
static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
static bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,
const std::string& blob,
const std::function<bool(int, const std::string&)>& writer) {
// Make sure we're writing to a valid metadata slot.
if (slot_number >= geometry.metadata_slot_count) {
if (slot_number >= metadata.geometry.metadata_slot_count) {
LERROR << "Invalid logical partition metadata slot number.";
return false;
}
if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) {
if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {
return false;
}
if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) {
if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {
return false;
}
return true;
@ -237,7 +259,7 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata) {
bool ok = true;
for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
ok &= WriteMetadata(fd, metadata.geometry, i, metadata_blob, DefaultWriter);
ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);
}
return ok;
}
@ -289,7 +311,7 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) {
if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) {
LERROR << "Error writing primary metadata to repair corrupted backup";
return false;
}
@ -301,14 +323,14 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) {
if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {
LERROR << "Error writing primary metadata to repair corrupted backup";
return false;
}
}
// Both copies should now be in sync, so we can continue the update.
return WriteMetadata(fd, geometry, slot_number, blob, writer);
return WriteMetadata(fd, metadata, slot_number, blob, writer);
}
bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) {