Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9a532417 authored by David Anderson's avatar David Anderson
Browse files

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

This patch removes the alignment, block device size, and starting sector
fields from LpGeometry into a new LpMetadataBlockDevice struct. The
metadata now contains a table of these structs, and the table will have
exactly one entry representing the super partition.

This refactoring will make it easier to have logical partitions span
multiple physical partitions. When that happens, the table will be
allowed to have more than one entry, and the first entry of the table
will be considered the "root" of the super partition.

Bug: 116802789
Test: liblp_test gtest
      device with logical partitions flashes and boots
Change-Id: I97f23beac0363182cb6ae78ba2595860950afcf0
parent 551efd11
Loading
Loading
Loading
Loading
+37 −11
Original line number Diff line number Diff line
@@ -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;
+31 −19
Original line number Diff line number Diff line
@@ -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;
}
+17 −6
Original line number Diff line number Diff line
@@ -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;
+8 −11
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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
Loading