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

Commit 65e3d0a9 authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 6473661 from 1cf1238d to rvc-release

Change-Id: I7b14cc60e0bfdd15a55914c00a7fe1ecfc8c563c
parents e86fd601 1cf1238d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -625,7 +625,7 @@ bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string
        if (!sm) {
            return device->WriteFail("Unable to create SnapshotManager");
        }
        if (!sm->HandleImminentDataWipe()) {
        if (!sm->FinishMergeInRecovery()) {
            return device->WriteFail("Unable to finish snapshot merge");
        }
    } else {
+0 −126
Original line number Diff line number Diff line
@@ -131,132 +131,6 @@ TEST_F(NativeTest, GetMappedImageDevice) {
    ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
}

// This fixture is for tests against a simulated device environment. Rather
// than use /data, we create an image and then layer a new filesystem within
// it. Each test then decides how to mount and create layered images. This
// allows us to test FBE vs FDE configurations.
class ImageTest : public ::testing::Test {
  public:
    ImageTest() : dm_(DeviceMapper::Instance()) {}

    void SetUp() override {
        manager_ = ImageManager::Open(kMetadataPath, gDataPath);
        ASSERT_NE(manager_, nullptr);

        manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());

        submanager_ = ImageManager::Open(kMetadataPath + "/mnt"s, gDataPath + "/mnt"s);
        ASSERT_NE(submanager_, nullptr);

        submanager_->set_partition_opener(std::make_unique<TestPartitionOpener>());

        // Ensure that metadata is cleared in between runs.
        submanager_->RemoveAllImages();
        manager_->RemoveAllImages();

        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
        base_name_ = tinfo->name();
        test_image_name_ = base_name_ + "-base";
        wrapper_device_name_ = base_name_ + "-wrapper";

        ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize * 16, false, nullptr));
        ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &base_device_));
    }

    void TearDown() override {
        submanager_->UnmapImageDevice(test_image_name_);
        umount(gDataMountPath.c_str());
        dm_.DeleteDeviceIfExists(wrapper_device_name_);
        manager_->UnmapImageDevice(base_name_);
        manager_->DeleteBackingImage(base_name_);
    }

  protected:
    bool DoFormat(const std::string& device) {
        // clang-format off
        std::vector<std::string> mkfs_args = {
            "/system/bin/mke2fs",
            "-F",
            "-b 4096",
            "-t ext4",
            "-m 0",
            "-O has_journal",
            device,
            ">/dev/null",
            "2>/dev/null",
            "</dev/null",
        };
        // clang-format on
        auto command = android::base::Join(mkfs_args, " ");
        return system(command.c_str()) == 0;
    }

    std::unique_ptr<ImageManager> manager_;
    std::unique_ptr<ImageManager> submanager_;

    DeviceMapper& dm_;
    std::string base_name_;
    std::string base_device_;
    std::string test_image_name_;
    std::string wrapper_device_name_;
};

TEST_F(ImageTest, DirectMount) {
    ASSERT_TRUE(DoFormat(base_device_));
    ASSERT_EQ(mount(base_device_.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);
    ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));

    std::string path;
    ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
    ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/loop"));
}

TEST_F(ImageTest, IndirectMount) {
#ifdef SKIP_TEST_IN_PRESUBMIT
    GTEST_SKIP() << "WIP failure b/148874852";
#endif
    // Create a simple wrapper around the base device that we'll mount from
    // instead. This will simulate the code paths for dm-crypt/default-key/bow
    // and force us to use device-mapper rather than loop devices.
    uint64_t device_size = 0;
    {
        unique_fd fd(open(base_device_.c_str(), O_RDWR | O_CLOEXEC));
        ASSERT_GE(fd, 0);
        device_size = get_block_device_size(fd);
        ASSERT_EQ(device_size, kTestImageSize * 16);
    }
    uint64_t num_sectors = device_size / 512;

    auto& dm = DeviceMapper::Instance();

    DmTable table;
    table.Emplace<DmTargetLinear>(0, num_sectors, base_device_, 0);
    ASSERT_TRUE(dm.CreateDevice(wrapper_device_name_, table));

    // Format and mount.
    std::string wrapper_device;
    ASSERT_TRUE(dm.GetDmDevicePathByName(wrapper_device_name_, &wrapper_device));
    ASSERT_TRUE(WaitForFile(wrapper_device, 5s));
    ASSERT_TRUE(DoFormat(wrapper_device));
    ASSERT_EQ(mount(wrapper_device.c_str(), gDataMountPath.c_str(), "ext4", 0, nullptr), 0);

    ASSERT_TRUE(submanager_->CreateBackingImage(test_image_name_, kTestImageSize, false, nullptr));

    std::set<std::string> backing_devices;
    auto init = [&](std::set<std::string> devices) -> bool {
        backing_devices = std::move(devices);
        return true;
    };

    std::string path;
    ASSERT_TRUE(submanager_->MapImageDevice(test_image_name_, 5s, &path));
    ASSERT_TRUE(android::base::StartsWith(path, "/dev/block/dm-"));
    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
    ASSERT_TRUE(submanager_->MapAllImages(init));
    ASSERT_FALSE(backing_devices.empty());
    ASSERT_TRUE(submanager_->UnmapImageDevice(test_image_name_));
}

