Calculate verity hash tree.
Calculate verity hash tree in FilesystemVerifierAction based on configs
specified in protobuf, and write it to target partition before reading
from those blocks.
A new error code kVerityCalculationError was added to report error if
it fails.
Bug: 28171891
Test: update_engine_unittests
Change-Id: I492885a0655bf51043902f578720fffd87e6a3cf
diff --git a/payload_consumer/verity_writer_android.cc b/payload_consumer/verity_writer_android.cc
new file mode 100644
index 0000000..b374817
--- /dev/null
+++ b/payload_consumer/verity_writer_android.cc
@@ -0,0 +1,100 @@
+//
+// Copyright (C) 2018 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/verity_writer_android.h"
+
+#include <fcntl.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+
+#include "update_engine/common/utils.h"
+
+namespace chromeos_update_engine {
+
+namespace verity_writer {
+std::unique_ptr<VerityWriterInterface> CreateVerityWriter() {
+ return std::make_unique<VerityWriterAndroid>();
+}
+} // namespace verity_writer
+
+bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition) {
+ partition_ = &partition;
+
+ if (partition_->hash_tree_size != 0) {
+ auto hash_function =
+ HashTreeBuilder::HashFunction(partition_->hash_tree_algorithm);
+ if (hash_function == nullptr) {
+ LOG(ERROR) << "Verity hash algorithm not supported: "
+ << partition_->hash_tree_algorithm;
+ return false;
+ }
+ hash_tree_builder_ = std::make_unique<HashTreeBuilder>(
+ partition_->block_size, hash_function);
+ TEST_AND_RETURN_FALSE(hash_tree_builder_->Initialize(
+ partition_->hash_tree_data_size, partition_->hash_tree_salt));
+ if (hash_tree_builder_->CalculateSize(partition_->hash_tree_data_size) !=
+ partition_->hash_tree_size) {
+ LOG(ERROR) << "Verity hash tree size does not match, stored: "
+ << partition_->hash_tree_size << ", calculated: "
+ << hash_tree_builder_->CalculateSize(
+ partition_->hash_tree_data_size);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerityWriterAndroid::Update(uint64_t offset,
+ const uint8_t* buffer,
+ size_t size) {
+ if (partition_->hash_tree_size != 0) {
+ uint64_t hash_tree_data_end =
+ partition_->hash_tree_data_offset + partition_->hash_tree_data_size;
+ uint64_t start_offset = std::max(offset, partition_->hash_tree_data_offset);
+ uint64_t end_offset = std::min(offset + size, hash_tree_data_end);
+ if (start_offset < end_offset) {
+ TEST_AND_RETURN_FALSE(hash_tree_builder_->Update(
+ buffer + start_offset - offset, end_offset - start_offset));
+
+ if (end_offset == hash_tree_data_end) {
+ // All hash tree data blocks has been hashed, write hash tree to disk.
+ int fd = HANDLE_EINTR(open(partition_->target_path.c_str(), O_WRONLY));
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open " << partition_->target_path
+ << " to write verity data.";
+ return false;
+ }
+ ScopedFdCloser fd_closer(&fd);
+
+ LOG(INFO) << "Writing verity hash tree to " << partition_->target_path;
+ TEST_AND_RETURN_FALSE(hash_tree_builder_->BuildHashTree());
+ TEST_AND_RETURN_FALSE(hash_tree_builder_->WriteHashTreeToFd(
+ fd, partition_->hash_tree_offset));
+ hash_tree_builder_.reset();
+ }
+ }
+ }
+ if (partition_->fec_size != 0) {
+ // TODO(senj): Update FEC data.
+ }
+ return true;
+}
+
+} // namespace chromeos_update_engine