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

Commit 184bb8a7 authored by Yifan Hong's avatar Yifan Hong
Browse files

libsnapshot: CreateUpdateSnapshot accepts DeltaArchiveManifest

DeltaArchiveManifest includes full update package metadata.
Now, CreateUpdateSnapshot reads the source
metadata, updates it, and write to target metadata slot as well.

Test: libsnapshot_test
Bug: 140868231

Change-Id: Ia885b336145d02111ecff1aad421cb9b1efd18c2
parent d22459df
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <libfiemap/image_manager.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <update_engine/update_metadata.pb.h>

#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -89,6 +90,7 @@ class SnapshotManager final {
    using IPartitionOpener = android::fs_mgr::IPartitionOpener;
    using LpMetadata = android::fs_mgr::LpMetadata;
    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
    using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;

  public:
    // Dependency injection for testing.
@@ -98,6 +100,7 @@ class SnapshotManager final {
        virtual std::string GetGsidDir() const = 0;
        virtual std::string GetMetadataDir() const = 0;
        virtual std::string GetSlotSuffix() const = 0;
        virtual std::string GetOtherSlotSuffix() const = 0;
        virtual std::string GetSuperDevice(uint32_t slot) const = 0;
        virtual const IPartitionOpener& GetPartitionOpener() const = 0;
        virtual bool IsOverlayfsSetup() const = 0;
@@ -169,9 +172,7 @@ class SnapshotManager final {
    // Create necessary COW device / files for OTA clients. New logical partitions will be added to
    // group "cow" in target_metadata. Regions of partitions of current_metadata will be
    // "write-protected" and snapshotted.
    bool CreateUpdateSnapshots(MetadataBuilder* target_metadata, const std::string& target_suffix,
                               MetadataBuilder* current_metadata, const std::string& current_suffix,
                               const std::map<std::string, uint64_t>& cow_sizes);
    bool CreateUpdateSnapshots(const DeltaArchiveManifest& manifest);

    // Map a snapshotted partition for OTA clients to write to. Write-protected regions are
    // determined previously in CreateSnapshots.
+12 −8
Original line number Diff line number Diff line
@@ -25,6 +25,9 @@ using android::fs_mgr::Extent;
using android::fs_mgr::Interval;
using android::fs_mgr::kDefaultBlockSize;
using android::fs_mgr::Partition;
using chromeos_update_engine::InstallOperation;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;

namespace android {
namespace snapshot {
@@ -117,9 +120,15 @@ std::optional<uint64_t> PartitionCowCreator::GetSnapshotSize() {
    return snapshot_size;
}

std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
std::optional<uint64_t> PartitionCowCreator::GetCowSize(uint64_t snapshot_size) {
    // TODO: Use |operations|. to determine a minimum COW size.
    // kCowEstimateFactor is good for prototyping but we can't use that in production.
    static constexpr double kCowEstimateFactor = 1.05;
    auto cow_size = RoundUp(snapshot_size * kCowEstimateFactor, kDefaultBlockSize);
    return cow_size;
}

std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);

@@ -135,13 +144,8 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {

    ret.snapshot_status.snapshot_size = *snapshot_size;

    // TODO: always read from cow_size when the COW size is written in
    // update package. kCowEstimateFactor is good for prototyping but
    // we can't use that in production.
    if (!cow_size.has_value()) {
        cow_size =
                RoundUp(ret.snapshot_status.snapshot_size * kCowEstimateFactor, kDefaultBlockSize);
    }
    auto cow_size = GetCowSize(*snapshot_size);
    if (!cow_size.has_value()) return std::nullopt;

    // Compute regions that are free in both current and target metadata. These are the regions
    // we can use for COW partition.
+9 −5
Original line number Diff line number Diff line
@@ -32,20 +32,23 @@ struct PartitionCowCreator {
    using Interval = android::fs_mgr::Interval;
    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
    using Partition = android::fs_mgr::Partition;
    using InstallOperation = chromeos_update_engine::InstallOperation;
    template <typename T>
    using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;

    // The metadata that will be written to target metadata slot.
    MetadataBuilder* target_metadata;
    MetadataBuilder* target_metadata = nullptr;
    // The suffix of the target slot.
    std::string target_suffix;
    // The partition in target_metadata that needs to be snapshotted.
    Partition* target_partition;
    Partition* target_partition = nullptr;
    // The metadata at the current slot (that would be used if the device boots
    // normally). This is used to determine which extents are being used.
    MetadataBuilder* current_metadata;
    MetadataBuilder* current_metadata = nullptr;
    // The suffix of the current slot.
    std::string current_suffix;
    // The COW size given by client code.
    std::optional<uint64_t> cow_size;
    // List of operations to be applied on the partition.
    const RepeatedPtrField<InstallOperation>* operations = nullptr;

    struct Return {
        SnapshotManager::SnapshotStatus snapshot_status;
@@ -57,6 +60,7 @@ struct PartitionCowCreator {
  private:
    bool HasExtent(Partition* p, Extent* e);
    std::optional<uint64_t> GetSnapshotSize();
    std::optional<uint64_t> GetCowSize(uint64_t snapshot_size);
};

}  // namespace snapshot
+1 −4
Original line number Diff line number Diff line
@@ -76,14 +76,11 @@ TEST(PartitionCowCreator, IntersectSelf) {
                                .target_suffix = "_b",
                                .target_partition = system_b,
                                .current_metadata = builder_a.get(),
                                .current_suffix = "_a",
                                .cow_size = 20 * 1024};
                                .current_suffix = "_a"};
    auto ret = creator.Run();
    ASSERT_TRUE(ret.has_value());
    ASSERT_EQ(40 * 1024, ret->snapshot_status.device_size);
    ASSERT_EQ(40 * 1024, ret->snapshot_status.snapshot_size);
    ASSERT_EQ(20 * 1024,
              ret->snapshot_status.cow_file_size + ret->snapshot_status.cow_partition_size);
}

}  // namespace snapshot
+52 −20
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <liblp/liblp.h>

#include "partition_cow_creator.h"
#include "snapshot_metadata_updater.h"
#include "utility.h"

namespace android {
@@ -61,6 +62,10 @@ using android::fs_mgr::GetPartitionName;
using android::fs_mgr::LpMetadata;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::SlotNumberForSlotSuffix;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::InstallOperation;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
using std::chrono::duration_cast;
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -74,6 +79,7 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo {
    std::string GetGsidDir() const override { return "ota"s; }
    std::string GetMetadataDir() const override { return "/metadata/ota"s; }
    std::string GetSlotSuffix() const override { return fs_mgr_get_slot_suffix(); }
    std::string GetOtherSlotSuffix() const override { return fs_mgr_get_other_slot_suffix(); }
    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const { return opener_; }
    std::string GetSuperDevice(uint32_t slot) const override {
        return fs_mgr_get_super_partition_name(slot);
@@ -1713,26 +1719,44 @@ bool SnapshotManager::ForceLocalImageManager() {
    return true;
}

bool SnapshotManager::CreateUpdateSnapshots(MetadataBuilder* target_metadata,
                                            const std::string& target_suffix,
                                            MetadataBuilder* current_metadata,
                                            const std::string& current_suffix,
                                            const std::map<std::string, uint64_t>& cow_sizes) {
bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
    auto lock = LockExclusive();
    if (!lock) return false;

    // Add _{target_suffix} to COW size map.
    std::map<std::string, uint64_t> suffixed_cow_sizes;
    for (const auto& [name, size] : cow_sizes) {
        suffixed_cow_sizes[name + target_suffix] = size;
    const auto& opener = device_->GetPartitionOpener();
    auto current_suffix = device_->GetSlotSuffix();
    uint32_t current_slot = SlotNumberForSlotSuffix(current_suffix);
    auto target_suffix = device_->GetOtherSlotSuffix();
    uint32_t target_slot = SlotNumberForSlotSuffix(target_suffix);
    auto current_super = device_->GetSuperDevice(current_slot);

    auto current_metadata = MetadataBuilder::New(opener, current_super, current_slot);
    auto target_metadata =
            MetadataBuilder::NewForUpdate(opener, current_super, current_slot, target_slot);

    SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);
    if (!metadata_updater.Update()) {
        LOG(ERROR) << "Cannot calculate new metadata.";
        return false;
    }

    target_metadata->RemoveGroupAndPartitions(kCowGroupName);
    if (!target_metadata->AddGroup(kCowGroupName, 0)) {
        LOG(ERROR) << "Cannot add group " << kCowGroupName;
        return false;
    }

    std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
    for (const auto& partition_update : manifest.partitions()) {
        auto suffixed_name = partition_update.partition_name() + target_suffix;
        auto&& [it, inserted] = install_operation_map.emplace(std::move(suffixed_name),
                                                              &partition_update.operations());
        if (!inserted) {
            LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
                       << " in update manifest.";
            return false;
        }
    }

    // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
    // partition takes up a big chunk of space in super, causing COW images to be created on
    // retrofit Virtual A/B devices.
@@ -1757,18 +1781,16 @@ bool SnapshotManager::CreateUpdateSnapshots(MetadataBuilder* target_metadata,
    // these devices.
    AutoDeviceList created_devices;

    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
        std::optional<uint64_t> cow_size = std::nullopt;
        auto it = suffixed_cow_sizes.find(target_partition->name());
        if (it != suffixed_cow_sizes.end()) {
            cow_size = it->second;
            LOG(INFO) << "Using provided COW size " << *cow_size << " for partition "
                      << target_partition->name();
    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata.get(), target_suffix)) {
        const RepeatedPtrField<InstallOperation>* operations = nullptr;
        auto operations_it = install_operation_map.find(target_partition->name());
        if (operations_it != install_operation_map.end()) {
            operations = operations_it->second;
        }

        // Compute the device sizes for the partition.
        PartitionCowCreator cow_creator{target_metadata,  target_suffix,  target_partition,
                                        current_metadata, current_suffix, cow_size};
        PartitionCowCreator cow_creator{target_metadata.get(),  target_suffix,  target_partition,
                                        current_metadata.get(), current_suffix, operations};
        auto cow_creator_ret = cow_creator.Run();
        if (!cow_creator_ret.has_value()) {
            return false;
@@ -1846,13 +1868,17 @@ bool SnapshotManager::CreateUpdateSnapshots(MetadataBuilder* target_metadata,

    auto& dm = DeviceMapper::Instance();
    auto exported_target_metadata = target_metadata->Export();
    if (exported_target_metadata == nullptr) {
        LOG(ERROR) << "Cannot export target metadata";
        return false;
    }
    CreateLogicalPartitionParams cow_params{
            .block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
            .metadata = exported_target_metadata.get(),
            .timeout_ms = std::chrono::milliseconds::max(),
            .partition_opener = &device_->GetPartitionOpener(),
    };
    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata.get(), target_suffix)) {
        AutoDeviceList created_devices_for_cow;

        if (!UnmapPartitionWithSnapshot(lock.get(), target_partition->name())) {
@@ -1884,6 +1910,12 @@ bool SnapshotManager::CreateUpdateSnapshots(MetadataBuilder* target_metadata,
        // Let destructor of created_devices_for_cow to unmap the COW devices.
    };

    if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
                              *exported_target_metadata, target_slot)) {
        LOG(ERROR) << "Cannot write target metadata";
        return false;
    }

    created_devices.Release();
    LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;

Loading