bool Mkdir(const std::string& path) {
    if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
        std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
+5 −0
Original line number Diff line number Diff line
@@ -223,6 +223,10 @@ class SnapshotManager final {
    // optional callback fires periodically to query progress via GetUpdateState.
    bool HandleImminentDataWipe(const std::function<void()>& callback = {});

    // Force a merge to complete in recovery. This is similar to HandleImminentDataWipe
    // but does not expect a data wipe after.
    bool FinishMergeInRecovery();

    // This method is only allowed in recovery and is used as a helper to
    // initialize the snapshot devices as a requirement to mount a snapshotted
    // /system in recovery.
@@ -541,6 +545,7 @@ class SnapshotManager final {
    std::unique_ptr<IDeviceInfo> device_;
    std::unique_ptr<IImageManager> images_;
    bool has_local_image_manager_ = false;
    bool in_factory_data_reset_ = false;
};

}  // namespace snapshot
+54 −1
Original line number Diff line number Diff line
@@ -577,8 +577,16 @@ bool SnapshotManager::InitiateMerge() {
        return false;
    }

    auto other_suffix = device_->GetOtherSlotSuffix();

    auto& dm = DeviceMapper::Instance();
    for (const auto& snapshot : snapshots) {
        if (android::base::EndsWith(snapshot, other_suffix)) {
            // Allow the merge to continue, but log this unexpected case.
            LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
            continue;
        }

        // The device has to be mapped, since everything should be merged at
        // the same time. This is a fairly serious error. We could forcefully
        // map everything here, but it should have been mapped during first-
@@ -1008,6 +1016,15 @@ std::string SnapshotManager::GetForwardMergeIndicatorPath() {
}

void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
    // It's not possible to remove update state in recovery, so write an
    // indicator that cleanup is needed on reboot. If a factory data reset
    // was requested, it doesn't matter, everything will get wiped anyway.
    // To make testing easier we consider a /data wipe as cleaned up.
    if (device_->IsRecovery() && !in_factory_data_reset_) {
        WriteUpdateState(lock, UpdateState::MergeCompleted);
        return;
    }

    RemoveAllUpdateState(lock);
}

@@ -2528,7 +2545,43 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba
        }
        return true;
    };
    if (!ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback)) {

    in_factory_data_reset_ = true;
    bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
    in_factory_data_reset_ = false;

    if (!ok) {
        return false;
    }

    // Nothing should be depending on partitions now, so unmap them all.
    if (!UnmapAllPartitions()) {
        LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
    }
    return true;
}

bool SnapshotManager::FinishMergeInRecovery() {
    if (!device_->IsRecovery()) {
        LOG(ERROR) << "Data wipes are only allowed in recovery.";
        return false;
    }

    auto mount = EnsureMetadataMounted();
    if (!mount || !mount->HasDevice()) {
        return false;
    }

    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
    auto super_path = device_->GetSuperDevice(slot_number);
    if (!CreateLogicalAndSnapshotPartitions(super_path)) {
        LOG(ERROR) << "Unable to map partitions to complete merge.";
        return false;
    }

    UpdateState state = ProcessUpdateState();
    if (state != UpdateState::MergeCompleted) {
        LOG(ERROR) << "Merge returned unexpected status: " << state;
        return false;
    }

+46 −0
Original line number Diff line number Diff line
@@ -1454,6 +1454,52 @@ TEST_F(SnapshotUpdateTest, MergeInRecovery) {
    ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
}

// Test that a merge does not clear the snapshot state in fastboot.
TEST_F(SnapshotUpdateTest, MergeInFastboot) {
    // Execute the first update.
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
    ASSERT_TRUE(MapUpdateSnapshots());
    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));

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

    // After reboot, init does first stage mount.
    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
    ASSERT_NE(init, nullptr);
    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
    init = nullptr;

    // Initiate the merge and then immediately stop it to simulate a reboot.
    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
    ASSERT_TRUE(new_sm->InitiateMerge());
    ASSERT_TRUE(UnmapAll());

    // Simulate a reboot into recovery.
    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
    test_device->set_recovery(true);
    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());

    ASSERT_TRUE(new_sm->FinishMergeInRecovery());

    auto mount = new_sm->EnsureMetadataMounted();
    ASSERT_TRUE(mount && mount->HasDevice());
    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);

    // Finish the merge in a normal boot.
    test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
    init = SnapshotManager::NewForFirstStageMount(test_device.release());
    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
    init = nullptr;

    test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
    new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
}

// Test that after an OTA, before a merge, we can wipe data in recovery.
TEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {
    // Execute the first update.
Loading