update_engine: Place enterprise rollback save marker file
- There are now two types of rollback. One that does a full powerwash
and one that save some system state.
- When an enterprise rollback powerwash is scheduled, place a marker
file to tell the shutdown process to save data before rebooting.
- This lets rollback preserve additional data over a powerwash that
can be restored later.
- Change a few places that were using is_rollback to save_rollback_data
to be explicit.
BUG=chromium:955463
TEST=unittests
Change-Id: I9f18319e711e425a6e712dd319e03bcc6ddd0a1b
Reviewed-on: https://chromium-review.googlesource.com/1414030
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: Zentaro Kavanagh <zentaro@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index ede36b3..17cefd8 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -146,6 +146,9 @@
// True if this update is a rollback.
bool is_rollback{false};
+ // True if this rollback should preserve some system data.
+ bool rollback_data_save_requested{false};
+
// True if the update should write verity.
// False otherwise.
bool write_verity{true};
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index a782b8f..cc3843d 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -57,9 +57,14 @@
CHECK(HasInputObject());
install_plan_ = GetInputObject();
- // Currently we're always powerwashing when rolling back.
+ // We always powerwash when rolling back, however policy can determine
+ // if this is a full/normal powerwash, or a special rollback powerwash
+ // that retains a small amount of system state such as enrollment and
+ // network configuration. In both cases all user accounts are deleted.
if (install_plan_.powerwash_required || install_plan_.is_rollback) {
- if (hardware_->SchedulePowerwash(install_plan_.is_rollback)) {
+ bool save_rollback_data =
+ install_plan_.is_rollback && install_plan_.rollback_data_save_requested;
+ if (hardware_->SchedulePowerwash(save_rollback_data)) {
powerwash_scheduled_ = true;
} else {
return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index caee5e2..04c81fa 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -100,7 +100,8 @@
void RunPostinstallAction(const string& device_path,
const string& postinstall_program,
bool powerwash_required,
- bool is_rollback);
+ bool is_rollback,
+ bool save_rollback_data);
public:
void ResumeRunningAction() {
@@ -170,7 +171,8 @@
const string& device_path,
const string& postinstall_program,
bool powerwash_required,
- bool is_rollback) {
+ bool is_rollback,
+ bool save_rollback_data) {
ActionProcessor processor;
processor_ = &processor;
auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
@@ -184,6 +186,7 @@
install_plan.download_url = "http://127.0.0.1:8080/update";
install_plan.powerwash_required = powerwash_required;
install_plan.is_rollback = is_rollback;
+ install_plan.rollback_data_save_requested = save_rollback_data;
feeder_action->set_obj(install_plan);
auto runner_action = std::make_unique<PostinstallRunnerAction>(
&fake_boot_control_, &fake_hardware_);
@@ -249,7 +252,8 @@
TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPostinstallAction(loop.dev(), kPostinstallDefaultScript, false, false);
+ RunPostinstallAction(
+ loop.dev(), kPostinstallDefaultScript, false, false, false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
EXPECT_TRUE(processor_delegate_.processing_done_called_);
@@ -260,7 +264,7 @@
TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false, false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
}
@@ -270,6 +274,7 @@
RunPostinstallAction(loop.dev(),
"bin/postinst_example",
/*powerwash_required=*/true,
+ false,
false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
@@ -278,14 +283,31 @@
EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
}
-TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTest) {
+TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTestNoDataSave) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
// Run a simple postinstall program, rollback happened.
RunPostinstallAction(loop.dev(),
"bin/postinst_example",
false,
- /*is_rollback=*/true);
+ /*is_rollback=*/true,
+ /*save_rollback_data=*/false);
+ EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
+
+ // Check that powerwash was scheduled and that it's NOT a rollback powerwash.
+ EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
+ EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTestWithDataSave) {
+ ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
+
+ // Run a simple postinstall program, rollback happened.
+ RunPostinstallAction(loop.dev(),
+ "bin/postinst_example",
+ false,
+ /*is_rollback=*/true,
+ /*save_rollback_data=*/true);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
// Check that powerwash was scheduled and that it's a rollback powerwash.
@@ -296,7 +318,8 @@
// Runs postinstall from a partition file that doesn't mount, so it should
// fail.
TEST_F(PostinstallRunnerActionTest, RunAsRootCantMountTest) {
- RunPostinstallAction("/dev/null", kPostinstallDefaultScript, false, false);
+ RunPostinstallAction(
+ "/dev/null", kPostinstallDefaultScript, false, false, false);
EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
// In case of failure, Postinstall should not signal a powerwash even if it
@@ -309,7 +332,7 @@
// fail.
TEST_F(PostinstallRunnerActionTest, RunAsRootErrScriptTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false, false);
EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
}
@@ -317,7 +340,7 @@
// UMA with a different error code. Test those cases are properly detected.
TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareBErrScriptTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false, false);
EXPECT_EQ(ErrorCode::kPostinstallBootedFromFirmwareB,
processor_delegate_.code_);
}
@@ -325,7 +348,7 @@
// Check that you can't specify an absolute path.
TEST_F(PostinstallRunnerActionTest, RunAsRootAbsolutePathNotAllowedTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false);
+ RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false, false);
EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
}
@@ -334,7 +357,8 @@
// SElinux labels are only set on Android.
TEST_F(PostinstallRunnerActionTest, RunAsRootCheckFileContextsTest) {
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
- RunPostinstallAction(loop.dev(), "bin/self_check_context", false, false);
+ RunPostinstallAction(
+ loop.dev(), "bin/self_check_context", false, false, false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
}
#endif // __ANDROID__
@@ -347,7 +371,7 @@
loop_.PostTask(FROM_HERE,
base::Bind(&PostinstallRunnerActionTest::SuspendRunningAction,
base::Unretained(this)));
- RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, 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_);
@@ -359,7 +383,7 @@
// Wait for the action to start and then cancel it.
CancelWhenStarted();
- RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
+ RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false, false);
// When canceling the action, the action never finished and therefore we had
// a ProcessingStopped call instead.
EXPECT_FALSE(processor_delegate_.code_set_);
@@ -382,7 +406,8 @@
ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
setup_action_delegate_ = &mock_delegate_;
- RunPostinstallAction(loop.dev(), "bin/postinst_progress", false, false);
+ RunPostinstallAction(
+ loop.dev(), "bin/postinst_progress", false, false, false);
EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
}