diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index bd1e284da..2c5bf754c 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -1619,6 +1619,18 @@ bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) { // as dm-snapshot (for example, after merge completes). bool should_unmap = current_slot != Slot::Target; bool should_delete = ShouldDeleteSnapshot(flashing_status, current_slot, name); + if (should_unmap && android::base::EndsWith(name, device_->GetSlotSuffix())) { + // Something very unexpected has happened - we want to unmap this + // snapshot, but it's on the wrong slot. We can't unmap an active + // partition. If this is not really a snapshot, skip the unmap + // step. + auto& dm = DeviceMapper::Instance(); + if (dm.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) { + LOG(ERROR) << "Detected snapshot " << name << " on " << current_slot << " slot" + << " for source partition; removing without unmap."; + should_unmap = false; + } + } bool partition_ok = true; if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) { diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 25500b5f3..6ed012941 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -2021,6 +2021,34 @@ TEST_F(SnapshotUpdateTest, MapAllSnapshots) { ASSERT_TRUE(IsPartitionUnchanged("sys_b")); } +TEST_F(SnapshotUpdateTest, CancelOnTargetSlot) { + AddOperationForPartitions(); + + // Execute the update from B->A. + test_device->set_slot_suffix("_b"); + ASSERT_TRUE(sm->BeginUpdate()); + ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_)); + + std::string path; + ASSERT_TRUE(CreateLogicalPartition( + CreateLogicalPartitionParams{ + .block_device = fake_super, + .metadata_slot = 0, + .partition_name = "sys_a", + .timeout_ms = 1s, + .partition_opener = opener_.get(), + }, + &path)); + + // Hold sys_a open so it can't be unmapped. + unique_fd fd(open(path.c_str(), O_RDONLY)); + + // Switch back to "A", make sure we can cancel. Instead of unmapping sys_a + // we should simply delete the old snapshots. + test_device->set_slot_suffix("_a"); + ASSERT_TRUE(sm->BeginUpdate()); +} + class FlashAfterUpdateTest : public SnapshotUpdateTest, public WithParamInterface> { public: