diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 55f4ed7ca..a49b0261c 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -408,6 +408,7 @@ class SnapshotManager final : public ISnapshotManager { FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow); FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow); FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery); + FRIEND_TEST(SnapshotUpdateTest, QueryStatusError); FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow); FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate); friend class SnapshotTest; diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h index 1f57bbc97..c3b40dca4 100644 --- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h @@ -100,6 +100,9 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo { return IDeviceInfo::OpenImageManager("ota/test"); } android::dm::IDeviceMapper& GetDeviceMapper() override { + if (dm_) { + return *dm_; + } return android::dm::DeviceMapper::Instance(); } @@ -111,6 +114,8 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo { } void set_recovery(bool value) { recovery_ = value; } void set_first_stage_init(bool value) { first_stage_init_ = value; } + void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; } + MergeStatus merge_status() const { return merge_status_; } private: @@ -120,6 +125,45 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo { bool recovery_ = false; bool first_stage_init_ = false; std::unordered_set unbootable_slots_; + android::dm::IDeviceMapper* dm_ = nullptr; +}; + +class DeviceMapperWrapper : public android::dm::IDeviceMapper { + using DmDeviceState = android::dm::DmDeviceState; + using DmTable = android::dm::DmTable; + + public: + DeviceMapperWrapper() : impl_(android::dm::DeviceMapper::Instance()) {} + explicit DeviceMapperWrapper(android::dm::IDeviceMapper& impl) : impl_(impl) {} + + virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path, + const std::chrono::milliseconds& timeout_ms) override { + return impl_.CreateDevice(name, table, path, timeout_ms); + } + virtual DmDeviceState GetState(const std::string& name) const override { + return impl_.GetState(name); + } + virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) { + return impl_.LoadTableAndActivate(name, table); + } + virtual bool GetTableInfo(const std::string& name, std::vector* table) { + return impl_.GetTableInfo(name, table); + } + virtual bool GetTableStatus(const std::string& name, std::vector* table) { + return impl_.GetTableStatus(name, table); + } + virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) { + return impl_.GetDmDevicePathByName(name, path); + } + virtual bool GetDeviceString(const std::string& name, std::string* dev) { + return impl_.GetDeviceString(name, dev); + } + virtual bool DeleteDeviceIfExists(const std::string& name) { + return impl_.DeleteDeviceIfExists(name); + } + + private: + android::dm::IDeviceMapper& impl_; }; class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher { diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 43c7fe2b3..d78ba0a83 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -56,6 +56,7 @@ namespace snapshot { using android::base::unique_fd; using android::dm::DeviceMapper; using android::dm::DmDeviceState; +using android::dm::IDeviceMapper; using android::fiemap::FiemapStatus; using android::fiemap::IImageManager; using android::fs_mgr::BlockDeviceInfo; @@ -911,6 +912,11 @@ class SnapshotUpdateTest : public SnapshotTest { ASSERT_TRUE(hash.has_value()); hashes_[name] = *hash; } + + // OTA client blindly unmaps all partitions that are possibly mapped. + for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { + ASSERT_TRUE(sm->UnmapUpdateSnapshot(name)); + } } void TearDown() override { RETURN_IF_NON_VIRTUAL_AB(); @@ -925,6 +931,14 @@ class SnapshotUpdateTest : public SnapshotTest { MountMetadata(); for (const auto& suffix : {"_a", "_b"}) { test_device->set_slot_suffix(suffix); + + // Cheat our way out of merge failed states. + if (sm->ProcessUpdateState() == UpdateState::MergeFailed) { + ASSERT_TRUE(AcquireLock()); + ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None)); + lock_ = {}; + } + EXPECT_TRUE(sm->CancelUpdate()) << suffix; } EXPECT_TRUE(UnmapAll()); @@ -1097,11 +1111,6 @@ class SnapshotUpdateTest : public SnapshotTest { // Also test UnmapUpdateSnapshot unmaps everything. // Also test first stage mount and merge after this. TEST_F(SnapshotUpdateTest, FullUpdateFlow) { - // OTA client blindly unmaps all partitions that are possibly mapped. - for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { - ASSERT_TRUE(sm->UnmapUpdateSnapshot(name)); - } - // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs // fit in super, but not |prd|. constexpr uint64_t partition_size = 3788_KiB; @@ -1189,11 +1198,6 @@ TEST_F(SnapshotUpdateTest, DuplicateOps) { GTEST_SKIP() << "Compression-only test"; } - // OTA client blindly unmaps all partitions that are possibly mapped. - for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { - ASSERT_TRUE(sm->UnmapUpdateSnapshot(name)); - } - // Execute the update. ASSERT_TRUE(sm->BeginUpdate()); ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_)); @@ -1239,11 +1243,6 @@ TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) { GTEST_SKIP() << "Skipping Virtual A/B Compression test"; } - // OTA client blindly unmaps all partitions that are possibly mapped. - for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { - ASSERT_TRUE(sm->UnmapUpdateSnapshot(name)); - } - auto old_sys_size = GetSize(sys_); auto old_prd_size = GetSize(prd_); @@ -1630,11 +1629,6 @@ TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) { ASSERT_NE(nullptr, metadata); ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0)); - // OTA client blindly unmaps all partitions that are possibly mapped. - for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { - ASSERT_TRUE(sm->UnmapUpdateSnapshot(name)); - } - // Add operations for sys. The whole device is written. AddOperation(sys_); @@ -2074,11 +2068,6 @@ TEST_F(SnapshotUpdateTest, LowSpace) { } TEST_F(SnapshotUpdateTest, AddPartition) { - // OTA client blindly unmaps all partitions that are possibly mapped. - for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { - ASSERT_TRUE(sm->UnmapUpdateSnapshot(name)); - } - group_->add_partition_names("dlkm"); auto dlkm = manifest_.add_partitions(); @@ -2249,6 +2238,60 @@ TEST_F(SnapshotUpdateTest, CancelOnTargetSlot) { ASSERT_TRUE(sm->BeginUpdate()); } +TEST_F(SnapshotUpdateTest, QueryStatusError) { + // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs + // fit in super, but not |prd|. + constexpr uint64_t partition_size = 3788_KiB; + SetSize(sys_, partition_size); + + AddOperationForPartitions({sys_}); + + // Execute the update. + ASSERT_TRUE(sm->BeginUpdate()); + ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_)); + ASSERT_TRUE(WriteSnapshotAndHash("sys_b")); + ASSERT_TRUE(sm->FinishedSnapshotWrites(false)); + ASSERT_TRUE(UnmapAll()); + + class DmStatusFailure final : public DeviceMapperWrapper { + public: + bool GetTableStatus(const std::string& name, std::vector* table) override { + if (!DeviceMapperWrapper::GetTableStatus(name, table)) { + return false; + } + if (name == "sys_b" && !table->empty()) { + auto& info = table->at(0); + if (DeviceMapper::GetTargetType(info.spec) == "snapshot-merge") { + info.data = "Merge failed"; + } + } + return true; + } + }; + DmStatusFailure wrapper; + + // After reboot, init does first stage mount. + auto info = new TestDeviceInfo(fake_super, "_b"); + info->set_dm(&wrapper); + + auto init = NewManagerForFirstStageMount(info); + ASSERT_NE(init, nullptr); + + ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); + ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); + + // Initiate the merge and wait for it to be completed. + ASSERT_TRUE(init->InitiateMerge()); + ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState()); + + // Simulate a reboot that tries the merge again, with the non-failing dm. + ASSERT_TRUE(UnmapAll()); + init = NewManagerForFirstStageMount("_b"); + ASSERT_NE(init, nullptr); + ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); + ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState()); +} + class FlashAfterUpdateTest : public SnapshotUpdateTest, public WithParamInterface> { public: @@ -2265,11 +2308,6 @@ class FlashAfterUpdateTest : public SnapshotUpdateTest, }; TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) { - // OTA client blindly unmaps all partitions that are possibly mapped. - for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { - ASSERT_TRUE(sm->UnmapUpdateSnapshot(name)); - } - // Execute the update. ASSERT_TRUE(sm->BeginUpdate()); ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));