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

Commit 70827f6b authored by David Anderson's avatar David Anderson Committed by android-build-merger
Browse files

Merge "liblp: Add support for updateable partition groups." am: d7f2c560

am: f082a7ca

Change-Id: Id30236488b5c0be4f22b203beb32940a363a6448
parents fcd0c1e3 f082a7ca
Loading
Loading
Loading
Loading
+103 −8
Original line number Diff line number Diff line
@@ -79,8 +79,9 @@ void ZeroExtent::AddTo(LpMetadata* out) const {
    out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0});
}

Partition::Partition(const std::string& name, const std::string& guid, uint32_t attributes)
    : name_(name), guid_(guid), attributes_(attributes), size_(0) {}
Partition::Partition(const std::string& name, const std::string& group_name,
                     const std::string& guid, uint32_t attributes)
    : name_(name), group_name_(group_name), guid_(guid), attributes_(attributes), size_(0) {}

void Partition::AddExtent(std::unique_ptr<Extent>&& extent) {
    size_ += extent->num_sectors() * LP_SECTOR_SIZE;
@@ -128,6 +129,17 @@ void Partition::ShrinkTo(uint64_t aligned_size) {
    DCHECK(size_ == aligned_size);
}

uint64_t Partition::BytesOnDisk() const {
    uint64_t sectors = 0;
    for (const auto& extent : extents_) {
        if (!extent->AsLinearExtent()) {
            continue;
        }
        sectors += extent->num_sectors();
    }
    return sectors * LP_SECTOR_SIZE;
}

std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
                                                      uint32_t slot_number) {
    std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
@@ -175,14 +187,23 @@ MetadataBuilder::MetadataBuilder() {
    header_.header_size = sizeof(header_);
    header_.partitions.entry_size = sizeof(LpMetadataPartition);
    header_.extents.entry_size = sizeof(LpMetadataExtent);
    header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);
}

bool MetadataBuilder::Init(const LpMetadata& metadata) {
    geometry_ = metadata.geometry;

    for (const auto& group : metadata.groups) {
        std::string group_name = GetPartitionGroupName(group);
        if (!AddGroup(group_name, group.maximum_size)) {
            return false;
        }
    }

    for (const auto& partition : metadata.partitions) {
        Partition* builder = AddPartition(GetPartitionName(partition), GetPartitionGuid(partition),
                                          partition.attributes);
        std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);
        Partition* builder = AddPartition(GetPartitionName(partition), group_name,
                                          GetPartitionGuid(partition), partition.attributes);
        if (!builder) {
            return false;
        }
@@ -292,11 +313,29 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata
    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)) {
        return false;
    }
    return true;
}

bool MetadataBuilder::AddGroup(const std::string& group_name, uint64_t maximum_size) {
    if (FindGroup(group_name)) {
        LERROR << "Group already exists: " << group_name;
        return false;
    }
    groups_.push_back(std::make_unique<PartitionGroup>(group_name, maximum_size));
    return true;
}

Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& guid,
                                         uint32_t attributes) {
    return AddPartition(name, "default", guid, attributes);
}

Partition* MetadataBuilder::AddPartition(const std::string& name, const std::string& group_name,
                                         const std::string& guid, uint32_t attributes) {
    if (name.empty()) {
        LERROR << "Partition must have a non-empty name.";
        return nullptr;
@@ -305,7 +344,11 @@ Partition* MetadataBuilder::AddPartition(const std::string& name, const std::str
        LERROR << "Attempting to create duplication partition with name: " << name;
        return nullptr;
    }
    partitions_.push_back(std::make_unique<Partition>(name, guid, attributes));
    if (!FindGroup(group_name)) {
        LERROR << "Could not find partition group: " << group_name;
        return nullptr;
    }
    partitions_.push_back(std::make_unique<Partition>(name, group_name, guid, attributes));
    return partitions_.back().get();
}

@@ -318,6 +361,26 @@ Partition* MetadataBuilder::FindPartition(const std::string& name) {
    return nullptr;
}

PartitionGroup* MetadataBuilder::FindGroup(const std::string& group_name) const {
    for (const auto& group : groups_) {
        if (group->name() == group_name) {
            return group.get();
        }
    }
    return nullptr;
}

uint64_t MetadataBuilder::TotalSizeOfGroup(PartitionGroup* group) const {
    uint64_t total = 0;
    for (const auto& partition : partitions_) {
        if (partition->group_name() != group->name()) {
            continue;
        }
        total += partition->BytesOnDisk();
    }
    return total;
}

void MetadataBuilder::RemovePartition(const std::string& name) {
    for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {
        if ((*iter)->name() == name) {
@@ -328,8 +391,23 @@ void MetadataBuilder::RemovePartition(const std::string& name) {
}

bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) {
    // Figure out how much we need to allocate.
    PartitionGroup* group = FindGroup(partition->group_name());
    CHECK(group);

    // Figure out how much we need to allocate, and whether our group has
    // enough space remaining.
    uint64_t space_needed = aligned_size - partition->size();
    if (group->maximum_size() > 0) {
        uint64_t group_size = TotalSizeOfGroup(group);
        if (group_size >= group->maximum_size() ||
            group->maximum_size() - group_size < space_needed) {
            LERROR << "Partition " << partition->name() << " is part of group " << group->name()
                   << " which does not have enough space free (" << space_needed << "requested, "
                   << group_size << " used out of " << group->maximum_size();
            return false;
        }
    }

    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;
    DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);

@@ -441,6 +519,20 @@ std::unique_ptr<LpMetadata> MetadataBuilder::Export() {
    metadata->header = header_;
    metadata->geometry = geometry_;

    std::map<std::string, size_t> group_indices;
    for (const auto& group : groups_) {
        LpMetadataPartitionGroup out = {};

        if (group->name().size() > sizeof(out.name)) {
            LERROR << "Partition group name is too long: " << group->name();
            return nullptr;
        }
        strncpy(out.name, group->name().c_str(), sizeof(out.name));
        out.maximum_size = group->maximum_size();

        metadata->groups.push_back(out);
    }

    // Flatten the partition and extent structures into an LpMetadata, which
    // makes it very easy to validate, serialize, or pass on to device-mapper.
    for (const auto& partition : partitions_) {
@@ -475,6 +567,7 @@ std::unique_ptr<LpMetadata> MetadataBuilder::Export() {

    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());
    return metadata;
}

@@ -530,8 +623,10 @@ bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_s
        ShrinkPartition(partition, aligned_size);
    }

    LINFO << "Partition " << partition->name() << " will resize from " << old_size << " bytes to "
          << aligned_size << " bytes";
    if (partition->size() != old_size) {
        LINFO << "Partition " << partition->name() << " will resize from " << old_size
              << " bytes to " << aligned_size << " bytes";
    }
    return true;
}

+25 −0
Original line number Diff line number Diff line
@@ -495,3 +495,28 @@ TEST(liblp, AlignedFreeSpace) {
    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
    ASSERT_EQ(builder, nullptr);
}

TEST(liblp, HasDefaultGroup) {
    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
    ASSERT_NE(builder, nullptr);

    EXPECT_FALSE(builder->AddGroup("default", 0));
}

TEST(liblp, GroupSizeLimits) {
    BlockDeviceInfo device_info(1024 * 1024, 0, 0, 4096);
    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
    ASSERT_NE(builder, nullptr);

    ASSERT_TRUE(builder->AddGroup("google", 16384));

    Partition* partition = builder->AddPartition("system", "google", TEST_GUID, 0);
    ASSERT_NE(partition, nullptr);
    EXPECT_TRUE(builder->ResizePartition(partition, 8192));
    EXPECT_EQ(partition->size(), 8192);
    EXPECT_TRUE(builder->ResizePartition(partition, 16384));
    EXPECT_EQ(partition->size(), 16384);
    EXPECT_FALSE(builder->ResizePartition(partition, 32768));
    EXPECT_EQ(partition->size(), 16384);
}
+36 −1
Original line number Diff line number Diff line
@@ -95,11 +95,25 @@ class ZeroExtent final : public Extent {
    void AddTo(LpMetadata* out) const override;
};

class PartitionGroup final {
  public:
    explicit PartitionGroup(const std::string& name, uint64_t maximum_size)
        : name_(name), maximum_size_(maximum_size) {}

    const std::string& name() const { return name_; }
    uint64_t maximum_size() const { return maximum_size_; }

  private:
    std::string name_;
    uint64_t maximum_size_;
};

class Partition final {
    friend class MetadataBuilder;

  public:
    Partition(const std::string& name, const std::string& guid, uint32_t attributes);
    Partition(const std::string& name, const std::string& group_name, const std::string& guid,
              uint32_t attributes);

    // Add a raw extent.
    void AddExtent(std::unique_ptr<Extent>&& extent);
@@ -107,7 +121,12 @@ class Partition final {
    // Remove all extents from this partition.
    void RemoveExtents();

    // Compute the size used by linear extents. This is the same as size(),
    // but does not factor in extents which do not take up space.
    uint64_t BytesOnDisk() const;

    const std::string& name() const { return name_; }
    const std::string& group_name() const { return group_name_; }
    uint32_t attributes() const { return attributes_; }
    const std::string& guid() const { return guid_; }
    const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }
@@ -117,6 +136,7 @@ class Partition final {
    void ShrinkTo(uint64_t aligned_size);

    std::string name_;
    std::string group_name_;
    std::string guid_;
    std::vector<std::unique_ptr<Extent>> extents_;
    uint32_t attributes_;
@@ -156,12 +176,24 @@ class MetadataBuilder {
        return New(device_info, metadata_max_size, metadata_slot_count);
    }

    // Define a new partition group. By default there is one group called
    // "default", with an unrestricted size. A non-zero size will restrict the
    // total space used by all partitions in the group.
    //
    // This can fail and return false if the group already exists.
    bool AddGroup(const std::string& group_name, uint64_t maximum_size);

    // Export metadata so it can be serialized to an image, to disk, or mounted
    // via device-mapper.
    std::unique_ptr<LpMetadata> Export();

    // Add a partition, returning a handle so it can be sized as needed. If a
    // partition with the given name already exists, nullptr is returned.
    Partition* AddPartition(const std::string& name, const std::string& group_name,
                            const std::string& guid, uint32_t attributes);

    // Same as AddPartition above, but uses the default partition group which
    // has no size restrictions.
    Partition* AddPartition(const std::string& name, const std::string& guid, uint32_t attributes);

    // Delete a partition by name if it exists.
@@ -202,10 +234,13 @@ class MetadataBuilder {
    bool GrowPartition(Partition* partition, uint64_t aligned_size);
    void ShrinkPartition(Partition* partition, uint64_t aligned_size);
    uint64_t AlignSector(uint64_t sector);
    PartitionGroup* FindGroup(const std::string& group_name) const;
    uint64_t TotalSizeOfGroup(PartitionGroup* group) const;

    LpMetadataGeometry geometry_;
    LpMetadataHeader header_;
    std::vector<std::unique_ptr<Partition>> partitions_;
    std::vector<std::unique_ptr<PartitionGroup>> groups_;
    BlockDeviceInfo device_info_;
};

+2 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ struct LpMetadata {
    LpMetadataHeader header;
    std::vector<LpMetadataPartition> partitions;
    std::vector<LpMetadataExtent> extents;
    std::vector<LpMetadataPartitionGroup> groups;
};

// Place an initial partition table on the device. This will overwrite the
@@ -68,6 +69,7 @@ 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 GetPartitionGuid(const LpMetadataPartition& partition);
std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);

// Helper to return a slot number for a slot suffix.
uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
+19 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ extern "C" {
#define LP_METADATA_HEADER_MAGIC 0x414C5030

/* Current metadata version. */
#define LP_METADATA_MAJOR_VERSION 1
#define LP_METADATA_MAJOR_VERSION 2
#define LP_METADATA_MINOR_VERSION 0

/* Attributes for the LpMetadataPartition::attributes field.
@@ -216,6 +216,8 @@ typedef struct LpMetadataHeader {
    LpMetadataTableDescriptor partitions;
    /* 92: Extent table descriptor. */
    LpMetadataTableDescriptor extents;
    /* 104: Updateable group descriptor. */
    LpMetadataTableDescriptor groups;
} __attribute__((packed)) LpMetadataHeader;

/* This struct defines a logical partition entry, similar to what would be
@@ -245,6 +247,9 @@ typedef struct LpMetadataPartition {
     * least one extent.
     */
    uint32_t num_extents;

    /* 64: Group this partition belongs to. */
    uint32_t group_index;
} __attribute__((packed)) LpMetadataPartition;

/* This extent is a dm-linear target, and the index is an index into the
@@ -271,6 +276,19 @@ typedef struct LpMetadataExtent {
    uint64_t target_data;
} __attribute__((packed)) LpMetadataExtent;

/* This struct defines an entry in the groups table. Each group has a maximum
 * size, and partitions in a group must not exceed that size. There is always
 * a "default" group of unlimited size, which is used when not using update
 * groups or when using overlayfs or fastbootd.
 */
typedef struct LpMetadataPartitionGroup {
    /*  0: Name of this group. Any unused characters must be 0. */
    char name[36];

    /* 36: Maximum size in bytes. If 0, the group has no maximum size. */
    uint64_t maximum_size;
} LpMetadataPartitionGroup;

#ifdef __cplusplus
} /* extern "C" */
#endif
Loading