diff options
author | Yifan Hong <elsk@google.com> | 2020-02-20 19:51:05 -0800 |
---|---|---|
committer | Yifan Hong <elsk@google.com> | 2020-03-05 08:41:44 -0800 |
commit | 7c5ae0a12ef22f9dba005da9db13a8f37945f1a9 (patch) | |
tree | aa3333966b2b529ed85017b4bd01638a1f571a97 | |
parent | ae2558d900d5eafe2a511f8debe72a8564f5f966 (diff) |
libsnapshot: remove snapshots properly after flashing
If updated then immediately flashed target slot at unverified
stage, update_engine attempts to call RemoveAllSnapshots, but
it used to fail because unmapping fails. In reality, these devices
are dm-linear targets, so unmapping them is optional.
- Introduce a GetSnapshotFlashingStatus function that expose more
information than AreAllSnapshotsCancelled. We can use the returned
map<string, bool> to determine whether a partition is flashed or
not.
- Introduce ShouldUnmapDeleteSnapshot which determines whether
unmapping / deleteting a snapshot is needed in RemoveAllSnapshots.
Test: apply OTA, flash target slot, then inspect logs
Bug: 147696014
Change-Id: I0853d1e213566af2a3401e46fac7d9586cee7aaf
Merged-In: I0853d1e213566af2a3401e46fac7d9586cee7aaf
-rw-r--r-- | fs_mgr/libsnapshot/include/libsnapshot/snapshot.h | 14 | ||||
-rw-r--r-- | fs_mgr/libsnapshot/snapshot.cpp | 95 |
2 files changed, 99 insertions, 10 deletions
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 72f1d91de..1c9b9e525 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -384,6 +384,15 @@ class SnapshotManager final { // Helper for HandleCancelledUpdate. Assumes booting from new slot. bool AreAllSnapshotsCancelled(LockedFile* lock); + // Determine whether partition names in |snapshots| have been flashed and + // store result to |out|. + // Return true if values are successfully retrieved and false on error + // (e.g. super partition metadata cannot be read). When it returns true, + // |out| stores true for partitions that have been flashed and false for + // partitions that have not been flashed. + bool GetSnapshotFlashingStatus(LockedFile* lock, const std::vector<std::string>& snapshots, + std::map<std::string, bool>* out); + // Remove artifacts created by the update process, such as snapshots, and // set the update state to None. bool RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog = {}); @@ -517,6 +526,11 @@ class SnapshotManager final { std::string ReadUpdateSourceSlotSuffix(); + // Helper for RemoveAllSnapshots. + // Check whether |name| should be deleted as a snapshot name. + bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status, + Slot current_slot, const std::string& name); + std::string gsid_dir_; std::string metadata_dir_; std::unique_ptr<IDeviceInfo> device_; diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index c51c977db..e13fc879f 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -1244,6 +1244,28 @@ bool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) { return true; } + std::map<std::string, bool> flashing_status; + + if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) { + LOG(WARNING) << "Failed to determine whether partitions have been flashed. Not" + << "removing update states."; + return false; + } + + bool all_snapshots_cancelled = std::all_of(flashing_status.begin(), flashing_status.end(), + [](const auto& pair) { return pair.second; }); + + if (all_snapshots_cancelled) { + LOG(WARNING) << "All partitions are re-flashed after update, removing all update states."; + } + return all_snapshots_cancelled; +} + +bool SnapshotManager::GetSnapshotFlashingStatus(LockedFile* lock, + const std::vector<std::string>& snapshots, + std::map<std::string, bool>* out) { + CHECK(lock); + auto source_slot_suffix = ReadUpdateSourceSlotSuffix(); if (source_slot_suffix.empty()) { return false; @@ -1269,20 +1291,17 @@ bool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) { return false; } - bool all_snapshots_cancelled = true; for (const auto& snapshot_name : snapshots) { if (GetMetadataPartitionState(*metadata, snapshot_name) == MetadataPartitionState::Updated) { - all_snapshots_cancelled = false; - continue; + out->emplace(snapshot_name, false); + } else { + // Delete snapshots for partitions that are re-flashed after the update. + LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << "."; + out->emplace(snapshot_name, true); } - // Delete snapshots for partitions that are re-flashed after the update. - LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << "."; - } - if (all_snapshots_cancelled) { - LOG(WARNING) << "All partitions are re-flashed after update, removing all update states."; } - return all_snapshots_cancelled; + return true; } bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) { @@ -1292,10 +1311,38 @@ bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) { return false; } + std::map<std::string, bool> flashing_status; + if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) { + LOG(WARNING) << "Failed to get flashing status"; + } + + auto current_slot = GetCurrentSlot(); bool ok = true; bool has_mapped_cow_images = false; for (const auto& name : snapshots) { - if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) { + // If booting off source slot, it is okay to unmap and delete all the snapshots. + // If boot indicator is missing, update state is None or Initiated, so + // it is also okay to unmap and delete all the snapshots. + // If booting off target slot, + // - should not unmap because: + // - In Android mode, snapshots are not mapped, but + // filesystems are mounting off dm-linear targets directly. + // - In recovery mode, assume nothing is mapped, so it is optional to unmap. + // - If partition is flashed or unknown, it is okay to delete snapshots. + // Otherwise (UPDATED flag), only delete snapshots if they are not mapped + // as dm-snapshot (for example, after merge completes). + bool should_unmap = current_slot != Slot::Target; + bool should_delete = ShouldDeleteSnapshot(lock, flashing_status, current_slot, name); + + bool partition_ok = true; + if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) { + partition_ok = false; + } + if (partition_ok && should_delete && !DeleteSnapshot(lock, name)) { + partition_ok = false; + } + + if (!partition_ok) { // Remember whether or not we were able to unmap the cow image. auto cow_image_device = GetCowImageDeviceName(name); has_mapped_cow_images |= @@ -1318,6 +1365,34 @@ bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) { return ok; } +// See comments in RemoveAllSnapshots(). +bool SnapshotManager::ShouldDeleteSnapshot(LockedFile* lock, + const std::map<std::string, bool>& flashing_status, + Slot current_slot, const std::string& name) { + if (current_slot != Slot::Target) { + return true; + } + auto it = flashing_status.find(name); + if (it == flashing_status.end()) { + LOG(WARNING) << "Can't determine flashing status for " << name; + return true; + } + if (it->second) { + // partition flashed, okay to delete obsolete snapshots + return true; + } + // partition updated, only delete if not dm-snapshot + SnapshotStatus status; + if (!ReadSnapshotStatus(lock, name, &status)) { + LOG(WARNING) << "Unable to read snapshot status for " << name + << ", guessing snapshot device name"; + auto extra_name = GetSnapshotExtraDeviceName(name); + return !IsSnapshotDevice(name) && !IsSnapshotDevice(extra_name); + } + auto dm_name = GetSnapshotDeviceName(name, status); + return !IsSnapshotDevice(dm_name); +} + UpdateState SnapshotManager::GetUpdateState(double* progress) { // If we've never started an update, the state file won't exist. auto state_file = GetStateFilePath(); |