Loading fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +1 −0 Original line number Diff line number Diff line Loading @@ -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; Loading fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +44 −0 Original line number Diff line number Diff line Loading @@ -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(); } Loading @@ -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: Loading @@ -120,6 +125,45 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo { bool recovery_ = false; bool first_stage_init_ = false; std::unordered_set<uint32_t> 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<TargetInfo>* table) { return impl_.GetTableInfo(name, table); } virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* 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 { Loading fs_mgr/libsnapshot/snapshot_test.cpp +68 −30 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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()); Loading Loading @@ -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; Loading Loading @@ -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_)); Loading Loading @@ -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_); Loading Loading @@ -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_); Loading Loading @@ -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(); Loading Loading @@ -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<TargetInfo>* 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<std::tuple<uint32_t, bool>> { public: Loading @@ -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_)); Loading Loading
fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +1 −0 Original line number Diff line number Diff line Loading @@ -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; Loading
fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +44 −0 Original line number Diff line number Diff line Loading @@ -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(); } Loading @@ -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: Loading @@ -120,6 +125,45 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo { bool recovery_ = false; bool first_stage_init_ = false; std::unordered_set<uint32_t> 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<TargetInfo>* table) { return impl_.GetTableInfo(name, table); } virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* 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 { Loading
fs_mgr/libsnapshot/snapshot_test.cpp +68 −30 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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()); Loading Loading @@ -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; Loading Loading @@ -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_)); Loading Loading @@ -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_); Loading Loading @@ -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_); Loading Loading @@ -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(); Loading Loading @@ -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<TargetInfo>* 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<std::tuple<uint32_t, bool>> { public: Loading @@ -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_)); Loading