diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-06-30 10:08:23 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-06-30 10:08:23 +0000 |
commit | e4bce2440e498e78ba7a4ef52b53e059147f2f14 (patch) | |
tree | eaf5efaa52859f5cc863de675b8436763e033c22 | |
parent | 04816a52949c2d4f69a5a3abd5843316acbde836 (diff) | |
parent | 2218a7229a48b3fe97a4054e0875327f768ab64a (diff) |
Snap for 7508253 from 2218a7229a48b3fe97a4054e0875327f768ab64a to s-keystone-qcom-release
Change-Id: I31e87662f49ec0ed9b8b82276d8e986b7208e002
-rw-r--r-- | aosp/update_attempter_android.cc | 17 | ||||
-rw-r--r-- | aosp/update_attempter_android_unittest.cc | 15 | ||||
-rw-r--r-- | common/constants.cc | 1 | ||||
-rw-r--r-- | common/constants.h | 1 | ||||
-rw-r--r-- | common/scoped_task_id.h | 123 | ||||
-rw-r--r-- | payload_consumer/file_descriptor.cc | 4 | ||||
-rw-r--r-- | payload_consumer/filesystem_verifier_action.cc | 290 | ||||
-rw-r--r-- | payload_consumer/filesystem_verifier_action.h | 32 | ||||
-rw-r--r-- | payload_consumer/filesystem_verifier_action_unittest.cc | 342 | ||||
-rw-r--r-- | stable/Android.bp | 1 | ||||
-rw-r--r-- | stable/aidl_api/libupdate_engine_stable/1/.hash | 1 | ||||
-rw-r--r-- | stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStable.aidl | 39 | ||||
-rw-r--r-- | stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStableCallback.aidl | 38 |
13 files changed, 678 insertions, 226 deletions
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc index ba61f255..4636c430 100644 --- a/aosp/update_attempter_android.cc +++ b/aosp/update_attempter_android.cc @@ -364,6 +364,12 @@ bool UpdateAttempterAndroid::ResetStatus(brillo::ErrorPtr* error) { LOG(INFO) << "Attempting to reset state from " << UpdateStatusToString(status_) << " to UpdateStatus::IDLE"; + if (apex_handler_android_ != nullptr) { + LOG(INFO) << "Cleaning up reserved space for compressed APEX (if any)"; + std::vector<ApexInfo> apex_infos_blank; + apex_handler_android_->AllocateSpace(apex_infos_blank); + } + switch (status_) { case UpdateStatus::IDLE: { if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_)) { @@ -884,20 +890,25 @@ void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() { string current_version = android::base::GetProperty("ro.build.version.incremental", ""); TEST_AND_RETURN(!current_version.empty()); + const auto current_slot = boot_control_->GetCurrentSlot(); // If there's no record of previous version (e.g. due to a data wipe), we // save the info of current boot and skip the metrics report. if (!prefs_->Exists(kPrefsPreviousVersion)) { prefs_->SetString(kPrefsBootId, current_boot_id); prefs_->SetString(kPrefsPreviousVersion, current_version); + prefs_->SetInt64(std::string{kPrefsPreviousSlot}, + boot_control_->GetCurrentSlot()); ClearMetricsPrefs(); return; } + int64_t previous_slot = -1; + prefs_->GetInt64(kPrefsPreviousSlot, &previous_slot); string previous_version; - // update_engine restarted under the same build. + // update_engine restarted under the same build and same slot. // TODO(xunchang) identify and report rollback by checking UpdateMarker. if (prefs_->GetString(kPrefsPreviousVersion, &previous_version) && - previous_version == current_version) { + previous_version == current_version && previous_slot == current_slot) { string last_boot_id; bool is_reboot = prefs_->Exists(kPrefsBootId) && (prefs_->GetString(kPrefsBootId, &last_boot_id) && @@ -917,6 +928,8 @@ void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() { // TODO(xunchang) check the build version is larger than the previous one. prefs_->SetString(kPrefsBootId, current_boot_id); prefs_->SetString(kPrefsPreviousVersion, current_version); + prefs_->SetInt64(std::string{kPrefsPreviousSlot}, + boot_control_->GetCurrentSlot()); bool previous_attempt_exists = prefs_->Exists(kPrefsPayloadAttemptNumber); // |kPrefsPayloadAttemptNumber| should be cleared upon successful update. diff --git a/aosp/update_attempter_android_unittest.cc b/aosp/update_attempter_android_unittest.cc index f799df3e..f73df168 100644 --- a/aosp/update_attempter_android_unittest.cc +++ b/aosp/update_attempter_android_unittest.cc @@ -24,6 +24,7 @@ #include <base/time/time.h> #include <gtest/gtest.h> +#include "common/constants.h" #include "update_engine/aosp/daemon_state_android.h" #include "update_engine/common/fake_boot_control.h" #include "update_engine/common/fake_clock.h" @@ -81,6 +82,8 @@ TEST_F(UpdateAttempterAndroidTest, UpdatePrefsSameBuildVersionOnInit) { prefs_.SetString(kPrefsPreviousVersion, build_version); prefs_.SetString(kPrefsBootId, "oldboot"); prefs_.SetInt64(kPrefsNumReboots, 1); + prefs_.SetInt64(kPrefsPreviousSlot, 1); + boot_control_.SetCurrentSlot(1); EXPECT_CALL(*metrics_reporter_, ReportTimeToReboot(_)).Times(0); update_attempter_android_.Init(); @@ -88,15 +91,15 @@ TEST_F(UpdateAttempterAndroidTest, UpdatePrefsSameBuildVersionOnInit) { // Check that the boot_id and reboot_count are updated. std::string boot_id; utils::GetBootId(&boot_id); - EXPECT_TRUE(prefs_.Exists(kPrefsBootId)); + ASSERT_TRUE(prefs_.Exists(kPrefsBootId)); std::string prefs_boot_id; - EXPECT_TRUE(prefs_.GetString(kPrefsBootId, &prefs_boot_id)); - EXPECT_EQ(boot_id, prefs_boot_id); + ASSERT_TRUE(prefs_.GetString(kPrefsBootId, &prefs_boot_id)); + ASSERT_EQ(boot_id, prefs_boot_id); - EXPECT_TRUE(prefs_.Exists(kPrefsNumReboots)); + ASSERT_TRUE(prefs_.Exists(kPrefsNumReboots)); int64_t reboot_count; - EXPECT_TRUE(prefs_.GetInt64(kPrefsNumReboots, &reboot_count)); - EXPECT_EQ(2, reboot_count); + ASSERT_TRUE(prefs_.GetInt64(kPrefsNumReboots, &reboot_count)); + ASSERT_EQ(2, reboot_count); } TEST_F(UpdateAttempterAndroidTest, UpdatePrefsBuildVersionChangeOnInit) { diff --git a/common/constants.cc b/common/constants.cc index a9cf238d..0677e663 100644 --- a/common/constants.cc +++ b/common/constants.cc @@ -112,6 +112,7 @@ const char kPrefsWallClockScatteringWaitPeriod[] = "wall-clock-wait-period"; const char kPrefsWallClockStagingWaitPeriod[] = "wall-clock-staging-wait-period"; const char kPrefsManifestBytes[] = "manifest-bytes"; +const char kPrefsPreviousSlot[] = "previous-slot"; // These four fields are generated by scripts/brillo_update_payload. const char kPayloadPropertyFileSize[] = "FILE_SIZE"; diff --git a/common/constants.h b/common/constants.h index 64447cea..68f720db 100644 --- a/common/constants.h +++ b/common/constants.h @@ -76,6 +76,7 @@ extern const char kPrefsPingLastRollcall[]; extern const char kPrefsLastFp[]; extern const char kPrefsPostInstallSucceeded[]; extern const char kPrefsPreviousVersion[]; +extern const char kPrefsPreviousSlot[]; extern const char kPrefsResumedUpdateFailures[]; extern const char kPrefsRollbackHappened[]; extern const char kPrefsRollbackVersion[]; diff --git a/common/scoped_task_id.h b/common/scoped_task_id.h new file mode 100644 index 00000000..91a29860 --- /dev/null +++ b/common/scoped_task_id.h @@ -0,0 +1,123 @@ +// +// Copyright (C) 2021 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. +// + +#ifndef UPDATE_ENGINE_SCOPED_TASK_ID_H_ +#define UPDATE_ENGINE_SCOPED_TASK_ID_H_ + +#include <type_traits> +#include <utility> + +#include <base/bind.h> +#include <brillo/message_loops/message_loop.h> + +namespace chromeos_update_engine { + +// This class provides unique_ptr like semantic for |MessageLoop::TaskId|, when +// instance of this class goes out of scope, underlying task will be cancelled. +class ScopedTaskId { + using MessageLoop = brillo::MessageLoop; + + public: + // Move only type similar to unique_ptr. + ScopedTaskId(const ScopedTaskId&) = delete; + ScopedTaskId& operator=(const ScopedTaskId&) = delete; + + constexpr ScopedTaskId() = default; + + constexpr ScopedTaskId(ScopedTaskId&& other) noexcept { + *this = std::move(other); + } + + constexpr ScopedTaskId& operator=(ScopedTaskId&& other) noexcept { + std::swap(task_id_, other.task_id_); + return *this; + } + + // Post a callback on current message loop, return true if succeeded, false if + // the previous callback hasn't run yet, or scheduling failed at MessageLoop + // side. + [[nodiscard]] bool PostTask(const base::Location& from_here, + base::OnceClosure&& callback, + base::TimeDelta delay = {}) noexcept { + return PostTask<decltype(callback)>(from_here, std::move(callback), delay); + } + [[nodiscard]] bool PostTask(const base::Location& from_here, + std::function<void()>&& callback, + base::TimeDelta delay = {}) noexcept { + return PostTask<decltype(callback)>(from_here, std::move(callback), delay); + } + + ~ScopedTaskId() noexcept { Cancel(); } + + // Cancel the underlying managed task, true if cancel successful. False if no + // task scheduled or task cancellation failed + bool Cancel() noexcept { + if (task_id_ != MessageLoop::kTaskIdNull) { + if (MessageLoop::current()->CancelTask(task_id_)) { + LOG(INFO) << "Cancelled task id " << task_id_; + task_id_ = MessageLoop::kTaskIdNull; + return true; + } + } + return false; + } + + [[nodiscard]] constexpr bool IsScheduled() const noexcept { + return task_id_ != MessageLoop::kTaskIdNull; + } + + [[nodiscard]] constexpr bool operator==(const ScopedTaskId& other) const + noexcept { + return other.task_id_ == task_id_; + } + + [[nodiscard]] constexpr bool operator<(const ScopedTaskId& other) const + noexcept { + return task_id_ < other.task_id_; + } + + private: + template <typename Callable> + [[nodiscard]] bool PostTask(const base::Location& from_here, + Callable&& callback, + base::TimeDelta delay) noexcept { + if (task_id_ != MessageLoop::kTaskIdNull) { + LOG(ERROR) << "Scheduling another task but task id " << task_id_ + << " isn't executed yet! This can cause the old task to leak."; + return false; + } + task_id_ = MessageLoop::current()->PostDelayedTask( + from_here, + base::BindOnce(&ScopedTaskId::ExecuteTask<decltype(callback)>, + base::Unretained(this), + std::move(callback)), + delay); + return task_id_ != MessageLoop::kTaskIdNull; + } + template <typename Callable> + void ExecuteTask(Callable&& callback) { + task_id_ = MessageLoop::kTaskIdNull; + if constexpr (std::is_same_v<Callable&&, base::OnceClosure&&>) { + std::move(callback).Run(); + } else { + std::move(callback)(); + } + } + MessageLoop::TaskId task_id_{MessageLoop::kTaskIdNull}; +}; +} // namespace chromeos_update_engine + +#endif diff --git a/payload_consumer/file_descriptor.cc b/payload_consumer/file_descriptor.cc index 7c69c1b4..da76327c 100644 --- a/payload_consumer/file_descriptor.cc +++ b/payload_consumer/file_descriptor.cc @@ -139,7 +139,9 @@ bool EintrSafeFileDescriptor::Flush() { } bool EintrSafeFileDescriptor::Close() { - CHECK_GE(fd_, 0); + if (fd_ < 0) { + return false; + } // https://stackoverflow.com/questions/705454/does-linux-guarantee-the-contents-of-a-file-is-flushed-to-disc-after-close // |close()| doesn't imply |fsync()|, we need to do it manually. fsync(fd_); diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc index b14cbc8d..22c8e0bd 100644 --- a/payload_consumer/filesystem_verifier_action.cc +++ b/payload_consumer/filesystem_verifier_action.cc @@ -35,6 +35,7 @@ #include <brillo/secure_blob.h> #include <brillo/streams/file_stream.h> +#include "common/error_code.h" #include "payload_generator/delta_diff_generator.h" #include "update_engine/common/utils.h" #include "update_engine/payload_consumer/file_descriptor.h" @@ -77,6 +78,7 @@ namespace chromeos_update_engine { namespace { const off_t kReadFileBufferSize = 128 * 1024; +constexpr float kVerityProgressPercent = 0.6; } // namespace void FilesystemVerifierAction::PerformAction() { @@ -102,7 +104,6 @@ void FilesystemVerifierAction::PerformAction() { } void FilesystemVerifierAction::TerminateProcessing() { - brillo::MessageLoop::current()->CancelTask(pending_task_id_); cancelled_ = true; Cleanup(ErrorCode::kSuccess); // error code is ignored if canceled_ is true. } @@ -134,11 +135,29 @@ void FilesystemVerifierAction::UpdateProgress(double progress) { } } -bool FilesystemVerifierAction::InitializeFdVABC() { +void FilesystemVerifierAction::UpdatePartitionProgress(double progress) { + // We don't consider sizes of each partition. Every partition + // has the same length on progress bar. + // TODO(b/186087589): Take sizes of each partition into account. + UpdateProgress((progress + partition_index_) / + install_plan_.partitions.size()); +} + +bool FilesystemVerifierAction::InitializeFdVABC(bool should_write_verity) { const InstallPlan::Partition& partition = install_plan_.partitions[partition_index_]; - if (!ShouldWriteVerity()) { + if (!should_write_verity) { + // In VABC, we cannot map/unmap partitions w/o first closing ALL fds first. + // Since this function might be called inside a ScheduledTask, the closure + // might have a copy of partition_fd_ when executing this function. Which + // means even if we do |partition_fd_.reset()| here, there's a chance that + // underlying fd isn't closed until we return. This is unacceptable, we need + // to close |partition_fd| right away. + if (partition_fd_) { + partition_fd_->Close(); + partition_fd_.reset(); + } // In VABC, if we are not writing verity, just map all partitions, // and read using regular fd on |postinstall_mount_device| . // All read will go through snapuserd, which provides a consistent @@ -152,8 +171,6 @@ bool FilesystemVerifierAction::InitializeFdVABC() { dynamic_control_->MapAllPartitions(); return InitializeFd(partition.readonly_target_path); } - - // FilesystemVerifierAction need the read_fd_. partition_fd_ = dynamic_control_->OpenCowFd(partition.name, partition.source_path, true); if (!partition_fd_) { @@ -180,6 +197,112 @@ bool FilesystemVerifierAction::InitializeFd(const std::string& part_path) { return true; } +void FilesystemVerifierAction::WriteVerityAndHashPartition( + FileDescriptorPtr fd, + const off64_t start_offset, + const off64_t end_offset, + void* buffer, + const size_t buffer_size) { + if (start_offset >= end_offset) { + LOG_IF(WARNING, start_offset > end_offset) + << "start_offset is greater than end_offset : " << start_offset << " > " + << end_offset; + if (!verity_writer_->Finalize(fd, fd)) { + LOG(ERROR) << "Failed to write verity data"; + Cleanup(ErrorCode::kVerityCalculationError); + return; + } + if (dynamic_control_->UpdateUsesSnapshotCompression()) { + // Spin up snapuserd to read fs. + if (!InitializeFdVABC(false)) { + LOG(ERROR) << "Failed to map all partitions"; + Cleanup(ErrorCode::kFilesystemVerifierError); + return; + } + } + HashPartition(partition_fd_, 0, partition_size_, buffer, buffer_size); + return; + } + const auto cur_offset = fd->Seek(start_offset, SEEK_SET); + if (cur_offset != start_offset) { + PLOG(ERROR) << "Failed to seek to offset: " << start_offset; + Cleanup(ErrorCode::kVerityCalculationError); + return; + } + const auto read_size = + std::min<size_t>(buffer_size, end_offset - start_offset); + const auto bytes_read = fd->Read(buffer, read_size); + if (bytes_read < 0 || static_cast<size_t>(bytes_read) != read_size) { + PLOG(ERROR) << "Failed to read offset " << start_offset << " expected " + << read_size << " bytes, actual: " << bytes_read; + Cleanup(ErrorCode::kVerityCalculationError); + return; + } + if (!verity_writer_->Update( + start_offset, static_cast<const uint8_t*>(buffer), read_size)) { + LOG(ERROR) << "VerityWriter::Update() failed"; + Cleanup(ErrorCode::kVerityCalculationError); + return; + } + UpdatePartitionProgress((start_offset + bytes_read) * 1.0f / partition_size_ * + kVerityProgressPercent); + CHECK(pending_task_id_.PostTask( + FROM_HERE, + base::BindOnce(&FilesystemVerifierAction::WriteVerityAndHashPartition, + base::Unretained(this), + fd, + start_offset + bytes_read, + end_offset, + buffer, + buffer_size))); +} + +void FilesystemVerifierAction::HashPartition(FileDescriptorPtr fd, + const off64_t start_offset, + const off64_t end_offset, + void* buffer, + const size_t buffer_size) { + if (start_offset >= end_offset) { + LOG_IF(WARNING, start_offset > end_offset) + << "start_offset is greater than end_offset : " << start_offset << " > " + << end_offset; + FinishPartitionHashing(); + return; + } + const auto cur_offset = fd->Seek(start_offset, SEEK_SET); + if (cur_offset != start_offset) { + PLOG(ERROR) << "Failed to seek to offset: " << start_offset; + Cleanup(ErrorCode::kFilesystemVerifierError); + return; + } + const auto read_size = + std::min<size_t>(buffer_size, end_offset - start_offset); + const auto bytes_read = fd->Read(buffer, read_size); + if (bytes_read < 0 || static_cast<size_t>(bytes_read) != read_size) { + PLOG(ERROR) << "Failed to read offset " << start_offset << " expected " + << read_size << " bytes, actual: " << bytes_read; + Cleanup(ErrorCode::kFilesystemVerifierError); + return; + } + if (!hasher_->Update(buffer, read_size)) { + LOG(ERROR) << "Hasher updated failed on offset" << start_offset; + Cleanup(ErrorCode::kFilesystemVerifierError); + return; + } + const auto progress = (start_offset + bytes_read) * 1.0f / partition_size_; + UpdatePartitionProgress(progress * (1 - kVerityProgressPercent) + + kVerityProgressPercent); + CHECK(pending_task_id_.PostTask( + FROM_HERE, + base::BindOnce(&FilesystemVerifierAction::HashPartition, + base::Unretained(this), + fd, + start_offset + bytes_read, + end_offset, + buffer, + buffer_size))); +} + void FilesystemVerifierAction::StartPartitionHashing() { if (partition_index_ == install_plan_.partitions.size()) { if (!install_plan_.untouched_dynamic_partitions.empty()) { @@ -201,26 +324,14 @@ void FilesystemVerifierAction::StartPartitionHashing() { } const InstallPlan::Partition& partition = install_plan_.partitions[partition_index_]; - string part_path; - switch (verifier_step_) { - case VerifierStep::kVerifySourceHash: - part_path = partition.source_path; - partition_size_ = partition.source_size; - break; - case VerifierStep::kVerifyTargetHash: - part_path = partition.target_path; - partition_size_ = partition.target_size; - break; - } + const auto& part_path = GetPartitionPath(); + partition_size_ = GetPartitionSize(); LOG(INFO) << "Hashing partition " << partition_index_ << " (" << partition.name << ") on device " << part_path; auto success = false; - if (dynamic_control_->UpdateUsesSnapshotCompression() && - verifier_step_ == VerifierStep::kVerifyTargetHash && - dynamic_control_->IsDynamicPartition(partition.name, - install_plan_.target_slot)) { - success = InitializeFdVABC(); + if (IsVABC(partition)) { + success = InitializeFdVABC(ShouldWriteVerity()); } else { if (part_path.empty()) { if (partition_size_ == 0) { @@ -255,125 +366,61 @@ void FilesystemVerifierAction::StartPartitionHashing() { filesystem_data_end_ = partition.fec_offset; } if (ShouldWriteVerity()) { + LOG(INFO) << "Verity writes enabled on partition " << partition.name; if (!verity_writer_->Init(partition)) { LOG(INFO) << "Verity writes enabled on partition " << partition.name; Cleanup(ErrorCode::kVerityCalculationError); return; } + WriteVerityAndHashPartition( + partition_fd_, 0, filesystem_data_end_, buffer_.data(), buffer_.size()); } else { LOG(INFO) << "Verity writes disabled on partition " << partition.name; + HashPartition( + partition_fd_, 0, partition_size_, buffer_.data(), buffer_.size()); } +} - // Start the first read. - ScheduleFileSystemRead(); +bool FilesystemVerifierAction::IsVABC( + const InstallPlan::Partition& partition) const { + return dynamic_control_->UpdateUsesSnapshotCompression() && + verifier_step_ == VerifierStep::kVerifyTargetHash && + dynamic_control_->IsDynamicPartition(partition.name, + install_plan_.target_slot); } -bool FilesystemVerifierAction::ShouldWriteVerity() { +const std::string& FilesystemVerifierAction::GetPartitionPath() const { const InstallPlan::Partition& partition = install_plan_.partitions[partition_index_]; - return verifier_step_ == VerifierStep::kVerifyTargetHash && - install_plan_.write_verity && - (partition.hash_tree_size > 0 || partition.fec_size > 0); -} - -void FilesystemVerifierAction::ReadVerityAndFooter() { - if (ShouldWriteVerity()) { - if (!verity_writer_->Finalize(partition_fd_, partition_fd_)) { - LOG(ERROR) << "Failed to write hashtree/FEC data."; - Cleanup(ErrorCode::kFilesystemVerifierError); - return; - } - } - // Since we handed our |read_fd_| to verity_writer_ during |Finalize()| - // call, fd's position could have been changed. Re-seek. - partition_fd_->Seek(filesystem_data_end_, SEEK_SET); - auto bytes_to_read = partition_size_ - filesystem_data_end_; - while (bytes_to_read > 0) { - const auto read_size = std::min<size_t>(buffer_.size(), bytes_to_read); - auto bytes_read = partition_fd_->Read(buffer_.data(), read_size); - if (bytes_read <= 0) { - PLOG(ERROR) << "Failed to read hash tree " << bytes_read; - Cleanup(ErrorCode::kFilesystemVerifierError); - return; - } - if (!hasher_->Update(buffer_.data(), bytes_read)) { - LOG(ERROR) << "Unable to update the hash."; - Cleanup(ErrorCode::kError); - return; - } - bytes_to_read -= bytes_read; + switch (verifier_step_) { + case VerifierStep::kVerifySourceHash: + return partition.source_path; + case VerifierStep::kVerifyTargetHash: + if (IsVABC(partition)) { + return partition.readonly_target_path; + } else { + return partition.target_path; + } } - FinishPartitionHashing(); } -void FilesystemVerifierAction::ScheduleFileSystemRead() { - // We can only start reading anything past |hash_tree_offset| after we have - // already read all the data blocks that the hash tree covers. The same - // applies to FEC. - - size_t bytes_to_read = std::min(static_cast<uint64_t>(buffer_.size()), - filesystem_data_end_ - offset_); - if (!bytes_to_read) { - ReadVerityAndFooter(); - return; - } - partition_fd_->Seek(offset_, SEEK_SET); - auto bytes_read = partition_fd_->Read(buffer_.data(), bytes_to_read); - if (bytes_read < 0) { - LOG(ERROR) << "Unable to schedule an asynchronous read from the stream. " - << bytes_read; - Cleanup(ErrorCode::kError); - } else { - // We could just invoke |OnReadDoneCallback()|, it works. But |PostTask| - // is used so that users can cancel updates. - pending_task_id_ = brillo::MessageLoop::current()->PostTask( - base::Bind(&FilesystemVerifierAction::OnReadDone, - base::Unretained(this), - bytes_read)); +size_t FilesystemVerifierAction::GetPartitionSize() const { + const InstallPlan::Partition& partition = + install_plan_.partitions[partition_index_]; + switch (verifier_step_) { + case VerifierStep::kVerifySourceHash: + return partition.source_size; + case VerifierStep::kVerifyTargetHash: + return partition.target_size; } } -void FilesystemVerifierAction::OnReadDone(size_t bytes_read) { - if (cancelled_) { - Cleanup(ErrorCode::kError); - return; - } - if (bytes_read == 0) { - LOG(ERROR) << "Failed to read the remaining " << partition_size_ - offset_ - << " bytes from partition " - << install_plan_.partitions[partition_index_].name; - Cleanup(ErrorCode::kFilesystemVerifierError); - return; - } - - if (!hasher_->Update(buffer_.data(), bytes_read)) { - LOG(ERROR) << "Unable to update the hash."; - Cleanup(ErrorCode::kError); - return; - } - - // WE don't consider sizes of each partition. Every partition - // has the same length on progress bar. - // TODO(zhangkelvin) Take sizes of each partition into account - - UpdateProgress( - (static_cast<double>(offset_) / partition_size_ + partition_index_) / - install_plan_.partitions.size()); - if (ShouldWriteVerity()) { - if (!verity_writer_->Update(offset_, buffer_.data(), bytes_read)) { - LOG(ERROR) << "Unable to update verity"; - Cleanup(ErrorCode::kVerityCalculationError); - return; - } - } - - offset_ += bytes_read; - if (offset_ == filesystem_data_end_) { - ReadVerityAndFooter(); - return; - } - - ScheduleFileSystemRead(); +bool FilesystemVerifierAction::ShouldWriteVerity() { + const InstallPlan::Partition& partition = + install_plan_.partitions[partition_index_]; + return verifier_step_ == VerifierStep::kVerifyTargetHash && + install_plan_.write_verity && + (partition.hash_tree_size > 0 || partition.fec_size > 0); } void FilesystemVerifierAction::FinishPartitionHashing() { @@ -447,6 +494,7 @@ void FilesystemVerifierAction::FinishPartitionHashing() { hasher_.reset(); buffer_.clear(); if (partition_fd_) { + partition_fd_->Close(); partition_fd_.reset(); } StartPartitionHashing(); diff --git a/payload_consumer/filesystem_verifier_action.h b/payload_consumer/filesystem_verifier_action.h index 78634cb3..850abdad 100644 --- a/payload_consumer/filesystem_verifier_action.h +++ b/payload_consumer/filesystem_verifier_action.h @@ -22,12 +22,14 @@ #include <memory> #include <string> +#include <utility> #include <vector> #include <brillo/message_loops/message_loop.h> #include "update_engine/common/action.h" #include "update_engine/common/hash_calculator.h" +#include "update_engine/common/scoped_task_id.h" #include "update_engine/payload_consumer/file_descriptor.h" #include "update_engine/payload_consumer/install_plan.h" #include "update_engine/payload_consumer/verity_writer_interface.h" @@ -84,6 +86,16 @@ class FilesystemVerifierAction : public InstallPlanAction { private: friend class FilesystemVerifierActionTestDelegate; + void WriteVerityAndHashPartition(FileDescriptorPtr fd, + const off64_t start_offset, + const off64_t end_offset, + void* buffer, + const size_t buffer_size); + void HashPartition(FileDescriptorPtr fd, + const off64_t start_offset, + const off64_t end_offset, + void* buffer, + const size_t buffer_size); // Return true if we need to write verity bytes. bool ShouldWriteVerity(); @@ -91,16 +103,11 @@ class FilesystemVerifierAction : public InstallPlanAction { // remaining to be hashed, it finishes the action. void StartPartitionHashing(); - // Schedules the asynchronous read of the filesystem part of this - // partition(not including hashtree/verity). - void ScheduleFileSystemRead(); + const std::string& GetPartitionPath() const; - // Read the verity part of this partition.(hash tree and FEC) - void ReadVerityAndFooter(); + bool IsVABC(const InstallPlan::Partition& partition) const; - // Called from the main loop when a single read from |src_stream_| succeeds or - // fails, calling OnReadDoneCallback() and OnReadErrorCallback() respectively. - void OnReadDone(size_t bytes_read); + size_t GetPartitionSize() const; // When the read is done, finalize the hash checking of the current partition // and continue checking the next one. @@ -114,9 +121,13 @@ class FilesystemVerifierAction : public InstallPlanAction { // Invoke delegate callback to report progress, if delegate is not null void UpdateProgress(double progress); + // Updates progress of current partition. |progress| should be in range [0, + // 1], and it will be scaled appropriately with # of partitions. + void UpdatePartitionProgress(double progress); + // Initialize read_fd_ and write_fd_ bool InitializeFd(const std::string& part_path); - bool InitializeFdVABC(); + bool InitializeFdVABC(bool should_write_verity); // The type of the partition that we are verifying. VerifierStep verifier_step_ = VerifierStep::kVerifyTargetHash; @@ -161,8 +172,7 @@ class FilesystemVerifierAction : public InstallPlanAction { // Callback that should be cancelled on |TerminateProcessing|. Usually this // points to pending read callbacks from async stream. - brillo::MessageLoop::TaskId pending_task_id_{ - brillo::MessageLoop::kTaskIdNull}; + ScopedTaskId pending_task_id_; DISALLOW_COPY_AND_ASSIGN(FilesystemVerifierAction); }; diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc index 586662d9..f2f29547 100644 --- a/payload_consumer/filesystem_verifier_action_unittest.cc +++ b/payload_consumer/filesystem_verifier_action_unittest.cc @@ -16,6 +16,8 @@ #include "update_engine/payload_consumer/filesystem_verifier_action.h" +#include <algorithm> +#include <cstring> #include <memory> #include <string> #include <utility> @@ -25,8 +27,10 @@ #include <brillo/message_loops/fake_message_loop.h> #include <brillo/message_loops/message_loop_utils.h> #include <brillo/secure_blob.h> +#include <fec/ecc.h> #include <gtest/gtest.h> #include <libsnapshot/snapshot_writer.h> +#include <sys/stat.h> #include "update_engine/common/dynamic_partition_control_stub.h" #include "update_engine/common/hash_calculator.h" @@ -35,6 +39,7 @@ #include "update_engine/common/utils.h" #include "update_engine/payload_consumer/fake_file_descriptor.h" #include "update_engine/payload_consumer/install_plan.h" +#include "update_engine/payload_consumer/verity_writer_android.h" using brillo::MessageLoop; using std::string; @@ -50,19 +55,46 @@ namespace chromeos_update_engine { class FilesystemVerifierActionTest : public ::testing::Test { public: static constexpr size_t BLOCK_SIZE = 4096; + // We use SHA256 for testing, so hash size is 256bits / 8 + static constexpr size_t HASH_SIZE = 256 / 8; static constexpr size_t PARTITION_SIZE = BLOCK_SIZE * 1024; + static constexpr size_t HASH_TREE_START_OFFSET = 800 * BLOCK_SIZE; + size_t hash_tree_size = 0; + size_t fec_start_offset = 0; + size_t fec_data_size = 0; + static constexpr size_t FEC_ROOTS = 2; + size_t fec_rounds = 0; + size_t fec_size = 0; protected: void SetUp() override { + hash_tree_size = HashTreeBuilder::CalculateSize( + HASH_TREE_START_OFFSET, BLOCK_SIZE, HASH_SIZE); + fec_start_offset = HASH_TREE_START_OFFSET + hash_tree_size; + fec_data_size = fec_start_offset; + static constexpr size_t FEC_ROOTS = 2; + fec_rounds = + utils::DivRoundUp(fec_data_size / BLOCK_SIZE, FEC_RSM - FEC_ROOTS); + fec_size = fec_rounds * FEC_ROOTS * BLOCK_SIZE; + + fec_data_.resize(fec_size); + hash_tree_data_.resize(hash_tree_size); + // Globally readable writable, as we want to write data + ASSERT_EQ(0, fchmod(source_part_.fd(), 0666)) + << " Failed to set " << source_part_.path() << " as writable " + << strerror(errno); + ASSERT_EQ(0, fchmod(target_part_.fd(), 0666)) + << " Failed to set " << target_part_.path() << " as writable " + << strerror(errno); brillo::Blob part_data(PARTITION_SIZE); test_utils::FillWithData(&part_data); ASSERT_TRUE(utils::WriteFile( - source_part.path().c_str(), part_data.data(), part_data.size())); - // FillWithData() will will with different data next call. We want + source_part_.path().c_str(), part_data.data(), part_data.size())); + // FillWithData() will fill with different data next call. We want // source/target partitions to contain different data for testing. test_utils::FillWithData(&part_data); ASSERT_TRUE(utils::WriteFile( - target_part.path().c_str(), part_data.data(), part_data.size())); + target_part_.path().c_str(), part_data.data(), part_data.size())); loop_.SetAsCurrent(); } @@ -70,6 +102,8 @@ class FilesystemVerifierActionTest : public ::testing::Test { EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1)); } + void DoTestVABC(bool clear_target_hash, bool enable_verity); + // Returns true iff test has completed successfully. bool DoTest(bool terminate_early, bool hash_fail); @@ -81,29 +115,105 @@ class FilesystemVerifierActionTest : public ::testing::Test { std::string name = "fake_part") { InstallPlan::Partition& part = install_plan->partitions.emplace_back(); part.name = name; - part.target_path = target_part.path(); + part.target_path = target_part_.path(); part.readonly_target_path = part.target_path; part.target_size = PARTITION_SIZE; part.block_size = BLOCK_SIZE; - part.source_path = source_part.path(); + part.source_path = source_part_.path(); + part.source_size = PARTITION_SIZE; EXPECT_TRUE( - HashCalculator::RawHashOfFile(source_part.path(), &part.source_hash)); + HashCalculator::RawHashOfFile(source_part_.path(), &part.source_hash)); EXPECT_TRUE( - HashCalculator::RawHashOfFile(target_part.path(), &part.target_hash)); + HashCalculator::RawHashOfFile(target_part_.path(), &part.target_hash)); return ∂ } + static void ZeroRange(FileDescriptorPtr fd, + size_t start_block, + size_t num_blocks) { + std::vector<unsigned char> buffer(BLOCK_SIZE); + ASSERT_EQ((ssize_t)(start_block * BLOCK_SIZE), + fd->Seek(start_block * BLOCK_SIZE, SEEK_SET)); + for (size_t i = 0; i < num_blocks; i++) { + ASSERT_TRUE(utils::WriteAll(fd, buffer.data(), buffer.size())); + } + } + + void SetHashWithVerity(InstallPlan::Partition* partition) { + partition->hash_tree_algorithm = "sha256"; + partition->hash_tree_size = hash_tree_size; + partition->hash_tree_offset = HASH_TREE_START_OFFSET; + partition->hash_tree_data_offset = 0; + partition->hash_tree_data_size = HASH_TREE_START_OFFSET; + partition->fec_size = fec_size; + partition->fec_offset = fec_start_offset; + partition->fec_data_offset = 0; + partition->fec_data_size = fec_data_size; + partition->fec_roots = FEC_ROOTS; + VerityWriterAndroid verity_writer; + ASSERT_TRUE(verity_writer.Init(*partition)); + LOG(INFO) << "Opening " << partition->readonly_target_path; + auto fd = std::make_shared<EintrSafeFileDescriptor>(); + ASSERT_TRUE(fd->Open(partition->readonly_target_path.c_str(), O_RDWR)) + << "Failed to open " << partition->target_path.c_str() << " " + << strerror(errno); + std::vector<unsigned char> buffer(BLOCK_SIZE); + // Only need to read up to hash tree + auto bytes_to_read = HASH_TREE_START_OFFSET; + auto offset = 0; + while (bytes_to_read > 0) { + const auto bytes_read = fd->Read( + buffer.data(), std::min<size_t>(buffer.size(), bytes_to_read)); + ASSERT_GT(bytes_read, 0) + << "offset: " << offset << " bytes to read: " << bytes_to_read + << " error: " << strerror(errno); + ASSERT_TRUE(verity_writer.Update(offset, buffer.data(), bytes_read)); + bytes_to_read -= bytes_read; + offset += bytes_read; + } + ASSERT_TRUE(verity_writer.Finalize(fd, fd)); + ASSERT_TRUE(fd->IsOpen()); + ASSERT_TRUE(HashCalculator::RawHashOfFile(target_part_.path(), + &partition->target_hash)); + + ASSERT_TRUE(fd->Seek(HASH_TREE_START_OFFSET, SEEK_SET)); + ASSERT_EQ(fd->Read(hash_tree_data_.data(), hash_tree_data_.size()), + static_cast<ssize_t>(hash_tree_data_.size())) + << "Failed to read hashtree " << strerror(errno); + ASSERT_TRUE(fd->Seek(fec_start_offset, SEEK_SET)); + ASSERT_EQ(fd->Read(fec_data_.data(), fec_data_.size()), + static_cast<ssize_t>(fec_data_.size())) + << "Failed to read FEC " << strerror(errno); + // Fs verification action is expected to write them, so clear verity data to + // ensure that they are re-created correctly. + ZeroRange( + fd, HASH_TREE_START_OFFSET / BLOCK_SIZE, hash_tree_size / BLOCK_SIZE); + ZeroRange(fd, fec_start_offset / BLOCK_SIZE, fec_size / BLOCK_SIZE); + } brillo::FakeMessageLoop loop_{nullptr}; ActionProcessor processor_; DynamicPartitionControlStub dynamic_control_stub_; - static ScopedTempFile source_part; - static ScopedTempFile target_part; + std::vector<unsigned char> fec_data_; + std::vector<unsigned char> hash_tree_data_; + static ScopedTempFile source_part_; + static ScopedTempFile target_part_; + InstallPlan install_plan_; }; -ScopedTempFile FilesystemVerifierActionTest::source_part{ - "source_part.XXXXXX", false, PARTITION_SIZE}; -ScopedTempFile FilesystemVerifierActionTest::target_part{ - "target_part.XXXXXX", false, PARTITION_SIZE}; +ScopedTempFile FilesystemVerifierActionTest::source_part_{ + "source_part.XXXXXX", true, PARTITION_SIZE}; +ScopedTempFile FilesystemVerifierActionTest::target_part_{ + "target_part.XXXXXX", true, PARTITION_SIZE}; + +static void EnableVABC(MockDynamicPartitionControl* dynamic_control, + const std::string& part_name) { + ON_CALL(*dynamic_control, GetDynamicPartitionsFeatureFlag()) + .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH))); + ON_CALL(*dynamic_control, UpdateUsesSnapshotCompression()) + .WillByDefault(Return(true)); + ON_CALL(*dynamic_control, IsDynamicPartition(part_name, _)) + .WillByDefault(Return(true)); +} class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate { public: @@ -170,9 +280,8 @@ bool FilesystemVerifierActionTest::DoTest(bool terminate_early, bool success = true; // Set up the action objects - InstallPlan install_plan; - install_plan.source_slot = 0; - install_plan.target_slot = 1; + install_plan_.source_slot = 0; + install_plan_.target_slot = 1; InstallPlan::Partition part; part.name = "part"; part.target_size = kLoopFileSize - (hash_fail ? 1 : 0); @@ -187,23 +296,19 @@ bool FilesystemVerifierActionTest::DoTest(bool terminate_early, ADD_FAILURE(); success = false; } - install_plan.partitions = {part}; + install_plan_.partitions = {part}; - BuildActions(install_plan); + BuildActions(install_plan_); FilesystemVerifierActionTestDelegate delegate; processor_.set_delegate(&delegate); - loop_.PostTask(FROM_HERE, - base::Bind( - [](ActionProcessor* processor, bool terminate_early) { - processor->StartProcessing(); - if (terminate_early) { - processor->StopProcessing(); - } - }, - base::Unretained(&processor_), - terminate_early)); + loop_.PostTask(base::Bind(&ActionProcessor::StartProcessing, + base::Unretained(&processor_))); + if (terminate_early) { + loop_.PostTask(base::Bind(&ActionProcessor::StopProcessing, + base::Unretained(&processor_))); + } loop_.Run(); if (!terminate_early) { @@ -232,7 +337,7 @@ bool FilesystemVerifierActionTest::DoTest(bool terminate_early, EXPECT_TRUE(is_a_file_reading_eq); success = success && is_a_file_reading_eq; - bool is_install_plan_eq = (*delegate.install_plan_ == install_plan); + bool is_install_plan_eq = (*delegate.install_plan_ == install_plan_); EXPECT_TRUE(is_install_plan_eq); success = success && is_install_plan_eq; return success; @@ -297,14 +402,13 @@ TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) { } TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) { - InstallPlan install_plan; InstallPlan::Partition part; part.name = "nope"; part.source_path = "/no/such/file"; part.target_path = "/no/such/file"; - install_plan.partitions = {part}; + install_plan_.partitions = {part}; - BuildActions(install_plan); + BuildActions(install_plan_); FilesystemVerifierActionTest2Delegate delegate; processor_.set_delegate(&delegate); @@ -345,7 +449,6 @@ TEST_F(FilesystemVerifierActionTest, RunAsRootWriteVerityTest) { test_utils::ScopedLoopbackDeviceBinder target_device( part_file.path(), true, &target_path); - InstallPlan install_plan; InstallPlan::Partition part; part.name = "part"; part.target_path = target_path; @@ -376,9 +479,9 @@ TEST_F(FilesystemVerifierActionTest, RunAsRootWriteVerityTest) { part.hash_tree_salt = {0x9e, 0xcb, 0xf8, 0xd5, 0x0b, 0xb4, 0x43, 0x0a, 0x7a, 0x10, 0xad, 0x96, 0xd7, 0x15, 0x70, 0xba, 0xed, 0x27, 0xe2, 0xae}; - install_plan.partitions = {part}; + install_plan_.partitions = {part}; - BuildActions(install_plan); + BuildActions(install_plan_); FilesystemVerifierActionTestDelegate delegate; processor_.set_delegate(&delegate); @@ -407,8 +510,7 @@ TEST_F(FilesystemVerifierActionTest, RunAsRootSkipWriteVerityTest) { test_utils::ScopedLoopbackDeviceBinder target_device( part_file.path(), true, &target_path); - InstallPlan install_plan; - install_plan.write_verity = false; + install_plan_.write_verity = false; InstallPlan::Partition part; part.name = "part"; part.target_path = target_path; @@ -423,9 +525,9 @@ TEST_F(FilesystemVerifierActionTest, RunAsRootSkipWriteVerityTest) { part.fec_offset = part.fec_data_size; part.fec_size = 2 * 4096; EXPECT_TRUE(HashCalculator::RawHashOfData(part_data, &part.target_hash)); - install_plan.partitions = {part}; + install_plan_.partitions = {part}; - BuildActions(install_plan); + BuildActions(install_plan_); FilesystemVerifierActionTestDelegate delegate; processor_.set_delegate(&delegate); @@ -442,76 +544,146 @@ TEST_F(FilesystemVerifierActionTest, RunAsRootSkipWriteVerityTest) { ASSERT_EQ(ErrorCode::kSuccess, delegate.code()); } -TEST_F(FilesystemVerifierActionTest, RunWithVABCNoVerity) { - InstallPlan install_plan; - auto part_ptr = AddFakePartition(&install_plan); +void FilesystemVerifierActionTest::DoTestVABC(bool clear_target_hash, + bool enable_verity) { + auto part_ptr = AddFakePartition(&install_plan_); + if (::testing::Test::HasFailure()) { + return; + } ASSERT_NE(part_ptr, nullptr); InstallPlan::Partition& part = *part_ptr; part.target_path = "Shouldn't attempt to open this path"; + if (enable_verity) { + install_plan_.write_verity = true; + ASSERT_NO_FATAL_FAILURE(SetHashWithVerity(&part)); + } + if (clear_target_hash) { + part.target_hash.clear(); + } NiceMock<MockDynamicPartitionControl> dynamic_control; - ON_CALL(dynamic_control, GetDynamicPartitionsFeatureFlag()) - .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH))); - ON_CALL(dynamic_control, UpdateUsesSnapshotCompression()) - .WillByDefault(Return(true)); - ON_CALL(dynamic_control, IsDynamicPartition(part.name, _)) - .WillByDefault(Return(true)); + EnableVABC(&dynamic_control, part.name); + auto open_cow = [part]() { + auto cow_fd = std::make_shared<EintrSafeFileDescriptor>(); + EXPECT_TRUE(cow_fd->Open(part.readonly_target_path.c_str(), O_RDWR)) + << "Failed to open part " << part.readonly_target_path + << strerror(errno); + return cow_fd; + }; EXPECT_CALL(dynamic_control, UpdateUsesSnapshotCompression()) .Times(AtLeast(1)); - // Since we are not writing verity, we should not attempt to OpenCowFd() - // reads should go through regular file descriptors on mapped partitions. - EXPECT_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _)) - .Times(0); - EXPECT_CALL(dynamic_control, MapAllPartitions()).Times(AtLeast(1)); + auto cow_fd = open_cow(); + if (HasFailure()) { + return; + } + + if (enable_verity) { + ON_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _)) + .WillByDefault(open_cow); + EXPECT_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _)) + .Times(AtLeast(1)); + + // fs verification isn't supposed to write to |readonly_target_path|. All + // writes should go through fd returned by |OpenCowFd|. Therefore we set + // target part as read-only to make sure. + ASSERT_EQ(0, chmod(part.readonly_target_path.c_str(), 0444)) + << " Failed to set " << part.readonly_target_path << " as read-only " + << strerror(errno); + } else { + // Since we are not writing verity, we should not attempt to OpenCowFd() + // reads should go through regular file descriptors on mapped partitions. + EXPECT_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _)) + .Times(0); + EXPECT_CALL(dynamic_control, MapAllPartitions()).Times(AtLeast(1)); + } EXPECT_CALL(dynamic_control, ListDynamicPartitionsForSlot(_, _, _)) .WillRepeatedly( DoAll(SetArgPointee<2, std::vector<std::string>>({part.name}), Return(true))); - BuildActions(install_plan, &dynamic_control); + BuildActions(install_plan_, &dynamic_control); FilesystemVerifierActionTestDelegate delegate; processor_.set_delegate(&delegate); - loop_.PostTask( - FROM_HERE, - base::Bind( - [](ActionProcessor* processor) { processor->StartProcessing(); }, - base::Unretained(&processor_))); + loop_.PostTask(FROM_HERE, + base::Bind(&ActionProcessor::StartProcessing, + base::Unretained(&processor_))); loop_.Run(); ASSERT_FALSE(processor_.IsRunning()); ASSERT_TRUE(delegate.ran()); - ASSERT_EQ(ErrorCode::kSuccess, delegate.code()); + if (enable_verity) { + std::vector<unsigned char> actual_fec(fec_size); + ssize_t bytes_read = 0; + ASSERT_TRUE(utils::PReadAll(cow_fd, + actual_fec.data(), + actual_fec.size(), + fec_start_offset, + &bytes_read)); + ASSERT_EQ(actual_fec, fec_data_); + std::vector<unsigned char> actual_hash_tree(hash_tree_size); + ASSERT_TRUE(utils::PReadAll(cow_fd, + actual_hash_tree.data(), + actual_hash_tree.size(), + HASH_TREE_START_OFFSET, + &bytes_read)); + ASSERT_EQ(actual_hash_tree, hash_tree_data_); + } + if (clear_target_hash) { + ASSERT_EQ(ErrorCode::kNewRootfsVerificationError, delegate.code()); + } else { + ASSERT_EQ(ErrorCode::kSuccess, delegate.code()); + } +} + +TEST_F(FilesystemVerifierActionTest, VABC_NoVerity_Success) { + DoTestVABC(false, false); +} + +TEST_F(FilesystemVerifierActionTest, VABC_NoVerity_Target_Mismatch) { + DoTestVABC(true, false); +} + +TEST_F(FilesystemVerifierActionTest, VABC_Verity_Success) { + DoTestVABC(false, true); +} + +TEST_F(FilesystemVerifierActionTest, VABC_Verity_ReadAfterWrite) { + ASSERT_NO_FATAL_FAILURE(DoTestVABC(false, true)); + // Run FS verification again, w/o writing verity. We have seen a bug where + // attempting to run fs again will cause previously written verity data to be + // dropped, so cover this scenario. + ASSERT_GE(install_plan_.partitions.size(), 1UL); + auto& part = install_plan_.partitions[0]; + install_plan_.write_verity = false; + part.readonly_target_path = target_part_.path(); + NiceMock<MockDynamicPartitionControl> dynamic_control; + EnableVABC(&dynamic_control, part.name); + + // b/186196758 is only visible if we repeatedely run FS verification w/o + // writing verity + for (int i = 0; i < 3; i++) { + BuildActions(install_plan_, &dynamic_control); + + FilesystemVerifierActionTestDelegate delegate; + processor_.set_delegate(&delegate); + loop_.PostTask( + FROM_HERE, + base::Bind( + [](ActionProcessor* processor) { processor->StartProcessing(); }, + base::Unretained(&processor_))); + loop_.Run(); + ASSERT_FALSE(processor_.IsRunning()); + ASSERT_TRUE(delegate.ran()); + ASSERT_EQ(ErrorCode::kSuccess, delegate.code()); + } } -TEST_F(FilesystemVerifierActionTest, ReadAfterWrite) { - ScopedTempFile cow_device_file("cow_device.XXXXXX", true); - android::snapshot::CompressedSnapshotWriter snapshot_writer{ - {.block_size = BLOCK_SIZE}}; - snapshot_writer.SetCowDevice(android::base::unique_fd{cow_device_file.fd()}); - snapshot_writer.Initialize(); - std::vector<unsigned char> buffer; - buffer.resize(BLOCK_SIZE); - std::fill(buffer.begin(), buffer.end(), 123); - - ASSERT_TRUE(snapshot_writer.AddRawBlocks(0, buffer.data(), buffer.size())); - ASSERT_TRUE(snapshot_writer.Finalize()); - auto cow_reader = snapshot_writer.OpenReader(); - ASSERT_NE(cow_reader, nullptr); - ASSERT_TRUE(snapshot_writer.AddRawBlocks(1, buffer.data(), buffer.size())); - ASSERT_TRUE(snapshot_writer.AddRawBlocks(2, buffer.data(), buffer.size())); - ASSERT_TRUE(snapshot_writer.Finalize()); - cow_reader = snapshot_writer.OpenReader(); - ASSERT_NE(cow_reader, nullptr); - std::vector<unsigned char> read_back; - read_back.resize(buffer.size()); - cow_reader->Seek(BLOCK_SIZE, SEEK_SET); - const auto bytes_read = cow_reader->Read(read_back.data(), read_back.size()); - ASSERT_EQ((size_t)(bytes_read), BLOCK_SIZE); - ASSERT_EQ(read_back, buffer); +TEST_F(FilesystemVerifierActionTest, VABC_Verity_Target_Mismatch) { + DoTestVABC(true, true); } } // namespace chromeos_update_engine diff --git a/stable/Android.bp b/stable/Android.bp index 5e54e9a0..1573ebd2 100644 --- a/stable/Android.bp +++ b/stable/Android.bp @@ -49,6 +49,7 @@ aidl_interface { ], }, }, + versions: ["1"], } // update_engine_stable_client (type: executable) diff --git a/stable/aidl_api/libupdate_engine_stable/1/.hash b/stable/aidl_api/libupdate_engine_stable/1/.hash new file mode 100644 index 00000000..f21562a4 --- /dev/null +++ b/stable/aidl_api/libupdate_engine_stable/1/.hash @@ -0,0 +1 @@ +526043ea6cb098d53a9c3e778420e64c4e864d8c diff --git a/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStable.aidl b/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStable.aidl new file mode 100644 index 00000000..67db18e9 --- /dev/null +++ b/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStable.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.os; +interface IUpdateEngineStable { + void applyPayloadFd(in ParcelFileDescriptor pfd, in long payload_offset, in long payload_size, in String[] headerKeyValuePairs); + boolean bind(android.os.IUpdateEngineStableCallback callback); + boolean unbind(android.os.IUpdateEngineStableCallback callback); +} diff --git a/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStableCallback.aidl b/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStableCallback.aidl new file mode 100644 index 00000000..dbca127c --- /dev/null +++ b/stable/aidl_api/libupdate_engine_stable/1/android/os/IUpdateEngineStableCallback.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.os; +interface IUpdateEngineStableCallback { + oneway void onStatusUpdate(int status_code, float percentage); + oneway void onPayloadApplicationComplete(int error_code); +} |