From c058df7b12e190ff521c97d1801713b830bdcb6d Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 22 Aug 2019 18:19:17 -0700 Subject: [PATCH] libsnapshot: Do not map snapshots for partitions that were reflashed. Bug: 139154795 Test: libsnapshot_test gtests Change-Id: Ic4bb663d4286040adec4c0141373c66a487c91ff --- fs_mgr/libsnapshot/Android.bp | 1 + .../include/libsnapshot/snapshot.h | 1 + fs_mgr/libsnapshot/snapshot.cpp | 6 ++ fs_mgr/libsnapshot/snapshot_test.cpp | 91 ++++++++++++++++++- 4 files changed, 94 insertions(+), 5 deletions(-) diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index b320db802..a54db58f7 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -86,6 +86,7 @@ cc_test { "libcutils", "libcrypto", "libfs_mgr", + "libgmock", "liblp", "libsnapshot", ], diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 0c0355d1b..b982b4bd3 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -158,6 +158,7 @@ class SnapshotManager final { FRIEND_TEST(SnapshotTest, CreateSnapshot); FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback); FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge); + FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate); FRIEND_TEST(SnapshotTest, MapPartialSnapshot); FRIEND_TEST(SnapshotTest, MapSnapshot); FRIEND_TEST(SnapshotTest, Merge); diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 87170a763..ab1157b82 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -1041,6 +1041,12 @@ bool SnapshotManager::CreateLogicalAndSnapshotPartitions(const std::string& supe continue; } + if (!(partition.attributes & LP_PARTITION_ATTR_UPDATED)) { + LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: " + << partition_name; + live_snapshots.erase(partition_name); + } + CreateLogicalPartitionParams params = { .block_device = super_device, .metadata = metadata.get(), diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index f4eb1ac0a..acffe8cd4 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "test_helpers.h" @@ -43,7 +44,10 @@ using android::dm::DmDeviceState; using android::fiemap::IImageManager; using android::fs_mgr::BlockDeviceInfo; using android::fs_mgr::CreateLogicalPartitionParams; +using android::fs_mgr::DestroyLogicalPartition; using android::fs_mgr::MetadataBuilder; +using namespace ::testing; +using namespace android::fs_mgr::testing; using namespace std::chrono_literals; using namespace std::string_literals; @@ -67,6 +71,7 @@ class SnapshotTest : public ::testing::Test { protected: void SetUp() override { + ResetMockPropertyFetcher(); InitializeState(); CleanupTestArtifacts(); FormatFakeSuper(); @@ -78,6 +83,7 @@ class SnapshotTest : public ::testing::Test { lock_ = nullptr; CleanupTestArtifacts(); + ResetMockPropertyFetcher(); } void InitializeState() { @@ -95,7 +101,8 @@ class SnapshotTest : public ::testing::Test { // get an accurate list to remove. lock_ = nullptr; - std::vector snapshots = {"test-snapshot", "test_partition_b"}; + std::vector snapshots = {"test-snapshot", "test_partition_a", + "test_partition_b"}; for (const auto& snapshot : snapshots) { DeleteSnapshotDevice(snapshot); DeleteBackingImage(image_manager_, snapshot + "-cow"); @@ -154,11 +161,10 @@ class SnapshotTest : public ::testing::Test { return false; } - // Update both slots for convenience. + // Update the source slot. auto metadata = builder->Export(); if (!metadata) return false; - if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0) || - !UpdatePartitionTable(opener, "super", *metadata.get(), 1)) { + if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) { return false; } @@ -174,6 +180,35 @@ class SnapshotTest : public ::testing::Test { return CreateLogicalPartition(params, path); } + bool MapUpdatePartitions() { + TestPartitionOpener opener(fake_super); + auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1); + if (!builder) return false; + + auto metadata = builder->Export(); + if (!metadata) return false; + + // Update the destination slot, mark it as updated. + if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) { + return false; + } + + for (const auto& partition : metadata->partitions) { + CreateLogicalPartitionParams params = { + .block_device = fake_super, + .metadata = metadata.get(), + .partition = &partition, + .force_writable = true, + .timeout_ms = 10s, + }; + std::string ignore_path; + if (!CreateLogicalPartition(params, &ignore_path)) { + return false; + } + } + return true; + } + void DeleteSnapshotDevice(const std::string& snapshot) { DeleteDevice(snapshot); DeleteDevice(snapshot + "-inner"); @@ -370,17 +405,22 @@ TEST_F(SnapshotTest, MergeCannotRemoveCow) { } TEST_F(SnapshotTest, FirstStageMountAndMerge) { + ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _)) + .WillByDefault(Return(true)); + ASSERT_TRUE(AcquireLock()); static const uint64_t kDeviceSize = 1024 * 1024; - ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize)); + ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize)); + ASSERT_TRUE(MapUpdatePartitions()); ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize, kDeviceSize)); // Simulate a reboot into the new slot. lock_ = nullptr; ASSERT_TRUE(sm->FinishedSnapshotWrites()); + ASSERT_TRUE(DestroyLogicalPartition("test_partition_b")); auto rebooted = new TestDeviceInfo(fake_super); rebooted->set_slot_suffix("_b"); @@ -403,6 +443,47 @@ TEST_F(SnapshotTest, FirstStageMountAndMerge) { ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot"); } +TEST_F(SnapshotTest, FlashSuperDuringUpdate) { + ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _)) + .WillByDefault(Return(true)); + + ASSERT_TRUE(AcquireLock()); + + static const uint64_t kDeviceSize = 1024 * 1024; + + ASSERT_TRUE(CreatePartition("test_partition_a", kDeviceSize)); + ASSERT_TRUE(MapUpdatePartitions()); + ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), "test_partition_b", kDeviceSize, kDeviceSize, + kDeviceSize)); + + // Simulate a reboot into the new slot. + lock_ = nullptr; + ASSERT_TRUE(sm->FinishedSnapshotWrites()); + ASSERT_TRUE(DestroyLogicalPartition("test_partition_b")); + + // Reflash the super partition. + FormatFakeSuper(); + ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize)); + + auto rebooted = new TestDeviceInfo(fake_super); + rebooted->set_slot_suffix("_b"); + + auto init = SnapshotManager::NewForFirstStageMount(rebooted); + ASSERT_NE(init, nullptr); + ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); + ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super")); + + ASSERT_TRUE(AcquireLock()); + + SnapshotManager::SnapshotStatus status; + ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status)); + + // We should not get a snapshot device now. + DeviceMapper::TargetInfo target; + auto dm_name = init->GetSnapshotDeviceName("test_partition_b", status); + ASSERT_FALSE(init->IsSnapshotDevice(dm_name, &target)); +} + } // namespace snapshot } // namespace android