Optional source filesystem verification in minor version 3.

In minor version 3, we use per-operation source hash to verify the source
instead of whole filesystem hash, but if target partition doesn't match,
we still need to check the source partition to see if it is the cause.

Bug: 23182225
TEST=Applied a minor version 3 delta payload.
TEST=cros_workon_make update_engine --test

Change-Id: I1f32c292d2938540b63f086cf4988d64620702a5
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index 6768407..8530d37 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -30,6 +30,7 @@
 
 #include "update_engine/common/boot_control_interface.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/delta_performer.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 
 using std::string;
@@ -59,7 +60,9 @@
   // For delta updates (major version 1) we need to populate the source
   // partition hash if not pre-populated.
   if (!install_plan_.is_full_update && install_plan_.partitions.empty() &&
-      verifier_mode_ == VerifierMode::kComputeSourceHash) {
+      verifier_mode_ == VerifierMode::kComputeSourceHash &&
+      DeltaPerformer::kSupportedMinorPayloadVersion <
+          kOpSrcHashMinorPayloadVersion) {
     LOG(INFO) << "Using legacy partition names.";
     InstallPlan::Partition part;
     string part_path;
@@ -124,7 +127,13 @@
 
 void FilesystemVerifierAction::StartPartitionHashing() {
   if (partition_index_ == install_plan_.partitions.size()) {
-    Cleanup(ErrorCode::kSuccess);
+    // We never called this action with kVerifySourceHash directly, if we are in
+    // this mode, it means the target partition verification has failed, so we
+    // should set the error code to reflect the error in target.
+    if (verifier_mode_ == VerifierMode::kVerifySourceHash)
+      Cleanup(ErrorCode::kNewRootfsVerificationError);
+    else
+      Cleanup(ErrorCode::kSuccess);
     return;
   }
   InstallPlan::Partition& partition =
@@ -133,6 +142,7 @@
   string part_path;
   switch (verifier_mode_) {
     case VerifierMode::kComputeSourceHash:
+    case VerifierMode::kVerifySourceHash:
       boot_control_->GetPartitionDevice(
           partition.name, install_plan_.source_slot, &part_path);
       remaining_size_ = partition.source_size;
@@ -239,17 +249,35 @@
   switch (verifier_mode_) {
     case VerifierMode::kComputeSourceHash:
       partition.source_hash = hasher_->raw_hash();
+      partition_index_++;
       break;
     case VerifierMode::kVerifyTargetHash:
       if (partition.target_hash != hasher_->raw_hash()) {
         LOG(ERROR) << "New '" << partition.name
                    << "' partition verification failed.";
-        return Cleanup(ErrorCode::kNewRootfsVerificationError);
+        if (DeltaPerformer::kSupportedMinorPayloadVersion <
+            kOpSrcHashMinorPayloadVersion)
+          return Cleanup(ErrorCode::kNewRootfsVerificationError);
+        // If we support per-operation source hash, then we skipped source
+        // filesystem verification, now that the target partition does not
+        // match, we need to switch to kVerifySourceHash mode to check if it's
+        // because the source partition does not match either.
+        verifier_mode_ = VerifierMode::kVerifySourceHash;
+        partition_index_ = 0;
+      } else {
+        partition_index_++;
       }
       break;
+    case VerifierMode::kVerifySourceHash:
+      if (partition.source_hash != hasher_->raw_hash()) {
+        LOG(ERROR) << "Old '" << partition.name
+                   << "' partition verification failed.";
+        return Cleanup(ErrorCode::kDownloadStateInitializationError);
+      }
+      partition_index_++;
+      break;
   }
   // Start hashing the next partition, if any.
-  partition_index_++;
   hasher_.reset();
   buffer_.clear();
   src_stream_->CloseBlocking(nullptr);
diff --git a/payload_consumer/filesystem_verifier_action.h b/payload_consumer/filesystem_verifier_action.h
index 0a66b01..94f1b4e 100644
--- a/payload_consumer/filesystem_verifier_action.h
+++ b/payload_consumer/filesystem_verifier_action.h
@@ -45,6 +45,7 @@
 enum class VerifierMode {
   kComputeSourceHash,
   kVerifyTargetHash,
+  kVerifySourceHash,
 };
 
 class FilesystemVerifierAction : public InstallPlanAction {
@@ -92,7 +93,7 @@
   void Cleanup(ErrorCode code);
 
   // The type of the partition that we are verifying.
-  const VerifierMode verifier_mode_;
+  VerifierMode verifier_mode_;
 
   // The BootControlInterface used to get the partitions based on the slots.
   const BootControlInterface* const boot_control_;
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index 7b88aca..f6f1a16 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -181,6 +181,7 @@
       }
       break;
     case VerifierMode::kComputeSourceHash:
+    case VerifierMode::kVerifySourceHash:
       part.source_size = kLoopFileSize;
       part.source_path = a_dev;
       fake_boot_control_.SetPartitionDevice(