update_engine: Use ExtentReader

Updates CopyAndHashExtents to use the new ExtentReader.

Adds CalculateAndValidatffeSourceHash() to DeltaPerformer to be used in both
SOURCE_BSDIFF and PUFFDIFF.

BUG=chromium:761138
TEST=FEATURES="test" emerge-amd64-generic update_engine; brillo_update_payload verify

Change-Id: I2e0c10fe0078c5a1ab4cd646a91d42893b6b691b
Reviewed-on: https://chromium-review.googlesource.com/653478
Commit-Ready: Amin Hassani <ahassani@chromium.org>
Tested-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
Reviewed-by: Sen Jiang <senj@chromium.org>
diff --git a/common/utils.h b/common/utils.h
index 184e72b..262a403 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -88,8 +88,8 @@
 bool PReadAll(int fd, void* buf, size_t count, off_t offset,
               ssize_t* out_bytes_read);
 
-bool PReadAll(const FileDescriptorPtr& fd, void* buf, size_t count, off_t offset,
-              ssize_t* out_bytes_read);
+bool PReadAll(const FileDescriptorPtr& fd, void* buf, size_t count,
+              off_t offset, ssize_t* out_bytes_read);
 
 // Opens |path| for reading and appends its entire content to the container
 // pointed to by |out_p|. Returns true upon successfully reading all of the
@@ -256,6 +256,16 @@
   }
 }
 
+// Return the total number of blocks in the passed |extents| collection.
+template <class T>
+uint64_t BlocksInExtents(const T& extents) {
+  uint64_t sum = 0;
+  for (const auto& ext : extents) {
+    sum += ext.num_blocks();
+  }
+  return sum;
+}
+
 // Converts seconds into human readable notation including days, hours, minutes
 // and seconds. For example, 185 will yield 3m5s, 4300 will yield 1h11m40s, and
 // 360000 will yield 4d4h0m0s.  Zero padding not applied. Seconds are always
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 9299dcd..e6aa6d0 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -46,6 +46,7 @@
 #include "update_engine/common/terminator.h"
 #include "update_engine/payload_consumer/bzip_extent_writer.h"
 #include "update_engine/payload_consumer/download_action.h"
+#include "update_engine/payload_consumer/extent_reader.h"
 #include "update_engine/payload_consumer/extent_writer.h"
 #include "update_engine/payload_consumer/file_descriptor_utils.h"
 #if USE_MTD
@@ -1172,6 +1173,28 @@
   return true;
 }
 
