diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp index 804069a3c..6ddd5a895 100644 --- a/fs_mgr/fs_mgr_dm_linear.cpp +++ b/fs_mgr/fs_mgr_dm_linear.cpp @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -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(sector, extent.num_sectors); break; - case LP_TARGET_TYPE_LINEAR: - target = std::make_unique(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(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; diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index 2c57a35b9..4dd60e98b 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -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 { } // 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 MetadataBuilder::Export() { metadata->partitions.push_back(part); } + metadata->block_devices = block_devices_; + metadata->header.partitions.num_entries = static_cast(metadata->partitions.size()); metadata->header.extents.num_entries = static_cast(metadata->extents.size()); metadata->header.groups.num_entries = static_cast(metadata->groups.size()); + metadata->header.block_devices.num_entries = + static_cast(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; } diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp index 27ad25082..c3a5ffe92 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -132,7 +132,9 @@ TEST(liblp, InternalAlignment) { ASSERT_NE(builder, nullptr); unique_ptr 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 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; diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp index dfa37fe77..46bdfa4d9 100644 --- a/fs_mgr/liblp/images.cpp +++ b/fs_mgr/liblp/images.cpp @@ -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& 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 diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h index 7e07df464..6d7324d4d 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -254,10 +254,14 @@ class MetadataBuilder { void ExtentsToFreeList(const std::vector& extents, std::vector* free_regions) const; + const LpMetadataBlockDevice& super_device() const { return block_devices_[0]; } + LpMetadataBlockDevice& super_device() { return block_devices_[0]; } + LpMetadataGeometry geometry_; LpMetadataHeader header_; std::vector> partitions_; std::vector> groups_; + std::vector block_devices_; }; // Read BlockDeviceInfo for a given block device. This always returns false diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h index 5f95dca6d..15fcd43ac 100644 --- a/fs_mgr/liblp/include/liblp/liblp.h +++ b/fs_mgr/liblp/include/liblp/liblp.h @@ -37,6 +37,7 @@ struct LpMetadata { std::vector partitions; std::vector extents; std::vector groups; + std::vector block_devices; }; // Place an initial partition table on the device. This will overwrite the @@ -69,6 +70,14 @@ std::unique_ptr 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); diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index 89b219c09..8a309becc 100644 --- a/fs_mgr/liblp/include/liblp/metadata_format.h +++ b/fs_mgr/liblp/include/liblp/metadata_format.h @@ -38,7 +38,7 @@ extern "C" { #define LP_METADATA_HEADER_MAGIC 0x414C5030 /* Current metadata version. */ -#define LP_METADATA_MAJOR_VERSION 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 diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index 2aa41f32e..3889e87f2 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -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); } diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index 43d807677..c34b1382d 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -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 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 diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp index 0556833ff..518920d26 100644 --- a/fs_mgr/liblp/utility.cpp +++ b/fs_mgr/liblp/utility.cpp @@ -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 diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h index 61e7d316c..65e643b70 100644 --- a/fs_mgr/liblp/utility.h +++ b/fs_mgr/liblp/utility.h @@ -23,7 +23,7 @@ #include -#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); diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp index 8baf9e730..bdf6dfda9 100644 --- a/fs_mgr/liblp/utility_test.cpp +++ b/fs_mgr/liblp/utility_test.cpp @@ -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); diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp index f857d8cc4..ddae8420a 100644 --- a/fs_mgr/liblp/writer.cpp +++ b/fs_mgr/liblp/writer.cpp @@ -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(metadata.groups.data()), metadata.groups.size() * sizeof(LpMetadataPartitionGroup)); + std::string block_devices(reinterpret_cast(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& 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& 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& 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) {