diff options
author | Artyom Chen <artyomchen@google.com> | 2023-10-30 10:16:31 +0000 |
---|---|---|
committer | Chromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-11-03 13:52:47 +0000 |
commit | c5a026d8c9ad881ee7834eb245a447455b2788c7 (patch) | |
tree | a826fb7fe5a0488de86759376cb9f4ebe2b2ea8b | |
parent | a8977dc7f58a39d3bc6753b1add5a6a0345260f2 (diff) | |
download | update_engine-upstream-master.tar.gz |
update_engine: Do not cancel external powerwash in invalidationsupstream-master
Do not cancel powerwash initiated with reason != update_engine during
update invalidations.
Also keep the powerwash marker file in case if there is an error when
fetching the powerwash reason.
BUG=None
TEST=cros_run_unit_tests --board=BOARD --packages update_engine
TEST=tast run -var=tlwAddress=<DUT> autoupdate.OmahaInvalidation.*
Change-Id: I6d1d5c5a68476643674bebf7d9a4f0fd9cde7ede
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/4957016
Commit-Queue: Artyom Chen <artyomchen@google.com>
Reviewed-by: John Admanski <jadmanski@chromium.org>
Reviewed-by: Jae Hoon Kim <kimjae@chromium.org>
Tested-by: Artyom Chen <artyomchen@google.com>
-rw-r--r-- | common/fake_hardware.h | 13 | ||||
-rw-r--r-- | common/hardware_interface.h | 10 | ||||
-rw-r--r-- | cros/hardware_chromeos.cc | 52 | ||||
-rw-r--r-- | cros/hardware_chromeos.h | 4 | ||||
-rw-r--r-- | cros/hardware_chromeos_unittest.cc | 54 | ||||
-rw-r--r-- | cros/update_attempter.cc | 16 | ||||
-rw-r--r-- | cros/update_attempter.h | 2 | ||||
-rw-r--r-- | cros/update_attempter_unittest.cc | 58 |
8 files changed, 196 insertions, 13 deletions
diff --git a/common/fake_hardware.h b/common/fake_hardware.h index 65a68974..64b7c117 100644 --- a/common/fake_hardware.h +++ b/common/fake_hardware.h @@ -19,6 +19,7 @@ #include <map> #include <memory> +#include <optional> #include <string> #include <utility> @@ -299,6 +300,17 @@ class FakeHardware : public HardwareInterface { } bool IsFWTryNextSlotReset() const { return reset_fw_try_next_slot_; } + std::optional<bool> IsPowerwashScheduledByUpdateEngine() const override { + return is_powerwash_scheduled_by_update_engine_; + } + void SetIsPowerwashScheduledByUpdateEngine(std::optional<bool> value) { + is_powerwash_scheduled_by_update_engine_ = value; + } + + base::FilePath GetPowerwashMarkerFullPath() const override { + return base::FilePath(); + }; + private: bool is_official_build_{true}; bool is_normal_boot_mode_{true}; @@ -318,6 +330,7 @@ class FakeHardware : public HardwareInterface { int kernel_max_rollforward_{kKernelMaxRollforward}; int firmware_max_rollforward_{kFirmwareMaxRollforward}; int powerwash_count_{kPowerwashCountNotSet}; + std::optional<bool> is_powerwash_scheduled_by_update_engine_{true}; bool powerwash_scheduled_{false}; bool save_rollback_data_{false}; int64_t build_timestamp_{0}; diff --git a/common/hardware_interface.h b/common/hardware_interface.h index 9d7be40a..ebae384d 100644 --- a/common/hardware_interface.h +++ b/common/hardware_interface.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <memory> +#include <optional> #include <string> #include <vector> @@ -115,6 +116,12 @@ class HardwareInterface { // recovery don't have this value set. virtual int GetPowerwashCount() const = 0; + // Parses the powerwash marker file and returns if powerwash is + // initiated by update engine. If the file is not found, returns + // std::nullopt. If parsing failed, returns false. + // Otherwise returns if a marker file contains reason=update_engine. + virtual std::optional<bool> IsPowerwashScheduledByUpdateEngine() const = 0; + // Signals that a powerwash (stateful partition wipe) should be performed // after reboot. If |save_rollback_data| is true additional state is // preserved during shutdown that can be restored after the powerwash. @@ -199,6 +206,9 @@ class HardwareInterface { // Resets a RW firmware partition slot to try on next boot to a current slot. // Returns false on failure, true on success. virtual bool ResetFWTryNextSlot() = 0; + + // Returns an absolute path to a powerwash marker file. + virtual base::FilePath GetPowerwashMarkerFullPath() const = 0; }; } // namespace chromeos_update_engine diff --git a/cros/hardware_chromeos.cc b/cros/hardware_chromeos.cc index c7f72d8c..24f4bed4 100644 --- a/cros/hardware_chromeos.cc +++ b/cros/hardware_chromeos.cc @@ -16,6 +16,7 @@ #include "update_engine/cros/hardware_chromeos.h" +#include <optional> #include <utility> #include <base/files/file_path.h> @@ -64,10 +65,14 @@ const char kPowerwashSafeDirectory[] = // a powerwash is performed. const char kPowerwashCountMarker[] = "powerwash_count"; -// The name of the marker file used to trigger powerwash when post-install +// The path of the marker file used to trigger powerwash when post-install // completes successfully so that the device is powerwashed on next reboot. -const char kPowerwashMarkerFile[] = - "/mnt/stateful_partition/factory_install_reset"; +constexpr char kPowerwashMarkerPath[] = + "mnt/stateful_partition/factory_install_reset"; + +// Expected tag in the powerwash marker file that indicates that +// powerwash is initiated by the update engine. +constexpr char kPowerwashReasonUpdateEngineTag[] = "reason=update_engine"; // The name of the marker file used to trigger a save of rollback data // during the next shutdown. @@ -326,29 +331,52 @@ bool HardwareChromeOS::SchedulePowerwash(bool save_rollback_data) { } auto powerwash_command = GeneratePowerwashCommand(save_rollback_data); - bool result = utils::WriteFile( - kPowerwashMarkerFile, powerwash_command.data(), powerwash_command.size()); + const std::string powerwash_marker_full_path = + GetPowerwashMarkerFullPath().value(); + bool result = utils::WriteFile(powerwash_marker_full_path.c_str(), + powerwash_command.data(), + powerwash_command.size()); if (result) { - LOG(INFO) << "Created " << kPowerwashMarkerFile + LOG(INFO) << "Created " << powerwash_marker_full_path << " to powerwash on next reboot (" << "save_rollback_data=" << save_rollback_data << ")"; } else { PLOG(ERROR) << "Error in creating powerwash marker file: " - << kPowerwashMarkerFile; + << powerwash_marker_full_path; } return result; } +std::optional<bool> HardwareChromeOS::IsPowerwashScheduledByUpdateEngine() + const { + const std::string powerwash_marker_full_path = + GetPowerwashMarkerFullPath().value(); + + if (!utils::FileExists(powerwash_marker_full_path.c_str())) { + return std::nullopt; + } + + std::string contents; + if (!utils::ReadFile(powerwash_marker_full_path, &contents)) { + LOG(ERROR) << "Failed to read the powerwash marker file."; + return false; + } + + return contents.find(kPowerwashReasonUpdateEngineTag) != std::string::npos; +} + bool HardwareChromeOS::CancelPowerwash() { - bool result = base::DeleteFile(base::FilePath(kPowerwashMarkerFile)); + const base::FilePath powerwash_marker_full_path = + GetPowerwashMarkerFullPath(); + bool result = base::DeleteFile(powerwash_marker_full_path); if (result) { LOG(INFO) << "Successfully deleted the powerwash marker file : " - << kPowerwashMarkerFile; + << powerwash_marker_full_path; } else { PLOG(ERROR) << "Could not delete the powerwash marker file : " - << kPowerwashMarkerFile; + << powerwash_marker_full_path; } // Delete the rollback save marker file if it existed. @@ -661,4 +689,8 @@ bool HardwareChromeOS::SetFWTryCount(int count) { return true; } +base::FilePath HardwareChromeOS::GetPowerwashMarkerFullPath() const { + return root_.Append(kPowerwashMarkerPath); +} + } // namespace chromeos_update_engine diff --git a/cros/hardware_chromeos.h b/cros/hardware_chromeos.h index f68431e3..bce8bc24 100644 --- a/cros/hardware_chromeos.h +++ b/cros/hardware_chromeos.h @@ -18,6 +18,7 @@ #define UPDATE_ENGINE_CROS_HARDWARE_CHROMEOS_H_ #include <memory> +#include <optional> #include <string> #include <vector> @@ -62,6 +63,7 @@ class HardwareChromeOS final : public HardwareInterface { bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override; bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override; int GetPowerwashCount() const override; + std::optional<bool> IsPowerwashScheduledByUpdateEngine() const override; // Must not be called prior to boot control initialization. bool SchedulePowerwash(bool save_rollback_data) override; bool CancelPowerwash() override; @@ -91,6 +93,8 @@ class HardwareChromeOS final : public HardwareInterface { bool ResetFWTryNextSlot() override; + base::FilePath GetPowerwashMarkerFullPath() const override; + private: friend class HardwareChromeOSTest; FRIEND_TEST(HardwareChromeOSTest, GeneratePowerwashCommandCheck); diff --git a/cros/hardware_chromeos_unittest.cc b/cros/hardware_chromeos_unittest.cc index f60337c4..1cd58eea 100644 --- a/cros/hardware_chromeos_unittest.cc +++ b/cros/hardware_chromeos_unittest.cc @@ -17,6 +17,7 @@ #include "update_engine/cros/hardware_chromeos.h" #include <memory> +#include <optional> #include <utility> #include <base/files/file_util.h> @@ -467,4 +468,57 @@ TEST_F(HardwareChromeOSTest, ResetFWTryNextSlotFailsIfSettingTryCountFails) { ASSERT_FALSE(result); } +TEST_F(HardwareChromeOSTest, IsPowerwashScheduledByUpdateEngineValidReason) { + base::FilePath root_path = root_dir_.GetPath(); + hardware_.SetRootForTest(root_path); + base::FilePath marker_path = hardware_.GetPowerwashMarkerFullPath(); + std::string marker_contents = "safe reason=update_engine\n test_key"; + ASSERT_TRUE(brillo::TouchFile(marker_path)); + ASSERT_TRUE(base::WriteFile(marker_path, marker_contents)); + + std::optional<bool> result = hardware_.IsPowerwashScheduledByUpdateEngine(); + + EXPECT_TRUE(result); + EXPECT_TRUE(result.value()); +} + +TEST_F(HardwareChromeOSTest, IsPowerwashScheduledByUpdateEngineNoMarker) { + base::FilePath root_path = root_dir_.GetPath(); + hardware_.SetRootForTest(root_path); + base::FilePath marker_path = hardware_.GetPowerwashMarkerFullPath(); + ASSERT_FALSE(base::PathExists(marker_path)); + + std::optional<bool> result = hardware_.IsPowerwashScheduledByUpdateEngine(); + + EXPECT_FALSE(result); +} + +TEST_F(HardwareChromeOSTest, IsPowerwashScheduledByUpdateEngineNoReasonKey) { + base::FilePath root_path = root_dir_.GetPath(); + hardware_.SetRootForTest(root_path); + base::FilePath marker_path = hardware_.GetPowerwashMarkerFullPath(); + std::string marker_contents = "safe reason test_key"; + ASSERT_TRUE(brillo::TouchFile(marker_path)); + ASSERT_TRUE(base::WriteFile(marker_path, marker_contents)); + + std::optional<bool> result = hardware_.IsPowerwashScheduledByUpdateEngine(); + + EXPECT_TRUE(result); + EXPECT_FALSE(result.value()); +} + +TEST_F(HardwareChromeOSTest, IsPowerwashScheduledByUpdateEngineEmptyReason) { + base::FilePath root_path = root_dir_.GetPath(); + hardware_.SetRootForTest(root_path); + base::FilePath marker_path = hardware_.GetPowerwashMarkerFullPath(); + std::string marker_contents = "safe reason=\n test_key"; + ASSERT_TRUE(brillo::TouchFile(marker_path)); + ASSERT_TRUE(base::WriteFile(marker_path, marker_contents)); + + std::optional<bool> result = hardware_.IsPowerwashScheduledByUpdateEngine(); + + EXPECT_TRUE(result); + EXPECT_FALSE(result.value()); +} + } // namespace chromeos_update_engine diff --git a/cros/update_attempter.cc b/cros/update_attempter.cc index d78eef9b..d0eb4a0d 100644 --- a/cros/update_attempter.cc +++ b/cros/update_attempter.cc @@ -1779,9 +1779,19 @@ bool UpdateAttempter::InvalidateUpdate() { } LOG(INFO) << "Clearing powerwash and rollback flags, if any."; - if (!SystemState::Get()->hardware()->CancelPowerwash()) { - LOG(WARNING) << "Failed to cancel powerwash. Continuing anyway."; - success = false; + const std::optional<bool> is_powerwash_scheduled_by_update_engine = + SystemState::Get()->hardware()->IsPowerwashScheduledByUpdateEngine(); + if (!is_powerwash_scheduled_by_update_engine) { + LOG(INFO) << "Powerwash is not scheduled, continuing."; + } else if (!is_powerwash_scheduled_by_update_engine.value()) { + LOG(INFO) << "Not cancelling powerwash. Either not initiated by update " + "engine or there was a parsing error."; + } else { + LOG(INFO) << "Cancelling powerwash that was initiated by update engine."; + if (!SystemState::Get()->hardware()->CancelPowerwash()) { + LOG(WARNING) << "Failed to cancel powerwash. Continuing anyway."; + success = false; + } } SystemState::Get()->payload_state()->SetRollbackHappened(false); diff --git a/cros/update_attempter.h b/cros/update_attempter.h index 5da200ec..7df9d81e 100644 --- a/cros/update_attempter.h +++ b/cros/update_attempter.h @@ -374,6 +374,8 @@ class UpdateAttempter : public ActionProcessorDelegate, FRIEND_TEST(UpdateAttempterTest, FirstUpdateBeforeReboot); FRIEND_TEST(UpdateAttempterTest, InvalidateLastUpdate); FRIEND_TEST(UpdateAttempterTest, InvalidateLastPowerwashUpdate); + FRIEND_TEST(UpdateAttempterTest, InvalidateLastUpdateNoPowerwashFile); + FRIEND_TEST(UpdateAttempterTest, InvalidateLastUpdateExternalPowerwash); FRIEND_TEST(UpdateAttempterTest, ConsecutiveUpdateBeforeRebootSuccess); FRIEND_TEST(UpdateAttempterTest, ConsecutiveUpdateBeforeRebootLimited); FRIEND_TEST(UpdateAttempterTest, ConsecutiveUpdateFailureMetric); diff --git a/cros/update_attempter_unittest.cc b/cros/update_attempter_unittest.cc index 41da85dd..6d0a1860 100644 --- a/cros/update_attempter_unittest.cc +++ b/cros/update_attempter_unittest.cc @@ -22,6 +22,7 @@ #include <limits> #include <map> #include <memory> +#include <optional> #include <string> #include <unordered_set> @@ -2597,6 +2598,9 @@ TEST_F(UpdateAttempterTest, InvalidateLastPowerwashUpdate) { bool save_rollback_data = true; FakeSystemState::Get()->fake_hardware()->SchedulePowerwash( save_rollback_data); + FakeSystemState::Get() + ->fake_hardware() + ->SetIsPowerwashScheduledByUpdateEngine(true); EXPECT_TRUE(FakeSystemState::Get()->fake_hardware()->IsPowerwashScheduled()); FakeSystemState::Get()->fake_clock()->SetBootTime(Time::FromTimeT(42)); attempter_.WriteUpdateCompletedMarker(); @@ -2616,6 +2620,60 @@ TEST_F(UpdateAttempterTest, InvalidateLastPowerwashUpdate) { EXPECT_TRUE(FakeSystemState::Get()->fake_hardware()->IsFWTryNextSlotReset()); } +TEST_F(UpdateAttempterTest, InvalidateLastUpdateNoPowerwashFile) { + // Mock a previous update. + bool save_rollback_data = true; + FakeSystemState::Get()->fake_hardware()->SchedulePowerwash( + save_rollback_data); + EXPECT_TRUE(FakeSystemState::Get()->fake_hardware()->IsPowerwashScheduled()); + FakeSystemState::Get() + ->fake_hardware() + ->SetIsPowerwashScheduledByUpdateEngine(std::nullopt); + FakeSystemState::Get()->fake_clock()->SetBootTime(Time::FromTimeT(42)); + attempter_.WriteUpdateCompletedMarker(); + EXPECT_TRUE(attempter_.GetBootTimeAtUpdate(nullptr)); + attempter_.status_ = UpdateStatus::UPDATED_NEED_REBOOT; + EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(), + SetRollbackHappened(false)) + .Times(1); + + // Invalidate an update. + MockAction action; + attempter_.ActionCompleted( + nullptr, &action, ErrorCode::kInvalidateLastUpdate); + + EXPECT_FALSE(attempter_.GetBootTimeAtUpdate(nullptr)); + EXPECT_TRUE(FakeSystemState::Get()->fake_hardware()->IsPowerwashScheduled()); + EXPECT_TRUE(FakeSystemState::Get()->fake_hardware()->IsFWTryNextSlotReset()); +} + +TEST_F(UpdateAttempterTest, InvalidateLastUpdateExternalPowerwash) { + // Mock a previous update. + bool save_rollback_data = true; + FakeSystemState::Get()->fake_hardware()->SchedulePowerwash( + save_rollback_data); + EXPECT_TRUE(FakeSystemState::Get()->fake_hardware()->IsPowerwashScheduled()); + FakeSystemState::Get() + ->fake_hardware() + ->SetIsPowerwashScheduledByUpdateEngine(false); + FakeSystemState::Get()->fake_clock()->SetBootTime(Time::FromTimeT(42)); + attempter_.WriteUpdateCompletedMarker(); + EXPECT_TRUE(attempter_.GetBootTimeAtUpdate(nullptr)); + attempter_.status_ = UpdateStatus::UPDATED_NEED_REBOOT; + EXPECT_CALL(*FakeSystemState::Get()->mock_payload_state(), + SetRollbackHappened(false)) + .Times(1); + + // Invalidate an update. + MockAction action; + attempter_.ActionCompleted( + nullptr, &action, ErrorCode::kInvalidateLastUpdate); + + EXPECT_FALSE(attempter_.GetBootTimeAtUpdate(nullptr)); + EXPECT_TRUE(FakeSystemState::Get()->fake_hardware()->IsPowerwashScheduled()); + EXPECT_TRUE(FakeSystemState::Get()->fake_hardware()->IsFWTryNextSlotReset()); +} + TEST_F(UpdateAttempterTest, CalculateDlcParamsRemoveStaleMetadata) { string dlc_id = "dlc0"; auto active_key = |