update_engine: Add support for enterprise rollback powerwash

- Adds the additional flag "rollback" to the powerwash file
- This flag allows additional data to be preserved over a powerwash
- Adds tests

BUG=chromium:881341
TEST=unittests
Change-Id: I4487f4de856ea8d2d0255e8de4cd1ba0762a8e53
Reviewed-on: https://chromium-review.googlesource.com/1412683
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>
Reviewed-by: Sen Jiang <senj@chromium.org>
diff --git a/common/fake_hardware.h b/common/fake_hardware.h
index 63346ce..3e5a66e 100644
--- a/common/fake_hardware.h
+++ b/common/fake_hardware.h
@@ -104,13 +104,15 @@
 
   int GetPowerwashCount() const override { return powerwash_count_; }
 
-  bool SchedulePowerwash() override {
+  bool SchedulePowerwash(bool is_rollback) override {
     powerwash_scheduled_ = true;
+    is_rollback_powerwash_ = is_rollback;
     return true;
   }
 
   bool CancelPowerwash() override {
     powerwash_scheduled_ = false;
+    is_rollback_powerwash_ = false;
     return true;
   }
 
@@ -190,6 +192,10 @@
   // Getters to verify state.
   int GetMaxKernelKeyRollforward() const { return kernel_max_rollforward_; }
 
+  bool GetIsRollbackPowerwashScheduled() const {
+    return powerwash_scheduled_ && is_rollback_powerwash_;
+  }
+
  private:
   bool is_official_build_{true};
   bool is_normal_boot_mode_{true};
@@ -207,6 +213,7 @@
   int firmware_max_rollforward_{kFirmwareMaxRollforward};
   int powerwash_count_{kPowerwashCountNotSet};
   bool powerwash_scheduled_{false};
+  bool is_rollback_powerwash_{false};
   int64_t build_timestamp_{0};
   bool first_active_omaha_ping_sent_{false};
 
diff --git a/common/hardware_interface.h b/common/hardware_interface.h
index bbc8660..0140588 100644
--- a/common/hardware_interface.h
+++ b/common/hardware_interface.h
@@ -102,8 +102,9 @@
   virtual int GetPowerwashCount() const = 0;
 
   // Signals that a powerwash (stateful partition wipe) should be performed
-  // after reboot.
-  virtual bool SchedulePowerwash() = 0;
+  // after reboot. If |is_rollback| is true additional state is preserved
+  // during shutdown that can be restored after the powerwash.
+  virtual bool SchedulePowerwash(bool is_rollback) = 0;
 
   // Cancel the powerwash operation scheduled to be performed on next boot.
   virtual bool CancelPowerwash() = 0;
diff --git a/hardware_android.cc b/hardware_android.cc
index a8a479d..21d4659 100644
--- a/hardware_android.cc
+++ b/hardware_android.cc
@@ -152,8 +152,9 @@
   return 0;
 }
 
-bool HardwareAndroid::SchedulePowerwash() {
+bool HardwareAndroid::SchedulePowerwash(bool is_rollback) {
   LOG(INFO) << "Scheduling a powerwash to BCB.";
+  LOG_IF(WARNING, is_rollback) << "is_rollback was true but isn't supported.";
   string err;
   if (!update_bootloader_message({"--wipe_data", "--reason=wipe_data_from_ota"},
                                  &err)) {
diff --git a/hardware_android.h b/hardware_android.h
index 920b659..5b3c99d 100644
--- a/hardware_android.h
+++ b/hardware_android.h
@@ -48,7 +48,7 @@
   bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override;
   bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override;
   int GetPowerwashCount() const override;
-  bool SchedulePowerwash() override;
+  bool SchedulePowerwash(bool is_rollback) override;
   bool CancelPowerwash() override;
   bool GetNonVolatileDirectory(base::FilePath* path) const override;
   bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
diff --git a/hardware_chromeos.cc b/hardware_chromeos.cc
index c5933b5..8ef05b2 100644
--- a/hardware_chromeos.cc
+++ b/hardware_chromeos.cc
@@ -61,9 +61,13 @@
 const char kPowerwashMarkerFile[] =
     "/mnt/stateful_partition/factory_install_reset";
 
-// The contents of the powerwash marker file.
+// The contents of the powerwash marker file for the non-rollback case.
 const char kPowerwashCommand[] = "safe fast keepimg reason=update_engine\n";
 
+// The contents of the powerwas marker file for the rollback case.
+const char kRollbackPowerwashCommand[] =
+    "safe fast keepimg rollback reason=update_engine\n";
+
 // UpdateManager config path.
 const char* kConfigFilePath = "/etc/update_manager.conf";
 
@@ -222,12 +226,15 @@
   return powerwash_count;
 }
 
-bool HardwareChromeOS::SchedulePowerwash() {
+bool HardwareChromeOS::SchedulePowerwash(bool is_rollback) {
+  const char* powerwash_command =
+      is_rollback ? kRollbackPowerwashCommand : kPowerwashCommand;
   bool result = utils::WriteFile(
-      kPowerwashMarkerFile, kPowerwashCommand, strlen(kPowerwashCommand));
+      kPowerwashMarkerFile, powerwash_command, strlen(powerwash_command));
   if (result) {
     LOG(INFO) << "Created " << kPowerwashMarkerFile
-              << " to powerwash on next reboot";
+              << " to powerwash on next reboot (is_rollback=" << is_rollback
+              << ")";
   } else {
     PLOG(ERROR) << "Error in creating powerwash marker file: "
                 << kPowerwashMarkerFile;
diff --git a/hardware_chromeos.h b/hardware_chromeos.h
index 5c66641..8829866 100644
--- a/hardware_chromeos.h
+++ b/hardware_chromeos.h
@@ -53,7 +53,7 @@
   bool SetMaxFirmwareKeyRollforward(int firmware_max_rollforward) override;
   bool SetMaxKernelKeyRollforward(int kernel_max_rollforward) override;
   int GetPowerwashCount() const override;
-  bool SchedulePowerwash() override;
+  bool SchedulePowerwash(bool is_rollback) override;
   bool CancelPowerwash() override;
   bool GetNonVolatileDirectory(base::FilePath* path) const override;
   bool GetPowerwashSafeDirectory(base::FilePath* path) const override;
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 83d910f..a782b8f 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -59,7 +59,7 @@
 
   // Currently we're always powerwashing when rolling back.
   if (install_plan_.powerwash_required || install_plan_.is_rollback) {
-    if (hardware_->SchedulePowerwash()) {
+    if (hardware_->SchedulePowerwash(install_plan_.is_rollback)) {
       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 8d461d5..caee5e2 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -255,6 +255,7 @@
 
   // Since powerwash_required was false, this should not trigger a powerwash.
   EXPECT_FALSE(fake_hardware_.IsPowerwashScheduled());
+  EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
 }
 
 TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) {
@@ -274,6 +275,7 @@
 
   // Check that powerwash was scheduled.
   EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
+  EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
 }
 
 TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTest) {
@@ -286,8 +288,9 @@
                        /*is_rollback=*/true);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 
-  // Check that powerwash was scheduled.
+  // Check that powerwash was scheduled and that it's a rollback powerwash.
   EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
+  EXPECT_TRUE(fake_hardware_.GetIsRollbackPowerwashScheduled());
 }
 
 // Runs postinstall from a partition file that doesn't mount, so it should
@@ -299,6 +302,7 @@
   // In case of failure, Postinstall should not signal a powerwash even if it
   // was requested.
   EXPECT_FALSE(fake_hardware_.IsPowerwashScheduled());
+  EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
 }
 
 // Check that the failures from the postinstall script cause the action to