+bool DeltaPerformer::CalculateAndValidateSourceHash(
+    const InstallOperation& operation, ErrorCode* error) {
+  const uint64_t kMaxBlocksToRead = 256;  // 1MB if block size is 4KB
+  auto total_blocks = utils::BlocksInExtents(operation.src_extents());
+  brillo::Blob buf(std::min(kMaxBlocksToRead, total_blocks) * block_size_);
+  DirectExtentReader reader;
+  TEST_AND_RETURN_FALSE(
+      reader.Init(source_fd_, operation.src_extents(), block_size_));
+  HashCalculator source_hasher;
+  while (total_blocks > 0) {
+    auto read_blocks = std::min(total_blocks, kMaxBlocksToRead);
+    TEST_AND_RETURN_FALSE(reader.Read(buf.data(), read_blocks * block_size_));
+    TEST_AND_RETURN_FALSE(
+        source_hasher.Update(buf.data(), read_blocks * block_size_));
+    total_blocks -= read_blocks;
+  }
+  TEST_AND_RETURN_FALSE(source_hasher.Finalize());
+  TEST_AND_RETURN_FALSE(
+      ValidateSourceHash(source_hasher.raw_hash(), operation, error));
+  return true;
+}
+
 bool DeltaPerformer::PerformSourceBsdiffOperation(
     const InstallOperation& operation, ErrorCode* error) {
   // Since we delete data off the beginning of the buffer as we use it,
@@ -1184,26 +1207,7 @@
     TEST_AND_RETURN_FALSE(operation.dst_length() % block_size_ == 0);
 
   if (operation.has_src_sha256_hash()) {
-    HashCalculator source_hasher;
-    const uint64_t kMaxBlocksToRead = 512;  // 2MB if block size is 4KB
-    brillo::Blob buf(kMaxBlocksToRead * block_size_);
-    for (const Extent& extent : operation.src_extents()) {
-      for (uint64_t i = 0; i < extent.num_blocks(); i += kMaxBlocksToRead) {
-        uint64_t blocks_to_read = min(
-            kMaxBlocksToRead, static_cast<uint64_t>(extent.num_blocks()) - i);
-        ssize_t bytes_to_read = blocks_to_read * block_size_;
-        ssize_t bytes_read_this_iteration = 0;
-        TEST_AND_RETURN_FALSE(
-            utils::PReadAll(source_fd_, buf.data(), bytes_to_read,
-                            (extent.start_block() + i) * block_size_,
-                            &bytes_read_this_iteration));
-        TEST_AND_RETURN_FALSE(bytes_read_this_iteration == bytes_to_read);
-        TEST_AND_RETURN_FALSE(source_hasher.Update(buf.data(), bytes_to_read));
-      }
-    }
-    TEST_AND_RETURN_FALSE(source_hasher.Finalize());
-    TEST_AND_RETURN_FALSE(
-        ValidateSourceHash(source_hasher.raw_hash(), operation, error));
+    TEST_AND_RETURN_FALSE(CalculateAndValidateSourceHash(operation, error));
   }
 
   string input_positions;
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 038b260..5fc972b 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -19,6 +19,7 @@
 
 #include <inttypes.h>
 
+#include <limits>
 #include <string>
 #include <vector>
 
@@ -162,9 +163,9 @@
     public_key_path_ = public_key_path;
   }
 
-  // Set |*out_offset| to the byte offset where the size of the metadata signature
-  // is stored in a payload. Return true on success, if this field is not
-  // present in the payload, return false.
+  // Set |*out_offset| to the byte offset where the size of the metadata
+  // signature is stored in a payload. Return true on success, if this field is
+  // not present in the payload, return false.
   bool GetMetadataSignatureSizeOffset(uint64_t* out_offset) const;
 
   // Set |*out_offset| to the byte offset at which the manifest protobuf begins
@@ -242,6 +243,10 @@
   // buffer.
   ErrorCode ValidateMetadataSignature(const brillo::Blob& payload);
 
+  // Calculates and validates the source hash of the operation |operation|.
+  bool CalculateAndValidateSourceHash(const InstallOperation& operation,
+                                      ErrorCode* error);
+
   // Returns true on success.
   bool PerformInstallOperation(const InstallOperation& operation);
 
diff --git a/payload_consumer/file_descriptor_utils.cc b/payload_consumer/file_descriptor_utils.cc
index b092149..73f86df 100644
--- a/payload_consumer/file_descriptor_utils.cc
+++ b/payload_consumer/file_descriptor_utils.cc
@@ -22,6 +22,7 @@
 
 #include "update_engine/common/hash_calculator.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/extent_reader.h"
 #include "update_engine/payload_consumer/extent_writer.h"
 
 using google::protobuf::RepeatedPtrField;
@@ -34,15 +35,6 @@
 // Size of the buffer used to copy blocks.
 const int kMaxCopyBufferSize = 1024 * 1024;
 
