diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp index c8516ab2a..31a57a800 100644 --- a/fs_mgr/libfiemap/binder.cpp +++ b/fs_mgr/libfiemap/binder.cpp @@ -224,8 +224,9 @@ bool ImageManagerBinder::MapAllImages(const std::function IImageManager::Open( - const std::string& dir, const std::chrono::milliseconds& /*timeout_ms*/) { +std::unique_ptr IImageManager::Open(const std::string& dir, + const std::chrono::milliseconds& /*timeout_ms*/, + const DeviceInfo&) { android::sp service = android::gsi::GetGsiService(); android::sp manager; diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp index 44f659b5d..dcbbc5448 100644 --- a/fs_mgr/libfiemap/image_manager.cpp +++ b/fs_mgr/libfiemap/image_manager.cpp @@ -55,7 +55,8 @@ using android::fs_mgr::GetPartitionName; static constexpr char kTestImageMetadataDir[] = "/metadata/gsi/test"; static constexpr char kOtaTestImageMetadataDir[] = "/metadata/gsi/ota/test"; -std::unique_ptr ImageManager::Open(const std::string& dir_prefix) { +std::unique_ptr ImageManager::Open(const std::string& dir_prefix, + const DeviceInfo& device_info) { auto metadata_dir = "/metadata/gsi/" + dir_prefix; auto data_dir = "/data/gsi/" + dir_prefix; auto install_dir_file = gsi::DsuInstallDirFile(gsi::GetDsuSlot(dir_prefix)); @@ -63,17 +64,28 @@ std::unique_ptr ImageManager::Open(const std::string& dir_prefix) if (ReadFileToString(install_dir_file, &path)) { data_dir = path; } - return Open(metadata_dir, data_dir); + return Open(metadata_dir, data_dir, device_info); } std::unique_ptr ImageManager::Open(const std::string& metadata_dir, - const std::string& data_dir) { - return std::unique_ptr(new ImageManager(metadata_dir, data_dir)); + const std::string& data_dir, + const DeviceInfo& device_info) { + return std::unique_ptr(new ImageManager(metadata_dir, data_dir, device_info)); } -ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir) - : metadata_dir_(metadata_dir), data_dir_(data_dir) { +ImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir, + const DeviceInfo& device_info) + : metadata_dir_(metadata_dir), data_dir_(data_dir), device_info_(device_info) { partition_opener_ = std::make_unique(); + + // Allow overriding whether ImageManager thinks it's in recovery, for testing. +#ifdef __ANDROID_RECOVERY__ + device_info_.is_recovery = {true}; +#else + if (!device_info_.is_recovery.has_value()) { + device_info_.is_recovery = {false}; + } +#endif } std::string ImageManager::GetImageHeaderPath(const std::string& name) { @@ -261,10 +273,11 @@ bool ImageManager::DeleteBackingImage(const std::string& name) { return false; } -#if defined __ANDROID_RECOVERY__ - LOG(ERROR) << "Cannot remove images backed by /data in recovery"; - return false; -#else + if (device_info_.is_recovery.value()) { + LOG(ERROR) << "Cannot remove images backed by /data in recovery"; + return false; + } + std::string message; auto header_file = GetImageHeaderPath(name); if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) { @@ -278,7 +291,6 @@ bool ImageManager::DeleteBackingImage(const std::string& name) { LOG(ERROR) << "Error removing " << status_file << ": " << message; } return RemoveImageMetadata(metadata_dir_, name); -#endif } // Create a block device for an image file, using its extents in its @@ -521,6 +533,9 @@ bool ImageManager::MapImageDevice(const std::string& name, // filesystem. This should only happen on devices with no encryption, or // devices with FBE and no metadata encryption. For these cases it suffices // to perform normal file writes to /data/gsi (which is unencrypted). + // + // Note: this is not gated on DeviceInfo, because the recovery-specific path + // must only be used in actual recovery. std::string block_device; bool can_use_devicemapper; if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) { diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h index 7e305099b..3c8700025 100644 --- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h +++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -37,11 +38,17 @@ class IImageManager { virtual ~IImageManager() {} + // Helper for dependency injection. + struct DeviceInfo { + std::optional is_recovery; + }; + // When linking to libfiemap_binder, the Open() call will use binder. // Otherwise, the Open() call will use the ImageManager implementation - // below. + // below. In binder mode, device_info is ignored. static std::unique_ptr Open(const std::string& dir_prefix, - const std::chrono::milliseconds& timeout_ms); + const std::chrono::milliseconds& timeout_ms, + const DeviceInfo& device_info = {}); // Flags for CreateBackingImage(). static constexpr int CREATE_IMAGE_DEFAULT = 0x0; @@ -131,11 +138,13 @@ class ImageManager final : public IImageManager { // Return an ImageManager for the given metadata and data directories. Both // directories must already exist. static std::unique_ptr Open(const std::string& metadata_dir, - const std::string& data_dir); + const std::string& data_dir, + const DeviceInfo& device_info = {}); // Helper function that derives the metadata and data dirs given a single // prefix. - static std::unique_ptr Open(const std::string& dir_prefix); + static std::unique_ptr Open(const std::string& dir_prefix, + const DeviceInfo& device_info = {}); // Methods that must be implemented from IImageManager. FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags, @@ -166,7 +175,8 @@ class ImageManager final : public IImageManager { FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes); private: - ImageManager(const std::string& metadata_dir, const std::string& data_dir); + ImageManager(const std::string& metadata_dir, const std::string& data_dir, + const DeviceInfo& device_info); std::string GetImageHeaderPath(const std::string& name); std::string GetStatusFilePath(const std::string& image_name); bool MapWithLoopDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms, @@ -187,6 +197,7 @@ class ImageManager final : public IImageManager { std::string metadata_dir_; std::string data_dir_; std::unique_ptr partition_opener_; + DeviceInfo device_info_; }; // RAII helper class for mapping and opening devices with an ImageManager. diff --git a/fs_mgr/libfiemap/passthrough.cpp b/fs_mgr/libfiemap/passthrough.cpp index 1ccd9a0c6..d521804c5 100644 --- a/fs_mgr/libfiemap/passthrough.cpp +++ b/fs_mgr/libfiemap/passthrough.cpp @@ -20,9 +20,10 @@ namespace android { namespace fiemap { std::unique_ptr IImageManager::Open(const std::string& dir_prefix, - const std::chrono::milliseconds& timeout_ms) { + const std::chrono::milliseconds& timeout_ms, + const DeviceInfo& device_info) { (void)timeout_ms; - return ImageManager::Open(dir_prefix); + return ImageManager::Open(dir_prefix, device_info); } } // namespace fiemap diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 195b6f2ba..d2fdfd6d8 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -392,6 +392,7 @@ class SnapshotManager final : public ISnapshotManager { FRIEND_TEST(SnapshotUpdateTest, DaemonTransition); FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback); FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery); + FRIEND_TEST(SnapshotUpdateTest, DataWipeWithStaleSnapshots); FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow); FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow); FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery); diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 8f3926aa4..245742e89 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -2583,7 +2583,11 @@ bool SnapshotManager::EnsureSnapuserdConnected() { } bool SnapshotManager::ForceLocalImageManager() { - images_ = android::fiemap::ImageManager::Open(gsid_dir_); + android::fiemap::ImageManager::DeviceInfo device_info = { + .is_recovery = {device_->IsRecovery()}, + }; + + images_ = android::fiemap::ImageManager::Open(gsid_dir_, device_info); if (!images_) { LOG(ERROR) << "Could not open ImageManager"; return false; diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index f18f176e2..9226b7f43 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -1860,6 +1860,67 @@ TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) { } } +// Test update package that requests data wipe. +TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) { + AddOperationForPartitions(); + + // Execute the update. + ASSERT_TRUE(sm->BeginUpdate()); + ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_)); + + // Write some data to target partitions. + for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { + ASSERT_TRUE(WriteSnapshotAndHash(name)) << name; + } + + // Create a stale snapshot that should not exist. + { + ASSERT_TRUE(AcquireLock()); + + PartitionCowCreator cow_creator = { + .compression_enabled = IsCompressionEnabled(), + .compression_algorithm = IsCompressionEnabled() ? "gz" : "none", + }; + SnapshotStatus status; + status.set_name("sys_a"); + status.set_device_size(1_MiB); + status.set_snapshot_size(2_MiB); + status.set_cow_partition_size(2_MiB); + + ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status)); + lock_ = nullptr; + + ASSERT_TRUE(sm->EnsureImageManager()); + ASSERT_TRUE(sm->image_manager()->CreateBackingImage("sys_a", 1_MiB, 0)); + } + + ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */)); + + // Simulate shutting down the device. + ASSERT_TRUE(UnmapAll()); + + // Simulate a reboot into recovery. + auto test_device = new TestDeviceInfo(fake_super, "_b"); + test_device->set_recovery(true); + auto new_sm = NewManagerForFirstStageMount(test_device); + + ASSERT_TRUE(new_sm->HandleImminentDataWipe()); + // Manually mount metadata so that we can call GetUpdateState() below. + MountMetadata(); + EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None); + ASSERT_FALSE(test_device->IsSlotUnbootable(1)); + ASSERT_FALSE(test_device->IsSlotUnbootable(0)); + + // Now reboot into new slot. + test_device = new TestDeviceInfo(fake_super, "_b"); + auto init = NewManagerForFirstStageMount(test_device); + ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); + // Verify that we are on the downgraded build. + for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { + ASSERT_TRUE(IsPartitionUnchanged(name)) << name; + } +} + TEST_F(SnapshotUpdateTest, Hashtree) { constexpr auto partition_size = 4_MiB; constexpr auto data_size = 3_MiB;