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