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
This commit is contained in:
parent
e256e3b7d4
commit
44b93df300
2 changed files with 99 additions and 10 deletions
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue