Move extent copy and hash logic to a new file.

The SOURCE_COPY operation used to copy the source blocks one by one to
the target partition. This process is sub-optimal if there are several
consecutive blocks. This patch moves this copy and hash logic to a new
file and adds several unittests for it. The new logic copies in chunks
of up to 1MiB when the source and target data is contiguous.

BUG=b:34284069
TEST=Added unittests.

Change-Id: I9ed52b429a54a2b4d6edaba051284b7dcd8a9525
(cherry picked from commit a48f630400429ca010c5462967607985f2ffa7e4)
Reviewed-on: https://chromium-review.googlesource.com/641958
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/payload_consumer/file_descriptor_utils.cc b/payload_consumer/file_descriptor_utils.cc
new file mode 100644
index 0000000..f7f61a5
--- /dev/null
+++ b/payload_consumer/file_descriptor_utils.cc
@@ -0,0 +1,115 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_consumer/file_descriptor_utils.h"
+
+#include <algorithm>
+
+#include <base/logging.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/extent_writer.h"
+
+using google::protobuf::RepeatedPtrField;
+using std::min;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// 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 {
+
+bool CopyAndHashExtents(FileDescriptorPtr source,
+                        const RepeatedPtrField<Extent>& src_extents,
+                        FileDescriptorPtr target,
+                        const RepeatedPtrField<Extent>& tgt_extents,
+                        uint32_t block_size,
+                        brillo::Blob* hash_out) {
+  HashCalculator source_hasher;
+
+  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;
+  std::vector<Extent> vec_tgt_extents;
+  vec_tgt_extents.reserve(tgt_extents.size());
+  for (const auto& ext : tgt_extents) {
+    vec_tgt_extents.push_back(ext);
+  }
+  TEST_AND_RETURN_FALSE(writer.Init(target, vec_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));
+
+      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));
+      }
+    }
+  }
+  TEST_AND_RETURN_FALSE(writer.End());
+
+  if (hash_out) {
+    TEST_AND_RETURN_FALSE(source_hasher.Finalize());
+    *hash_out = source_hasher.raw_hash();
+  }
+  return true;
+}
+
+}  // namespace fd_utils
+
+}  // namespace chromeos_update_engine