-// Return the total number of blocks in the passed |extents| list.
-uint64_t GetBlockCount(const RepeatedPtrField<Extent>& extents) {
-  uint64_t sum = 0;
-  for (const Extent& ext : extents) {
-    sum += ext.num_blocks();
-  }
-  return sum;
-}
-
 }  // namespace
 
 namespace fd_utils {
@@ -53,48 +45,31 @@
                         const RepeatedPtrField<Extent>& tgt_extents,
                         uint32_t block_size,
                         brillo::Blob* hash_out) {
-  HashCalculator source_hasher;
+  uint64_t total_blocks = utils::BlocksInExtents(src_extents);
+  TEST_AND_RETURN_FALSE(total_blocks == utils::BlocksInExtents(tgt_extents));
+
+  DirectExtentReader reader;
+  TEST_AND_RETURN_FALSE(reader.Init(source, src_extents, block_size));
+  DirectExtentWriter writer;
+  TEST_AND_RETURN_FALSE(writer.Init(target, tgt_extents, block_size));
 
   uint64_t buffer_blocks = kMaxCopyBufferSize / block_size;
   // Ensure we copy at least one block at a time.
   if (buffer_blocks < 1)
     buffer_blocks = 1;
-
-  uint64_t total_blocks = GetBlockCount(src_extents);
-  TEST_AND_RETURN_FALSE(total_blocks == GetBlockCount(tgt_extents));
-
   brillo::Blob buf(buffer_blocks * block_size);
 
-  DirectExtentWriter writer;
-  TEST_AND_RETURN_FALSE(writer.Init(target, tgt_extents, block_size));
-
-  for (const Extent& src_ext : src_extents) {
-    for (uint64_t src_ext_block = 0; src_ext_block < src_ext.num_blocks();
-         src_ext_block += buffer_blocks) {
-      uint64_t iteration_blocks =
-          min(buffer_blocks,
-              static_cast<uint64_t>(src_ext.num_blocks() - src_ext_block));
-      uint64_t src_start_block = src_ext.start_block() + src_ext_block;
-
-      ssize_t bytes_read_this_iteration;
-      TEST_AND_RETURN_FALSE(utils::PReadAll(source,
-                                            buf.data(),
-                                            iteration_blocks * block_size,
-                                            src_start_block * block_size,
-                                            &bytes_read_this_iteration));
-
+  HashCalculator source_hasher;
+  uint64_t blocks_left = total_blocks;
+  while (blocks_left > 0) {
+    uint64_t read_blocks = std::min(blocks_left, buffer_blocks);
+    TEST_AND_RETURN_FALSE(reader.Read(buf.data(), read_blocks * block_size));
+    if (hash_out) {
       TEST_AND_RETURN_FALSE(
-          bytes_read_this_iteration ==
-          static_cast<ssize_t>(iteration_blocks * block_size));
-
-      TEST_AND_RETURN_FALSE(
-          writer.Write(buf.data(), iteration_blocks * block_size));
-
-      if (hash_out) {
-        TEST_AND_RETURN_FALSE(
-            source_hasher.Update(buf.data(), iteration_blocks * block_size));
-      }
+          source_hasher.Update(buf.data(), read_blocks * block_size));
     }
+    TEST_AND_RETURN_FALSE(writer.Write(buf.data(), read_blocks * block_size));
+    blocks_left -= read_blocks;
   }
   TEST_AND_RETURN_FALSE(writer.End());
 
diff --git a/payload_consumer/file_descriptor_utils_unittest.cc b/payload_consumer/file_descriptor_utils_unittest.cc
index 42bdf17..8ba8ce6 100644
--- a/payload_consumer/file_descriptor_utils_unittest.cc
+++ b/payload_consumer/file_descriptor_utils_unittest.cc
@@ -32,13 +32,15 @@
 #include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 
+using google::protobuf::RepeatedPtrField;
+
 namespace chromeos_update_engine {
 
 namespace {
 
-::google::protobuf::RepeatedPtrField<Extent> CreateExtentList(
+RepeatedPtrField<Extent> CreateExtentList(
     const std::vector<std::pair<uint64_t, uint64_t>>& lst) {
-  ::google::protobuf::RepeatedPtrField<Extent> result;
+  RepeatedPtrField<Extent> result;
   for (const auto& item : lst) {
     *result.Add() = ExtentForRange(item.first, item.second);
   }