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

Commit 83fe0a35 authored by Yifan Hong's avatar Yifan Hong
Browse files

libsnapshot: Add prolog to RemoveAllUpdateStates.

Add an optional prolog arg (function<bool()>) that is invoked
before snapshots are deleted and update state set to none.

This allows update_engine to delete markers before deleting snapshots
to avoid depending on the erroneous markers. Otherwise, if update_engine
delete markers after libsnapshot deletes update states, the device could
technically get into a state where update_engine thinks the update has
been applied, but snapshots are gone.

Bug: 147696014
Test: libsnapshot_test

Change-Id: I71bfc04a81ea4f94b3072558be50d2f80565113e
Merged-In: I71bfc04a81ea4f94b3072558be50d2f80565113e
parent 91ffe19c
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -169,7 +169,8 @@ class SnapshotManager final {
    //
    // The optional callback allows the caller to periodically check the
    // progress with GetUpdateState().
    UpdateState ProcessUpdateState(const std::function<void()>& callback = {});
    UpdateState ProcessUpdateState(const std::function<void()>& callback = {},
                                   const std::function<bool()>& before_cancel = {});

  public:
    // Initiate the merge if necessary, then wait for the merge to finish.
@@ -179,7 +180,8 @@ class SnapshotManager final {
    //   - Unverified if called on the source slot
    //   - MergeCompleted if merge is completed
    //   - other states indicating an error has occurred
    UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr);
    UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr,
                                     const std::function<bool()>& before_cancel = {});

    // Wait for the merge if rebooted into the new slot. Does NOT initiate a
    // merge. If the merge has not been initiated (but should be), wait.
@@ -375,14 +377,14 @@ class SnapshotManager final {

    // Check for a cancelled or rolled back merge, returning true if such a
    // condition was detected and handled.
    bool HandleCancelledUpdate(LockedFile* lock);
    bool HandleCancelledUpdate(LockedFile* lock, const std::function<bool()>& before_cancel);

    // Helper for HandleCancelledUpdate. Assumes booting from new slot.
    bool AreAllSnapshotsCancelled(LockedFile* lock);

    // Remove artifacts created by the update process, such as snapshots, and
    // set the update state to None.
    bool RemoveAllUpdateState(LockedFile* lock);
    bool RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog = {});

    // Interact with /metadata/ota.
    std::unique_ptr<LockedFile> OpenLock(int lock_flags);
@@ -437,8 +439,8 @@ class SnapshotManager final {
    //   UpdateState::MergeCompleted
    //   UpdateState::MergeFailed
    //   UpdateState::MergeNeedsReboot
    UpdateState CheckMergeState();
    UpdateState CheckMergeState(LockedFile* lock);
    UpdateState CheckMergeState(const std::function<bool()>& before_cancel);
    UpdateState CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
    UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);

    // Interact with status files under /metadata/ota/snapshots.
+22 −13
Original line number Diff line number Diff line
@@ -219,7 +219,12 @@ static bool RemoveFileIfExists(const std::string& path) {
    return true;
}

bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog) {
    if (prolog && !prolog()) {
        LOG(WARNING) << "Can't RemoveAllUpdateState: prolog failed.";
        return false;
    }

    LOG(INFO) << "Removing all update state.";

#ifdef LIBSNAPSHOT_USE_CALLSTACK
@@ -789,9 +794,10 @@ bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::strin
// Note that when a merge fails, we will *always* try again to complete the
// merge each time the device boots. There is no harm in doing so, and if
// the problem was transient, we might manage to get a new outcome.
UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& callback) {
UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& callback,
                                                const std::function<bool()>& before_cancel) {
    while (true) {
        UpdateState state = CheckMergeState();
        UpdateState state = CheckMergeState(before_cancel);
        if (state == UpdateState::MergeFailed) {
            AcknowledgeMergeFailure();
        }
@@ -811,24 +817,25 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& cal
    }
}

UpdateState SnapshotManager::CheckMergeState() {
UpdateState SnapshotManager::CheckMergeState(const std::function<bool()>& before_cancel) {
    auto lock = LockExclusive();
    if (!lock) {
        return UpdateState::MergeFailed;
    }

    UpdateState state = CheckMergeState(lock.get());
    UpdateState state = CheckMergeState(lock.get(), before_cancel);
    if (state == UpdateState::MergeCompleted) {
        // Do this inside the same lock. Failures get acknowledged without the
        // lock, because flock() might have failed.
        AcknowledgeMergeSuccess(lock.get());
    } else if (state == UpdateState::Cancelled) {
        RemoveAllUpdateState(lock.get());
        RemoveAllUpdateState(lock.get(), before_cancel);
    }
    return state;
}

UpdateState SnapshotManager::CheckMergeState(LockedFile* lock) {
UpdateState SnapshotManager::CheckMergeState(LockedFile* lock,
                                             const std::function<bool()>& before_cancel) {
    UpdateState state = ReadUpdateState(lock);
    switch (state) {
        case UpdateState::None:
@@ -849,7 +856,7 @@ UpdateState SnapshotManager::CheckMergeState(LockedFile* lock) {
            // This is an edge case. Normally cancelled updates are detected
            // via the merge poll below, but if we never started a merge, we
            // need to also check here.
            if (HandleCancelledUpdate(lock)) {
            if (HandleCancelledUpdate(lock, before_cancel)) {
                return UpdateState::Cancelled;
            }
            return state;
@@ -1169,7 +1176,8 @@ bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
    return true;
}

bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock,
                                            const std::function<bool()>& before_cancel) {
    auto slot = GetCurrentSlot();
    if (slot == Slot::Unknown) {
        return false;
@@ -1177,7 +1185,7 @@ bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {

    // If all snapshots were reflashed, then cancel the entire update.
    if (AreAllSnapshotsCancelled(lock)) {
        RemoveAllUpdateState(lock);
        RemoveAllUpdateState(lock, before_cancel);
        return true;
    }

@@ -2388,7 +2396,8 @@ std::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() {
    return AutoUnmountDevice::New(device_->GetMetadataDir());
}

UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report) {
UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report,
                                                  const std::function<bool()>& before_cancel) {
    {
        auto lock = LockExclusive();
        // Sync update state from file with bootloader.
@@ -2413,7 +2422,7 @@ UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_rep
    LOG(INFO) << "Waiting for any previous merge request to complete. "
              << "This can take up to several minutes.";
    merge_stats.Resume();
    auto state = ProcessUpdateState(callback);
    auto state = ProcessUpdateState(callback, before_cancel);
    merge_stats.set_state(state);
    if (state == UpdateState::None) {
        LOG(INFO) << "Can't find any snapshot to merge.";
@@ -2436,7 +2445,7 @@ UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_rep
        // All other states can be handled by ProcessUpdateState.
        LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
        last_progress = 0;
        state = ProcessUpdateState(callback);
        state = ProcessUpdateState(callback, before_cancel);
        merge_stats.set_state(state);
    }