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

Commit 0a6f2502 authored by David Anderson's avatar David Anderson
Browse files

libsnapshot: Fix missing source partitions when adding a new partition.

When adding a new partition, don't attempt to map a source partition
during boot, because none exists. Instead use the base device.

Bug: 196922070
Test: vts_libsnapshot_test
Change-Id: Ice6015237b0a76a0210819994433e52159376393
Merged-In: Ice6015237b0a76a0210819994433e52159376393
parent d5a663fd
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -399,6 +399,7 @@ class SnapshotManager final : public ISnapshotManager {
    FRIEND_TEST(SnapshotTest, MergeFailureCode);
    FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
    FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
    FRIEND_TEST(SnapshotUpdateTest, AddPartition);
    FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
    FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
    FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
+11 −7
Original line number Diff line number Diff line
@@ -2091,6 +2091,7 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,
    if (live_snapshot_status->compression_enabled()) {
        // Get the source device (eg the view of the partition from before it was resized).
        std::string source_device_path;
        if (live_snapshot_status->old_partition_size() > 0) {
            if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,
                                 &source_device_path)) {
                LOG(ERROR) << "Could not map source device for: " << cow_name;
@@ -2099,6 +2100,9 @@ bool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,

            auto source_device = GetSourceDeviceName(params.GetPartitionName());
            created_devices.EmplaceBack<AutoUnmapDevice>(&dm, source_device);
        } else {
            source_device_path = base_path;
        }

        if (!WaitForDevice(source_device_path, remaining_time)) {
            return false;
+65 −1
Original line number Diff line number Diff line
@@ -963,7 +963,7 @@ class SnapshotUpdateTest : public SnapshotTest {
    }

    AssertionResult UnmapAll() {
        for (const auto& name : {"sys", "vnd", "prd"}) {
        for (const auto& name : {"sys", "vnd", "prd", "dlkm"}) {
            if (!dm_.DeleteDeviceIfExists(name + "_a"s)) {
                return AssertionFailure() << "Cannot unmap " << name << "_a";
            }
@@ -2026,6 +2026,70 @@ TEST_F(SnapshotUpdateTest, LowSpace) {
    ASSERT_LT(res.required_size(), 40_MiB);
}

TEST_F(SnapshotUpdateTest, AddPartition) {
    // OTA client blindly unmaps all partitions that are possibly mapped.
    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
        ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
    }

    group_->add_partition_names("dlkm");

    auto dlkm = manifest_.add_partitions();
    dlkm->set_partition_name("dlkm");
    dlkm->set_estimate_cow_size(2_MiB);
    SetSize(dlkm, 3_MiB);

    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs
    // fit in super, but not |prd|.
    constexpr uint64_t partition_size = 3788_KiB;
    SetSize(sys_, partition_size);
    SetSize(vnd_, partition_size);
    SetSize(prd_, partition_size);
    SetSize(dlkm, partition_size);

    AddOperationForPartitions({sys_, vnd_, prd_, dlkm});

    // Execute the update.
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));

    // Write some data to target partitions.
    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
        ASSERT_TRUE(WriteSnapshotAndHash(name));
    }

    // Assert that source partitions aren't affected.
    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
        ASSERT_TRUE(IsPartitionUnchanged(name));
    }

    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));

    // Simulate shutting down the device.
    ASSERT_TRUE(UnmapAll());

    // After reboot, init does first stage mount.
    auto init = NewManagerForFirstStageMount("_b");
    ASSERT_NE(init, nullptr);
    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));

    // Check that the target partitions have the same content.
    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
        ASSERT_TRUE(IsPartitionUnchanged(name));
    }

    // Initiate the merge and wait for it to be completed.
    ASSERT_TRUE(init->InitiateMerge());
    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());

    // Check that the target partitions have the same content after the merge.
    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
        ASSERT_TRUE(IsPartitionUnchanged(name))
                << "Content of " << name << " changes after the merge";
    }
}

class AutoKill final {
  public:
    explicit AutoKill(pid_t pid) : pid_(pid) {}