delta_generator: Include per-operation source hash.

This adds a src_sha256_hash field in the operation if minor version is 3.

Bug: 23182225
TEST=cros_workon_make update_engine --test
TEST=Generated a minor version 3 payload, the manifest is about 50% larger.

Change-Id: I0e363fca4de24bae64ac83930afdd42a6ddc21c9
diff --git a/payload_constants.cc b/payload_constants.cc
index cc18430..b831240 100644
--- a/payload_constants.cc
+++ b/payload_constants.cc
@@ -24,6 +24,7 @@
 const uint32_t kFullPayloadMinorVersion = 0;
 const uint32_t kInPlaceMinorPayloadVersion = 1;
 const uint32_t kSourceMinorPayloadVersion = 2;
+const uint32_t kOpSrcHashMinorPayloadVersion = 3;
 
 const char kLegacyPartitionNameKernel[] = "boot";
 const char kLegacyPartitionNameRoot[] = "system";
diff --git a/payload_constants.h b/payload_constants.h
index 38cc075..d423eeb 100644
--- a/payload_constants.h
+++ b/payload_constants.h
@@ -40,6 +40,9 @@
 // The minor version used by the A to B delta generator algorithm.
 extern const uint32_t kSourceMinorPayloadVersion;
 
+// The minor version that allows per-operation source hash.
+extern const uint32_t kOpSrcHashMinorPayloadVersion;
+
 
 // The kernel and rootfs partition names used by the BootControlInterface when
 // handling update payloads with a major version 1. The names of the updated
diff --git a/payload_generator/ab_generator.cc b/payload_generator/ab_generator.cc
index 336e6a8..2658fb8 100644
--- a/payload_generator/ab_generator.cc
+++ b/payload_generator/ab_generator.cc
@@ -20,7 +20,8 @@
 
 #include <base/strings/stringprintf.h>
 
-#include "update_engine/delta_performer.h"
+#include "update_engine/omaha_hash_calculator.h"
+#include "update_engine/payload_constants.h"
 #include "update_engine/payload_generator/annotated_operation.h"
 #include "update_engine/payload_generator/bzip.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
@@ -72,6 +73,10 @@
                                         merge_chunk_blocks,
                                         new_part.path,
                                         blob_file));
+
+  if (config.minor_version == kOpSrcHashMinorPayloadVersion)
+    TEST_AND_RETURN_FALSE(AddSourceHash(aops, old_part.path));
+
   return true;
 }
 
@@ -305,4 +310,26 @@
   return true;
 }
 
+bool ABGenerator::AddSourceHash(vector<AnnotatedOperation>* aops,
+                                const string& source_part_path) {
+  for (AnnotatedOperation& aop : *aops) {
+    if (aop.op.src_extents_size() == 0)
+      continue;
+
+    vector<Extent> src_extents;
+    ExtentsToVector(aop.op.src_extents(), &src_extents);
+    brillo::Blob src_data, src_hash;
+    uint64_t src_length =
+        aop.op.has_src_length()
+            ? aop.op.src_length()
+            : BlocksInExtents(aop.op.src_extents()) * kBlockSize;
+    TEST_AND_RETURN_FALSE(utils::ReadExtents(
+        source_part_path, src_extents, &src_data, src_length, kBlockSize));
+    TEST_AND_RETURN_FALSE(
+        OmahaHashCalculator::RawHashOfData(src_data, &src_hash));
+    aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
+  }
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/ab_generator.h b/payload_generator/ab_generator.h
index 25a7811..31cbb1e 100644
--- a/payload_generator/ab_generator.h
+++ b/payload_generator/ab_generator.h
@@ -107,6 +107,11 @@
                               const std::string& target_part,
                               BlobFileWriter* blob_file);
 
+  // Takes a vector of AnnotatedOperations |aops|, adds source hash to all
+  // operations that have src_extents.
+  static bool AddSourceHash(std::vector<AnnotatedOperation>* aops,
+                            const std::string& source_part_path);
+
  private:
   // Adds the data payload for a REPLACE/REPLACE_BZ operation |aop| by reading
   // its output extents from |target_part_path| and appending a corresponding
diff --git a/payload_generator/ab_generator_unittest.cc b/payload_generator/ab_generator_unittest.cc
index 4248f8d..8854225 100644
--- a/payload_generator/ab_generator_unittest.cc
+++ b/payload_generator/ab_generator_unittest.cc
@@ -25,6 +25,7 @@
 
 #include <gtest/gtest.h>
 
+#include "update_engine/omaha_hash_calculator.h"
 #include "update_engine/payload_generator/annotated_operation.h"
 #include "update_engine/payload_generator/bzip.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
@@ -550,4 +551,40 @@
   EXPECT_EQ(aops.size(), 4);
 }
 
+TEST_F(ABGeneratorTest, AddSourceHashTest) {
+  vector<AnnotatedOperation> aops;
+  InstallOperation first_op;
+  first_op.set_type(InstallOperation::SOURCE_COPY);
+  first_op.set_src_length(kBlockSize);
+  *(first_op.add_src_extents()) = ExtentForRange(0, 1);
+  AnnotatedOperation first_aop;
+  first_aop.op = first_op;
+  aops.push_back(first_aop);
+
+  InstallOperation second_op;
+  second_op.set_type(InstallOperation::REPLACE);
+  AnnotatedOperation second_aop;
+  second_aop.op = second_op;
+  aops.push_back(second_aop);
+
+  string src_part_path;
+  EXPECT_TRUE(utils::MakeTempFile("AddSourceHashTest_src_part.XXXXXX",
+                                  &src_part_path, nullptr));
+  ScopedPathUnlinker src_part_path_unlinker(src_part_path);
+  brillo::Blob src_data(kBlockSize);
+  test_utils::FillWithData(&src_data);
+  ASSERT_TRUE(utils::WriteFile(src_part_path.c_str(), src_data.data(),
+                               src_data.size()));
+
+  EXPECT_TRUE(ABGenerator::AddSourceHash(&aops, src_part_path));
+
+  EXPECT_TRUE(aops[0].op.has_src_sha256_hash());
+  EXPECT_FALSE(aops[1].op.has_src_sha256_hash());
+  brillo::Blob expected_hash;
+  EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(src_data, &expected_hash));
+  brillo::Blob result_hash(aops[0].op.src_sha256_hash().begin(),
+                           aops[0].op.src_sha256_hash().end());
+  EXPECT_EQ(expected_hash, result_hash);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index af81e62..96730aa 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -104,7 +104,8 @@
         if (config.minor_version == kInPlaceMinorPayloadVersion) {
           LOG(INFO) << "Using generator InplaceGenerator().";
           strategy.reset(new InplaceGenerator());
-        } else if (config.minor_version == kSourceMinorPayloadVersion) {
+        } else if (config.minor_version == kSourceMinorPayloadVersion ||
+                   config.minor_version == kOpSrcHashMinorPayloadVersion) {
           LOG(INFO) << "Using generator ABGenerator().";
           strategy.reset(new ABGenerator());
         } else {
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index 66928d3..c2a78cc 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -99,7 +99,8 @@
 
     // Check for the supported minor_version values.
     TEST_AND_RETURN_FALSE(minor_version == kInPlaceMinorPayloadVersion ||
-                          minor_version == kSourceMinorPayloadVersion);
+                          minor_version == kSourceMinorPayloadVersion ||
+                          minor_version == kOpSrcHashMinorPayloadVersion);
 
     // If new_image_info is present, old_image_info must be present.
     TEST_AND_RETURN_FALSE(source.ImageInfoIsEmpty() ==