update_engine: Process Omaha response for rollback images
Omaha returns whether the image returned is a rollback in the
_rollback="true" argument. If this is set, the client has to check
whether it's OK to apply the rollback image (policy is specifically
requesting a rollback and verified boot will accept the image based
on its kernel and firmware key versions).
In addition to this, the device has to do a safe powerwash if the
image is a rollback. (We're not supporting rollbacks with partial
or no powerwash yet.)
We're also setting the rollback_happened preference to avoid force
updates happening before the policy is available again.
Chromium CL adding the error code: http://crrev.com/c/1047866
BUG=chromium:840432
TEST='cros_run_unit_tests --board=caroline --packages update_engine'
Change-Id: I1436ca96211b2a8523e78bf83602ef8b6b525570
Reviewed-on: https://chromium-review.googlesource.com/1047610
Commit-Ready: Marton Hunyady <hunyadym@chromium.org>
Tested-by: Marton Hunyady <hunyadym@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 5cdfbc1..929cad3 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -127,6 +127,9 @@
// False otherwise.
bool run_post_install{true};
+ // True if this update is a rollback.
+ bool is_rollback{false};
+
// If not blank, a base-64 encoded representation of the PEM-encoded
// public key in the response.
std::string public_key_rsa;
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index cedecda..c672fef 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -57,7 +57,8 @@
CHECK(HasInputObject());
install_plan_ = GetInputObject();
- if (install_plan_.powerwash_required) {
+ // Currently we're always powerwashing when rolling back.
+ if (install_plan_.powerwash_required || install_plan_.is_rollback) {
if (hardware_->SchedulePowerwash()) {
powerwash_scheduled_ = true;
} else {
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index a319299..b7f0fdc 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -39,6 +39,7 @@
#include "update_engine/common/fake_hardware.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
+#include "update_engine/mock_payload_state.h"
using brillo::MessageLoop;
using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
@@ -95,9 +96,10 @@
// Setup an action processor and run the PostinstallRunnerAction with a single
// partition |device_path|, running the |postinstall_program| command from
// there.
- void RunPosinstallAction(const string& device_path,
- const string& postinstall_program,
- bool powerwash_required);
+ void RunPostinstallAction(const string& device_path,
+ const string& postinstall_program,
+ bool powerwash_required,
+ bool is_rollback);
public:
void ResumeRunningAction() {
@@ -163,10 +165,11 @@
ActionProcessor* processor_{nullptr};
};
-void PostinstallRunnerActionTest::RunPosinstallAction(
+void PostinstallRunnerActionTest::RunPostinstallAction(
const string& device_path,
const string& postinstall_program,
- bool powerwash_required) {
+ bool powerwash_required,
+ bool is_rollback) {
ActionProcessor processor;
processor_ = &processor;
ObjectFeederAction<InstallPlan> feeder_action;
@@ -179,6 +182,7 @@
install_plan.partitions = {part};
install_plan.download_url = "http://127.0.0.1:8080/update";
install_plan.powerwash_required = powerwash_required;
+ install_plan.is_rollback = is_rollback;
feeder_action.set_obj(install_plan);
PostinstallRunnerAction runner_action(&fake_boot_control_, &fake_hardware_);
postinstall_action_ = &runner_action;
@@ -241,7 +245,8 @@
// /postinst command which only exits 0.
TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPosinstallAction(loop.dev(), kPostinstallDefaultScript, false);
+
+ RunPostinstallAction(loop.dev(), kPostinstallDefaultScript, false, false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
EXPECT_TRUE(processor_delegate_.processing_done_called_);
@@ -251,14 +256,31 @@
TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPosinstallAction(loop.dev(), "bin/postinst_link", false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
}
TEST_F(PostinstallRunnerActionTest, RunAsRootPowerwashRequiredTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
// Run a simple postinstall program but requiring a powerwash.
- RunPosinstallAction(loop.dev(), "bin/postinst_example", true);
+ RunPostinstallAction(loop.dev(),
+ "bin/postinst_example",
+ /*powerwash_required=*/true,
+ false);
+ EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
+
+ // Check that powerwash was scheduled.
+ EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTest) {
+ ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
+
+ // Run a simple postinstall program, rollback happened.
+ RunPostinstallAction(loop.dev(),
+ "bin/postinst_example",
+ false,
+ /*is_rollback=*/true);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
// Check that powerwash was scheduled.
@@ -268,7 +290,7 @@
// Runs postinstall from a partition file that doesn't mount, so it should
// fail.
TEST_F(PostinstallRunnerActionTest, RunAsRootCantMountTest) {
- RunPosinstallAction("/dev/null", kPostinstallDefaultScript, false);
+ RunPostinstallAction("/dev/null", kPostinstallDefaultScript, false, false);
EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
// In case of failure, Postinstall should not signal a powerwash even if it
@@ -280,7 +302,7 @@
// fail.
TEST_F(PostinstallRunnerActionTest, RunAsRootErrScriptTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPosinstallAction(loop.dev(), "bin/postinst_fail1", false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false);
EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
}
@@ -288,7 +310,7 @@
// UMA with a different error code. Test those cases are properly detected.
TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareBErrScriptTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPosinstallAction(loop.dev(), "bin/postinst_fail3", false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false);
EXPECT_EQ(ErrorCode::kPostinstallBootedFromFirmwareB,
processor_delegate_.code_);
}
@@ -296,7 +318,7 @@
// Check that you can't specify an absolute path.
TEST_F(PostinstallRunnerActionTest, RunAsRootAbsolutePathNotAllowedTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPosinstallAction(loop.dev(), "/etc/../bin/sh", false);
+ RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false);
EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
}
@@ -305,7 +327,7 @@
// SElinux labels are only set on Android.
TEST_F(PostinstallRunnerActionTest, RunAsRootCheckFileContextsTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPosinstallAction(loop.dev(), "bin/self_check_context", false);
+ RunPosinstallAction(loop.dev(), "bin/self_check_context", false, false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
}
#endif // __ANDROID__
@@ -318,7 +340,7 @@
loop_.PostTask(FROM_HERE,
base::Bind(&PostinstallRunnerActionTest::SuspendRunningAction,
base::Unretained(this)));
- RunPosinstallAction(loop.dev(), "bin/postinst_suspend", false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
// postinst_suspend returns 0 only if it was suspended at some point.
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
EXPECT_TRUE(processor_delegate_.processing_done_called_);
@@ -330,7 +352,7 @@
// Wait for the action to start and then cancel it.
CancelWhenStarted();
- RunPosinstallAction(loop.dev(), "bin/postinst_suspend", false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
// When canceling the action, the action never finished and therefore we had
// a ProcessingStopped call instead.
EXPECT_FALSE(processor_delegate_.code_set_);
@@ -353,7 +375,7 @@
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
setup_action_delegate_ = &mock_delegate_;
- RunPosinstallAction(loop.dev(), "bin/postinst_progress", false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_progress", false, false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
}