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
This commit is contained in:
Yifan Hong 2020-02-18 15:04:28 -08:00
parent 6d2a79839f
commit ad74121bda
2 changed files with 30 additions and 19 deletions

View file

@ -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.

View file

@ -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;
}
@ -2391,7 +2399,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.
@ -2416,7 +2425,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.";
@ -2439,7 +2448,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);
}