Merge "Allow /postinstall files to have custom contexts" into sc-dev
diff --git a/Android.bp b/Android.bp
index bc178bc..a4b7978 100644
--- a/Android.bp
+++ b/Android.bp
@@ -224,6 +224,7 @@
"payload_consumer/bzip_extent_writer.cc",
"payload_consumer/cached_file_descriptor.cc",
"payload_consumer/certificate_parser_android.cc",
+ "payload_consumer/cow_writer_file_descriptor.cc",
"payload_consumer/delta_performer.cc",
"payload_consumer/extent_reader.cc",
"payload_consumer/extent_writer.cc",
diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
index 4220445..444fe42 100644
--- a/aosp/dynamic_partition_control_android.cc
+++ b/aosp/dynamic_partition_control_android.cc
@@ -49,6 +49,7 @@
#include "update_engine/common/dynamic_partition_control_interface.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/cow_writer_file_descriptor.h"
#include "update_engine/payload_consumer/delta_performer.h"
using android::base::GetBoolProperty;
@@ -283,6 +284,7 @@
}
bool DynamicPartitionControlAndroid::UnmapAllPartitions() {
+ snapshot_->UnmapAllSnapshots();
if (mapped_devices_.empty()) {
return false;
}
@@ -803,9 +805,7 @@
}
std::string device_dir_str;
- if (!GetDeviceDir(&device_dir_str)) {
- return false;
- }
+ TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
base::FilePath device_dir(device_dir_str);
auto source_device =
device_dir.Append(GetSuperPartitionName(source_slot)).value();
@@ -822,21 +822,68 @@
DeleteSourcePartitions(builder.get(), source_slot, manifest));
}
- if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
- return false;
- }
+ TEST_AND_RETURN_FALSE(
+ UpdatePartitionMetadata(builder.get(), target_slot, manifest));
auto target_device =
device_dir.Append(GetSuperPartitionName(target_slot)).value();
return StoreMetadata(target_device, builder.get(), target_slot);
}
+bool DynamicPartitionControlAndroid::CheckSuperPartitionAllocatableSpace(
+ android::fs_mgr::MetadataBuilder* builder,
+ const DeltaArchiveManifest& manifest,
+ bool use_snapshot) {
+ uint64_t total_size = 0;
+ for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
+ total_size += group.size();
+ }
+
+ std::string expr;
+ uint64_t allocatable_space = builder->AllocatableSpace();
+ // On device retrofitting dynamic partitions, allocatable_space = super.
+ // On device launching dynamic partitions w/o VAB,
+ // allocatable_space = super / 2.
+ // On device launching dynamic partitions with VAB, allocatable_space = super.
+ // For recovery sideload, allocatable_space = super.
+ if (!GetDynamicPartitionsFeatureFlag().IsRetrofit() && !use_snapshot &&
+ !IsRecovery()) {
+ allocatable_space /= 2;
+ expr = "half of ";
+ }
+ if (total_size > allocatable_space) {
+ LOG(ERROR) << "The maximum size of all groups for the target slot"
+ << " (" << total_size << ") has exceeded " << expr
+ << "allocatable space for dynamic partitions "
+ << allocatable_space << ".";
+ return false;
+ }
+
+ return true;
+}
+
bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
uint32_t source_slot,
uint32_t target_slot,
const DeltaArchiveManifest& manifest,
uint64_t* required_size) {
TEST_AND_RETURN_FALSE(ExpectMetadataMounted());
+
+ std::string device_dir_str;
+ TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+ base::FilePath device_dir(device_dir_str);
+ auto super_device =
+ device_dir.Append(GetSuperPartitionName(source_slot)).value();
+ auto builder = LoadMetadataBuilder(super_device, source_slot);
+ if (builder == nullptr) {
+ LOG(ERROR) << "No metadata at "
+ << BootControlInterface::SlotName(source_slot);
+ return false;
+ }
+
+ TEST_AND_RETURN_FALSE(
+ CheckSuperPartitionAllocatableSpace(builder.get(), manifest, true));
+
if (!snapshot_->BeginUpdate()) {
LOG(ERROR) << "Cannot begin new update.";
return false;
@@ -875,29 +922,8 @@
const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
DeleteGroupsWithSuffix(builder, target_suffix);
- uint64_t total_size = 0;
- for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
- total_size += group.size();
- }
-
- std::string expr;
- uint64_t allocatable_space = builder->AllocatableSpace();
- // On device retrofitting dynamic partitions, allocatable_space = super.
- // On device launching dynamic partitions w/o VAB,
- // allocatable_space = super / 2.
- // On device launching dynamic partitions with VAB, allocatable_space = super.
- if (!GetDynamicPartitionsFeatureFlag().IsRetrofit() &&
- !GetVirtualAbFeatureFlag().IsEnabled()) {
- allocatable_space /= 2;
- expr = "half of ";
- }
- if (total_size > allocatable_space) {
- LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
- << " (" << total_size << ") has exceeded " << expr
- << "allocatable space for dynamic partitions "
- << allocatable_space << ".";
- return false;
- }
+ TEST_AND_RETURN_FALSE(
+ CheckSuperPartitionAllocatableSpace(builder, manifest, false));
// name of partition(e.g. "system") -> size in bytes
std::map<std::string, uint64_t> partition_sizes;
@@ -1165,7 +1191,7 @@
LOG(INFO) << "Will overwrite existing partitions. Slot "
<< BootControlInterface::SlotName(source_slot)
- << "may be unbootable until update finishes!";
+ << " may be unbootable until update finishes!";
const std::string source_suffix = SlotSuffixForSlotNumber(source_slot);
DeleteGroupsWithSuffix(builder, source_suffix);
@@ -1328,7 +1354,7 @@
return snapshot_->OpenSnapshotWriter(params, std::move(source_path));
} // namespace chromeos_update_engine
-FileDescriptorPtr DynamicPartitionControlAndroid::OpenCowReader(
+FileDescriptorPtr DynamicPartitionControlAndroid::OpenCowFd(
const std::string& unsuffixed_partition_name,
const std::optional<std::string>& source_path,
bool is_append) {
@@ -1337,8 +1363,10 @@
if (cow_writer == nullptr) {
return nullptr;
}
- cow_writer->InitializeAppend(kEndOfInstallLabel);
- return cow_writer->OpenReader();
+ if (!cow_writer->InitializeAppend(kEndOfInstallLabel)) {
+ return nullptr;
+ }
+ return std::make_shared<CowWriterFileDescriptor>(std::move(cow_writer));
}
std::optional<base::FilePath> DynamicPartitionControlAndroid::GetSuperDevice() {
diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
index 4e75a9b..b7aa7ea 100644
--- a/aosp/dynamic_partition_control_android.h
+++ b/aosp/dynamic_partition_control_android.h
@@ -104,9 +104,9 @@
const std::string& unsuffixed_partition_name,
const std::optional<std::string>& source_path,
bool is_append) override;
- FileDescriptorPtr OpenCowReader(const std::string& unsuffixed_partition_name,
- const std::optional<std::string>&,
- bool is_append = false) override;
+ FileDescriptorPtr OpenCowFd(const std::string& unsuffixed_partition_name,
+ const std::optional<std::string>&,
+ bool is_append = false) override;
bool UnmapAllPartitions() override;
@@ -258,6 +258,13 @@
const DeltaArchiveManifest& manifest,
uint64_t* required_size);
+ // Returns true if the allocatable space in super partition is larger than
+ // the size of dynamic partition groups in the manifest.
+ bool CheckSuperPartitionAllocatableSpace(
+ android::fs_mgr::MetadataBuilder* builder,
+ const DeltaArchiveManifest& manifest,
+ bool use_snapshot);
+
enum class DynamicPartitionDeviceStatus {
SUCCESS,
ERROR,
diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc
index eb3f60c..0bb8df7 100644
--- a/aosp/dynamic_partition_control_android_unittest.cc
+++ b/aosp/dynamic_partition_control_android_unittest.cc
@@ -1051,6 +1051,7 @@
// Test happy path of PreparePartitionsForUpdate on a Virtual A/B device.
TEST_P(SnapshotPartitionTestP, PreparePartitions) {
ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
+ SetMetadata(source(), {});
uint64_t required_size = 0;
EXPECT_TRUE(PreparePartitionsForUpdate(&required_size));
EXPECT_EQ(0u, required_size);
@@ -1061,6 +1062,8 @@
TEST_P(SnapshotPartitionTestP, PreparePartitionsNoSpace) {
ExpectCreateUpdateSnapshots(android::snapshot::Return::NoSpace(1_GiB));
uint64_t required_size = 0;
+
+ SetMetadata(source(), {});
EXPECT_FALSE(PreparePartitionsForUpdate(&required_size));
EXPECT_EQ(1_GiB, required_size);
}
@@ -1070,6 +1073,10 @@
TEST_P(SnapshotPartitionTestP, RecoveryUseSuperEmpty) {
ExpectCreateUpdateSnapshots(android::snapshot::Return::Ok());
EXPECT_CALL(dynamicControl(), IsRecovery()).WillRepeatedly(Return(true));
+
+ // Metadata is needed to perform super partition size check.
+ SetMetadata(source(), {});
+
// Must not call PrepareDynamicPartitionsForUpdate if
// PrepareSnapshotPartitionsForUpdate succeeds.
EXPECT_CALL(dynamicControl(), PrepareDynamicPartitionsForUpdate(_, _, _, _))
diff --git a/aosp/mock_dynamic_partition_control_android.h b/aosp/mock_dynamic_partition_control_android.h
index d80dfb5..428b6c7 100644
--- a/aosp/mock_dynamic_partition_control_android.h
+++ b/aosp/mock_dynamic_partition_control_android.h
@@ -95,7 +95,7 @@
bool is_append),
(override));
MOCK_METHOD(FileDescriptorPtr,
- OpenCowReader,
+ OpenCowFd,
(const std::string& unsuffixed_partition_name,
const std::optional<std::string>& source_path,
bool is_append),
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
index da27932..d5e1d8d 100644
--- a/common/dynamic_partition_control_interface.h
+++ b/common/dynamic_partition_control_interface.h
@@ -165,7 +165,9 @@
const std::string& unsuffixed_partition_name,
const std::optional<std::string>&,
bool is_append = false) = 0;
- virtual FileDescriptorPtr OpenCowReader(
+ // Open a general purpose FD capable to reading and writing to COW. Note that
+ // writes must be block aligned.
+ virtual FileDescriptorPtr OpenCowFd(
const std::string& unsuffixed_partition_name,
const std::optional<std::string>&,
bool is_append = false) = 0;
diff --git a/common/dynamic_partition_control_stub.cc b/common/dynamic_partition_control_stub.cc
index 05803fe..dd30a8b 100644
--- a/common/dynamic_partition_control_stub.cc
+++ b/common/dynamic_partition_control_stub.cc
@@ -98,13 +98,6 @@
return nullptr;
}
-FileDescriptorPtr DynamicPartitionControlStub::OpenCowReader(
- const std::string& unsuffixed_partition_name,
- const std::optional<std::string>&,
- bool /*is_append */) {
- return nullptr;
-}
-
bool DynamicPartitionControlStub::MapAllPartitions() {
return false;
}
diff --git a/common/dynamic_partition_control_stub.h b/common/dynamic_partition_control_stub.h
index eb7154c..515ec7c 100644
--- a/common/dynamic_partition_control_stub.h
+++ b/common/dynamic_partition_control_stub.h
@@ -64,9 +64,12 @@
const std::string& unsuffixed_partition_name,
const std::optional<std::string>&,
bool is_append) override;
- FileDescriptorPtr OpenCowReader(const std::string& unsuffixed_partition_name,
- const std::optional<std::string>&,
- bool is_append = false) override;
+
+ FileDescriptorPtr OpenCowFd(const std::string& unsuffixed_partition_name,
+ const std::optional<std::string>&,
+ bool is_append = false) override {
+ return nullptr;
+ }
bool MapAllPartitions() override;
bool UnmapAllPartitions() override;
diff --git a/common/mock_dynamic_partition_control.h b/common/mock_dynamic_partition_control.h
index 391d3eb..bfd1b0c 100644
--- a/common/mock_dynamic_partition_control.h
+++ b/common/mock_dynamic_partition_control.h
@@ -37,7 +37,7 @@
MOCK_METHOD(FeatureFlag, GetVirtualAbFeatureFlag, (), (override));
MOCK_METHOD(bool, FinishUpdate, (bool), (override));
MOCK_METHOD(FileDescriptorPtr,
- OpenCowReader,
+ OpenCowFd,
(const std::string& unsuffixed_partition_name,
const std::optional<std::string>& source_path,
bool is_append),
diff --git a/download_action_android_unittest.cc b/download_action_android_unittest.cc
index b17550b..fef2d24 100644
--- a/download_action_android_unittest.cc
+++ b/download_action_android_unittest.cc
@@ -36,6 +36,7 @@
#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/annotated_operation.h"
#include "update_engine/payload_generator/payload_file.h"
#include "update_engine/payload_generator/payload_signer.h"
@@ -149,13 +150,16 @@
payload.size = data.size();
payload.payload_urls.emplace_back("http://fake_url.invalid");
install_plan.is_resume = true;
+ auto& install_part = install_plan.partitions.emplace_back();
+ install_part.source_path = partition_file.path();
+ install_part.target_path = partition_file.path();
action_pipe->set_contents(install_plan);
+ FakeHardware hardware;
// takes ownership of passed in HttpFetcher
auto download_action = std::make_unique<DownloadAction>(
- &prefs, &boot_control, nullptr, http_fetcher, false /* interactive */);
+ &prefs, &boot_control, &hardware, http_fetcher, false /* interactive */);
- FakeHardware hardware;
auto delta_performer = std::make_unique<DeltaPerformer>(&prefs,
&boot_control,
&hardware,
diff --git a/payload_consumer/cow_writer_file_descriptor.cc b/payload_consumer/cow_writer_file_descriptor.cc
new file mode 100644
index 0000000..d8c7afb
--- /dev/null
+++ b/payload_consumer/cow_writer_file_descriptor.cc
@@ -0,0 +1,138 @@
+//
+// 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.
+//
+
+#include "update_engine/payload_consumer/cow_writer_file_descriptor.h"
+
+#include <memory>
+#include <utility>
+
+#include <base/logging.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+CowWriterFileDescriptor::CowWriterFileDescriptor(
+ std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer)
+ : cow_writer_(std::move(cow_writer)),
+ cow_reader_(cow_writer_->OpenReader()) {}
+
+bool CowWriterFileDescriptor::Open(const char* path, int flags, mode_t mode) {
+ LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
+ return false;
+}
+bool CowWriterFileDescriptor::Open(const char* path, int flags) {
+ LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
+ return false;
+}
+
+ssize_t CowWriterFileDescriptor::Read(void* buf, size_t count) {
+ if (dirty_) {
+ // OK, CowReader provides a snapshot view of what the cow contains. Which
+ // means any writes happened after opening a CowReader isn't visible to
+ // that CowReader. Therefore, we re-open CowReader whenever we attempt a
+ // read after write. This does incur an overhead everytime you read after
+ // write.
+ // The usage of |dirty_| flag to coordinate re-open is a very coarse grained
+ // checked. This implementation has suboptimal performance. For better
+ // performance, keep track of blocks which are overwritten, and only re-open
+ // if reading a dirty block.
+ // TODO(b/173432386) Implement finer grained dirty checks
+ const auto offset = cow_reader_->Seek(0, SEEK_CUR);
+ cow_reader_.reset();
+ if (!cow_writer_->Finalize()) {
+ LOG(ERROR) << "Failed to Finalize() cow writer";
+ return -1;
+ }
+ cow_reader_ = cow_writer_->OpenReader();
+ if (cow_reader_ == nullptr) {
+ LOG(ERROR) << "Failed to re-open cow reader after writing to COW";
+ return -1;
+ }
+ const auto pos = cow_reader_->Seek(offset, SEEK_SET);
+ if (pos != offset) {
+ LOG(ERROR) << "Failed to seek to previous position after re-opening cow "
+ "reader, expected "
+ << offset << " actual: " << pos;
+ return -1;
+ }
+ dirty_ = false;
+ }
+ return cow_reader_->Read(buf, count);
+}
+
+ssize_t CowWriterFileDescriptor::Write(const void* buf, size_t count) {
+ auto offset = cow_reader_->Seek(0, SEEK_CUR);
+ CHECK_EQ(offset % cow_writer_->options().block_size, 0);
+ auto success = cow_writer_->AddRawBlocks(
+ offset / cow_writer_->options().block_size, buf, count);
+ if (success) {
+ if (cow_reader_->Seek(count, SEEK_CUR) < 0) {
+ return -1;
+ }
+ dirty_ = true;
+ return count;
+ }
+ return -1;
+}
+
+off64_t CowWriterFileDescriptor::Seek(const off64_t offset, int whence) {
+ return cow_reader_->Seek(offset, whence);
+}
+
+uint64_t CowWriterFileDescriptor::BlockDevSize() {
+ LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlockDevSize()";
+ return 0;
+}
+
+bool CowWriterFileDescriptor::BlkIoctl(int request,
+ uint64_t start,
+ uint64_t length,
+ int* result) {
+ LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlkIoctl()";
+ return false;
+}
+
+bool CowWriterFileDescriptor::Flush() {
+ // CowWriter already automatilly flushes, no need to do anything.
+ return true;
+}
+
+bool CowWriterFileDescriptor::Close() {
+ if (cow_writer_) {
+ TEST_AND_RETURN_FALSE(cow_writer_->Finalize());
+ cow_writer_ = nullptr;
+ }
+ if (cow_reader_) {
+ TEST_AND_RETURN_FALSE(cow_reader_->Close());
+ cow_reader_ = nullptr;
+ }
+ return true;
+}
+
+bool CowWriterFileDescriptor::IsSettingErrno() {
+ return false;
+}
+
+bool CowWriterFileDescriptor::IsOpen() {
+ return cow_writer_ != nullptr && cow_reader_ != nullptr;
+}
+
+CowWriterFileDescriptor::~CowWriterFileDescriptor() {
+ Close();
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/cow_writer_file_descriptor.h b/payload_consumer/cow_writer_file_descriptor.h
new file mode 100644
index 0000000..5d9ffc6
--- /dev/null
+++ b/payload_consumer/cow_writer_file_descriptor.h
@@ -0,0 +1,66 @@
+//
+// 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.
+//
+
+#include <cstdint>
+#include <memory>
+
+#include <libsnapshot/snapshot_writer.h>
+
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+
+// A Readable/Writable FileDescriptor class. This is a simple wrapper around
+// CowWriter. Only intended to be used by FileSystemVerifierAction for writing
+// FEC. Writes must be block aligned(4096) or write will fail.
+class CowWriterFileDescriptor final : public FileDescriptor {
+ public:
+ explicit CowWriterFileDescriptor(
+ std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer);
+ ~CowWriterFileDescriptor();
+
+ bool Open(const char* path, int flags, mode_t mode) override;
+ bool Open(const char* path, int flags) override;
+
+ ssize_t Read(void* buf, size_t count) override;
+
+ // |count| must be block aligned, current offset of this fd must also be block
+ // aligned.
+ ssize_t Write(const void* buf, size_t count) override;
+
+ off64_t Seek(off64_t offset, int whence) override;
+
+ uint64_t BlockDevSize() override;
+
+ bool BlkIoctl(int request,
+ uint64_t start,
+ uint64_t length,
+ int* result) override;
+
+ bool Flush() override;
+
+ bool Close() override;
+
+ bool IsSettingErrno() override;
+
+ bool IsOpen() override;
+
+ private:
+ std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer_;
+ FileDescriptorPtr cow_reader_;
+ bool dirty_ = false;
+};
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 82e589e..a57169b 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -584,6 +584,11 @@
CheckpointUpdateProgress(false);
}
+ if (partition_writer_) {
+ TEST_AND_RETURN_FALSE(partition_writer_->FinishedInstallOps());
+ }
+ CloseCurrentPartition();
+
// In major version 2, we don't add unused operation to the payload.
// If we already extracted the signature we should skip this step.
if (manifest_.has_signatures_offset() && manifest_.has_signatures_size() &&
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index aa2fbaa..09dc638 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -32,8 +32,10 @@
#include <base/strings/string_util.h>
#include <brillo/data_encoding.h>
#include <brillo/message_loops/message_loop.h>
+#include <brillo/secure_blob.h>
#include <brillo/streams/file_stream.h>
+#include "payload_generator/delta_diff_generator.h"
#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/file_descriptor.h"
@@ -106,8 +108,7 @@
}
void FilesystemVerifierAction::Cleanup(ErrorCode code) {
- read_fd_.reset();
- write_fd_.reset();
+ partition_fd_.reset();
// This memory is not used anymore.
buffer_.clear();
@@ -129,36 +130,30 @@
const InstallPlan::Partition& partition =
install_plan_.partitions[partition_index_];
- read_fd_ = dynamic_control_->OpenCowReader(
- partition.name, partition.source_path, true);
- if (!read_fd_) {
+ // FilesystemVerifierAction need the read_fd_.
+ partition_fd_ =
+ dynamic_control_->OpenCowFd(partition.name, partition.source_path, true);
+ if (!partition_fd_) {
LOG(ERROR) << "OpenCowReader(" << partition.name << ", "
<< partition.source_path << ") failed.";
return false;
}
partition_size_ = partition.target_size;
- // TODO(b/173432386): Support Verity writes for VABC.
- CHECK_EQ(partition.fec_size, 0U);
- CHECK_EQ(partition.hash_tree_size, 0U);
return true;
}
bool FilesystemVerifierAction::InitializeFd(const std::string& part_path) {
- read_fd_ = FileDescriptorPtr(new EintrSafeFileDescriptor());
- if (!read_fd_->Open(part_path.c_str(), O_RDONLY)) {
+ partition_fd_ = FileDescriptorPtr(new EintrSafeFileDescriptor());
+ const bool write_verity = ShouldWriteVerity();
+ int flags = write_verity ? O_RDWR : O_RDONLY;
+ if (!utils::SetBlockDeviceReadOnly(part_path, !write_verity)) {
+ LOG(WARNING) << "Failed to set block device " << part_path << " as "
+ << (write_verity ? "writable" : "readonly");
+ }
+ if (!partition_fd_->Open(part_path.c_str(), flags)) {
LOG(ERROR) << "Unable to open " << part_path << " for reading.";
return false;
}
-
- // Can't re-use |read_fd_|, as verity writer may call `seek` to modify state
- // of a file descriptor.
- if (ShouldWriteVerity()) {
- write_fd_ = FileDescriptorPtr(new EintrSafeFileDescriptor());
- if (!write_fd_->Open(part_path.c_str(), O_RDWR)) {
- LOG(ERROR) << "Unable to open " << part_path << " for Read/Write.";
- return false;
- }
- }
return true;
}
@@ -238,9 +233,12 @@
}
if (ShouldWriteVerity()) {
if (!verity_writer_->Init(partition)) {
+ LOG(INFO) << "Verity writes enabled on partition " << partition.name;
Cleanup(ErrorCode::kVerityCalculationError);
return;
}
+ } else {
+ LOG(INFO) << "Verity writes disabled on partition " << partition.name;
}
// Start the first read.
@@ -257,19 +255,19 @@
void FilesystemVerifierAction::ReadVerityAndFooter() {
if (ShouldWriteVerity()) {
- if (!verity_writer_->Finalize(read_fd_, write_fd_)) {
+ 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_;
- // Since we handed our |read_fd_| to verity_writer_ during |Finalize()| call,
- // fd's position could have been changed. Re-seek.
- read_fd_->Seek(filesystem_data_end_, SEEK_SET);
while (bytes_to_read > 0) {
const auto read_size = std::min<size_t>(buffer_.size(), bytes_to_read);
- auto bytes_read = read_fd_->Read(buffer_.data(), read_size);
+ 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);
@@ -296,10 +294,11 @@
ReadVerityAndFooter();
return;
}
-
- auto bytes_read = read_fd_->Read(buffer_.data(), bytes_to_read);
+ 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.";
+ 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|
@@ -424,11 +423,8 @@
// Start hashing the next partition, if any.
hasher_.reset();
buffer_.clear();
- if (read_fd_) {
- read_fd_.reset();
- }
- if (write_fd_) {
- write_fd_.reset();
+ if (partition_fd_) {
+ partition_fd_.reset();
}
StartPartitionHashing();
}
diff --git a/payload_consumer/filesystem_verifier_action.h b/payload_consumer/filesystem_verifier_action.h
index 1f527c9..78634cb 100644
--- a/payload_consumer/filesystem_verifier_action.h
+++ b/payload_consumer/filesystem_verifier_action.h
@@ -126,14 +126,8 @@
size_t partition_index_{0};
// If not null, the FileDescriptor used to read from the device.
- // |read_fd_| and |write_fd_| will be initialized when we begin hashing a
- // partition. They will be deallocated once we encounter an error or
- // successfully finished hashing.
- FileDescriptorPtr read_fd_;
- // If not null, the FileDescriptor used to write to the device.
- // For VABC, this will be different from |read_fd_|. For other cases
- // this can be the same as |read_fd_|.
- FileDescriptorPtr write_fd_;
+ // verity writer might attempt to write to this fd, if verity is enabled.
+ FileDescriptorPtr partition_fd_;
// Buffer for storing data we read.
brillo::Blob buffer_;
diff --git a/payload_consumer/filesystem_verifier_action_unittest.cc b/payload_consumer/filesystem_verifier_action_unittest.cc
index f618626..2cad523 100644
--- a/payload_consumer/filesystem_verifier_action_unittest.cc
+++ b/payload_consumer/filesystem_verifier_action_unittest.cc
@@ -33,6 +33,7 @@
#include "update_engine/common/mock_dynamic_partition_control.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/fake_file_descriptor.h"
#include "update_engine/payload_consumer/install_plan.h"
using brillo::MessageLoop;
@@ -83,7 +84,8 @@
if (action->Type() == FilesystemVerifierAction::StaticType()) {
ran_ = true;
code_ = code;
- EXPECT_FALSE(static_cast<FilesystemVerifierAction*>(action)->read_fd_);
+ EXPECT_FALSE(
+ static_cast<FilesystemVerifierAction*>(action)->partition_fd_);
} else if (action->Type() ==
ObjectCollectorAction<InstallPlan>::StaticType()) {
auto collector_action =
@@ -251,8 +253,8 @@
processor_.set_delegate(&delegate);
processor_.StartProcessing();
- EXPECT_FALSE(processor_.IsRunning());
- EXPECT_TRUE(delegate.ran_);
+ ASSERT_FALSE(processor_.IsRunning());
+ ASSERT_TRUE(delegate.ran_);
EXPECT_EQ(ErrorCode::kError, delegate.code_);
}
@@ -397,12 +399,12 @@
base::Unretained(&processor_)));
loop_.Run();
- EXPECT_FALSE(processor_.IsRunning());
- EXPECT_TRUE(delegate.ran());
- EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
+ ASSERT_FALSE(processor_.IsRunning());
+ ASSERT_TRUE(delegate.ran());
+ ASSERT_EQ(ErrorCode::kSuccess, delegate.code());
}
-TEST_F(FilesystemVerifierActionTest, RunWithVABC) {
+TEST_F(FilesystemVerifierActionTest, RunWithVABCNoVerity) {
InstallPlan install_plan;
InstallPlan::Partition& part = install_plan.partitions.emplace_back();
part.name = "fake_part";
@@ -410,21 +412,25 @@
part.target_size = 4096 * 4096;
part.block_size = 4096;
part.source_path = "/dev/fake_source_path";
+ part.fec_size = 0;
+ part.hash_tree_size = 0;
+ part.target_hash.clear();
+ part.source_hash.clear();
NiceMock<MockDynamicPartitionControl> dynamic_control;
+ auto fake_fd = std::make_shared<FakeFileDescriptor>();
ON_CALL(dynamic_control, GetDynamicPartitionsFeatureFlag())
.WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
ON_CALL(dynamic_control, UpdateUsesSnapshotCompression())
.WillByDefault(Return(true));
- ON_CALL(dynamic_control, OpenCowReader(_, _, _))
- .WillByDefault(Return(nullptr));
+ ON_CALL(dynamic_control, OpenCowFd(_, _, _)).WillByDefault(Return(fake_fd));
ON_CALL(dynamic_control, IsDynamicPartition(part.name, _))
.WillByDefault(Return(true));
EXPECT_CALL(dynamic_control, UpdateUsesSnapshotCompression())
.Times(AtLeast(1));
- EXPECT_CALL(dynamic_control, OpenCowReader(part.name, {part.source_path}, _))
+ EXPECT_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _))
.Times(1);
EXPECT_CALL(dynamic_control, ListDynamicPartitionsForSlot(_, _, _))
.WillRepeatedly(
@@ -443,10 +449,18 @@
base::Unretained(&processor_)));
loop_.Run();
- EXPECT_FALSE(processor_.IsRunning());
- EXPECT_TRUE(delegate.ran());
- // Filesystem verifier will fail, because we returned nullptr as CowReader
- EXPECT_EQ(ErrorCode::kFilesystemVerifierError, delegate.code());
+ ASSERT_FALSE(processor_.IsRunning());
+ ASSERT_TRUE(delegate.ran());
+ // Filesystem verifier will fail, because we set an empty hash
+ ASSERT_EQ(ErrorCode::kNewRootfsVerificationError, delegate.code());
+ const auto& read_pos = fake_fd->GetReadOps();
+ size_t expected_offset = 0;
+ for (const auto& [off, size] : read_pos) {
+ ASSERT_EQ(off, expected_offset);
+ expected_offset += size;
+ }
+ const auto actual_read_size = expected_offset;
+ ASSERT_EQ(actual_read_size, part.target_size);
}
} // namespace chromeos_update_engine
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index 5e42089..8f2d674 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -401,6 +401,10 @@
if (HasOutputPipe()) {
SetOutputObject(install_plan_);
}
+ auto dynamic_control = boot_control_->GetDynamicPartitionControl();
+ CHECK(dynamic_control);
+ dynamic_control->UnmapAllPartitions();
+ LOG(INFO) << "Unmapped all partitions.";
}
void PostinstallRunnerAction::SuspendAction() {
diff --git a/payload_consumer/verity_writer_android.cc b/payload_consumer/verity_writer_android.cc
index 01d8977..e2fab7d 100644
--- a/payload_consumer/verity_writer_android.cc
+++ b/payload_consumer/verity_writer_android.cc
@@ -43,9 +43,6 @@
bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition) {
partition_ = &partition;
- if (partition_->hash_tree_size != 0 || partition_->fec_size != 0) {
- utils::SetBlockDeviceReadOnly(partition_->target_path, false);
- }
if (partition_->hash_tree_size != 0) {
auto hash_function =
HashTreeBuilder::HashFunction(partition_->hash_tree_algorithm);
@@ -67,12 +64,18 @@
return false;
}
}
+ total_offset_ = 0;
return true;
}
-bool VerityWriterAndroid::Update(uint64_t offset,
+bool VerityWriterAndroid::Update(const uint64_t offset,
const uint8_t* buffer,
size_t size) {
+ if (offset != total_offset_) {
+ LOG(ERROR) << "Sequential read expected, expected to read at: "
+ << total_offset_ << " actual read occurs at: " << offset;
+ return false;
+ }
if (partition_->hash_tree_size != 0) {
const uint64_t hash_tree_data_end =
partition_->hash_tree_data_offset + partition_->hash_tree_data_size;
@@ -96,12 +99,22 @@
}
}
}
+ total_offset_ += size;
return true;
}
bool VerityWriterAndroid::Finalize(FileDescriptorPtr read_fd,
FileDescriptorPtr write_fd) {
+ const auto hash_tree_data_end =
+ partition_->hash_tree_data_offset + partition_->hash_tree_data_size;
+ if (total_offset_ < hash_tree_data_end) {
+ LOG(ERROR) << "Read up to " << total_offset_
+ << " when we are expecting to read everything "
+ "before "
+ << hash_tree_data_end;
+ return false;
+ }
// All hash tree data blocks has been hashed, write hash tree to disk.
LOG(INFO) << "Writing verity hash tree to " << partition_->target_path;
TEST_AND_RETURN_FALSE(hash_tree_builder_->BuildHashTree());
diff --git a/payload_consumer/verity_writer_android.h b/payload_consumer/verity_writer_android.h
index e95f188..8339528 100644
--- a/payload_consumer/verity_writer_android.h
+++ b/payload_consumer/verity_writer_android.h
@@ -64,6 +64,7 @@
const InstallPlan::Partition* partition_ = nullptr;
std::unique_ptr<HashTreeBuilder> hash_tree_builder_;
+ uint64_t total_offset_ = 0;
DISALLOW_COPY_AND_ASSIGN(VerityWriterAndroid);
};
diff --git a/payload_consumer/verity_writer_android_unittest.cc b/payload_consumer/verity_writer_android_unittest.cc
index 81d8ca2..75da0ae 100644
--- a/payload_consumer/verity_writer_android_unittest.cc
+++ b/payload_consumer/verity_writer_android_unittest.cc
@@ -76,6 +76,15 @@
ASSERT_TRUE(verity_writer_.Update(8192, part_data.data(), part_data.size()));
}
+TEST_F(VerityWriterAndroidTest, DiscontinuedRead) {
+ partition_.hash_tree_data_size = 8192;
+ partition_.hash_tree_size = 4096;
+ brillo::Blob part_data(4096);
+ ASSERT_TRUE(verity_writer_.Init(partition_));
+ ASSERT_TRUE(verity_writer_.Update(0, part_data.data(), part_data.size()));
+ ASSERT_FALSE(verity_writer_.Update(8192, part_data.data(), part_data.size()));
+}
+
TEST_F(VerityWriterAndroidTest, InvalidHashAlgorithmTest) {
partition_.hash_tree_algorithm = "sha123";
ASSERT_FALSE(verity_writer_.Init(partition_));
@@ -106,6 +115,32 @@
ASSERT_EQ(part_data, actual_part);
}
+TEST_F(VerityWriterAndroidTest, NonZeroOffsetSHA256Test) {
+ partition_.hash_tree_algorithm = "sha256";
+ partition_.hash_tree_data_offset = 100;
+ partition_.hash_tree_offset =
+ partition_.hash_tree_data_offset + partition_.hash_tree_data_size;
+ brillo::Blob part_data(8192 + partition_.hash_tree_data_offset);
+ test_utils::WriteFileVector(partition_.target_path, part_data);
+ ASSERT_TRUE(verity_writer_.Init(partition_));
+ ASSERT_TRUE(verity_writer_.Update(0, part_data.data(), 4096));
+ ASSERT_TRUE(verity_writer_.Update(4096, part_data.data() + 4096, 4096));
+ ASSERT_TRUE(verity_writer_.Update(
+ 8192, part_data.data() + 8192, partition_.hash_tree_data_offset));
+ ASSERT_TRUE(verity_writer_.Finalize(partition_fd_, partition_fd_));
+ brillo::Blob actual_part;
+ utils::ReadFile(partition_.target_path, &actual_part);
+ // dd if=/dev/zero bs=4096 count=1 2>/dev/null | sha256sum | xxd -r -p |
+ // hexdump -v -e '/1 "0x%02x, "'
+ brillo::Blob hash = {0xad, 0x7f, 0xac, 0xb2, 0x58, 0x6f, 0xc6, 0xe9,
+ 0x66, 0xc0, 0x04, 0xd7, 0xd1, 0xd1, 0x6b, 0x02,
+ 0x4f, 0x58, 0x05, 0xff, 0x7c, 0xb4, 0x7c, 0x7a,
+ 0x85, 0xda, 0xbd, 0x8b, 0x48, 0x89, 0x2c, 0xa7};
+ memcpy(
+ part_data.data() + partition_.hash_tree_offset, hash.data(), hash.size());
+ ASSERT_EQ(part_data, actual_part);
+}
+
TEST_F(VerityWriterAndroidTest, FECTest) {
partition_.fec_data_offset = 0;
partition_.fec_data_size = 4096;
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index a87dabf..ab0f036 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -137,6 +137,12 @@
{cow_merge_sequence_->begin(), cow_merge_sequence_->end()},
config_.block_size,
config_.target.dynamic_partition_metadata->vabc_compression_param());
+ if (!new_part_.disable_fec_computation) {
+ *cow_size_ +=
+ new_part_.verity.fec_extent.num_blocks() * config_.block_size;
+ }
+ *cow_size_ +=
+ new_part_.verity.hash_tree_extent.num_blocks() * config_.block_size;
LOG(INFO) << "Estimated COW size for partition: " << new_part_.name << " "
<< *cow_size_;
}