diff options
35 files changed, 892 insertions, 74 deletions
@@ -332,6 +332,7 @@ cc_defaults { "PlatformProperties", ], shared_libs: [ + "apex_aidl_interface-cpp", "libandroid_net", "libbase", "libbinder", @@ -367,6 +368,7 @@ cc_library_static { srcs: [ ":libupdate_engine_aidl", "common/system_state.cc", + "aosp/apex_handler_android.cc", "aosp/binder_service_android.cc", "aosp/binder_service_stable_android.cc", "aosp/daemon_android.cc", @@ -535,6 +537,7 @@ cc_defaults { "libverity_tree", "update_metadata-protos", "libpayload_extent_utils", + "libcow_size_estimator", ], shared_libs: [ "libbase", @@ -558,6 +561,25 @@ cc_library_static { } cc_library_static { + name: "libcow_size_estimator", + defaults: [ + "ue_defaults", + "update_metadata-protos_exports" + ], + host_supported: true, + recovery_available: true, + srcs: [ + "payload_generator/cow_size_estimator.cc", + ], + static_libs: [ + "update_metadata-protos", + "libbase", + "libsnapshot_cow", + "libcow_operation_convert", + ], +} + +cc_library_static { name: "libpayload_generator", defaults: [ "ue_defaults", @@ -574,7 +596,6 @@ cc_library_static { "payload_generator/block_mapping.cc", "payload_generator/boot_img_filesystem.cc", "payload_generator/bzip.cc", - "payload_generator/cow_size_estimator.cc", "payload_generator/deflate_utils.cc", "payload_generator/delta_diff_generator.cc", "payload_generator/delta_diff_utils.cc", @@ -740,6 +761,7 @@ cc_test { test_suites: ["device-tests"], srcs: [ + "aosp/apex_handler_android_unittest.cc", "aosp/dynamic_partition_control_android_unittest.cc", "aosp/update_attempter_android_unittest.cc", "certificate_checker_unittest.cc", @@ -849,3 +871,27 @@ cc_library_headers { }, } } + +cc_binary_host { + name: "cow_converter", + defaults: [ + "ue_defaults", + "libpayload_consumer_exports", + ], + srcs: [ + "aosp/cow_converter.cc", + ], + static_libs: [ + "liblog", + "libbrotli", + "libbase", + "libcow_operation_convert", + "libcow_size_estimator", + "libpayload_consumer", + "libpayload_extent_ranges", + "libpayload_extent_utils", + "libsnapshot_cow", + "libz", + "update_metadata-protos", + ], +}
\ No newline at end of file diff --git a/aosp/apex_handler_android.cc b/aosp/apex_handler_android.cc new file mode 100644 index 00000000..cdbc9834 --- /dev/null +++ b/aosp/apex_handler_android.cc @@ -0,0 +1,98 @@ +// +// 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 <utility> + +#include <base/files/file_util.h> + +#include "update_engine/aosp/apex_handler_android.h" +#include "update_engine/common/utils.h" + +namespace chromeos_update_engine { + +// Don't change this path... apexd relies on it. +constexpr const char* kApexReserveSpaceDir = "/data/apex/ota_reserved"; + +uint64_t ApexHandlerAndroid::CalculateSize( + const std::vector<ApexInfo>& apex_infos) const { + return CalculateSize(apex_infos, GetApexService()); +} + +uint64_t ApexHandlerAndroid::CalculateSize( + const std::vector<ApexInfo>& apex_infos, + android::sp<android::apex::IApexService> apex_service) const { + // The safest option is to allocate space for every compressed APEX + uint64_t size_required_default = 0; + + // We might not need to decompress every APEX. Communicate with apexd to get + // accurate requirement. + int64_t size_from_apexd; + android::apex::CompressedApexInfoList compressed_apex_info_list; + + for (const auto& apex_info : apex_infos) { + if (!apex_info.is_compressed()) { + continue; + } + + size_required_default += apex_info.decompressed_size(); + + android::apex::CompressedApexInfo compressed_apex_info; + compressed_apex_info.moduleName = apex_info.package_name(); + compressed_apex_info.versionCode = apex_info.version(); + compressed_apex_info.decompressedSize = apex_info.decompressed_size(); + compressed_apex_info_list.apexInfos.emplace_back( + std::move(compressed_apex_info)); + } + if (size_required_default == 0 || apex_service == nullptr) { + return size_required_default; + } + + auto result = apex_service->calculateSizeForCompressedApex( + compressed_apex_info_list, &size_from_apexd); + if (!result.isOk()) { + return size_required_default; + } + return size_from_apexd; +} + +bool ApexHandlerAndroid::AllocateSpace(const uint64_t size_required) const { + return AllocateSpace(size_required, kApexReserveSpaceDir); +} + +bool ApexHandlerAndroid::AllocateSpace(const uint64_t size_required, + const std::string& dir_path) const { + if (size_required == 0) { + return true; + } + base::FilePath path{dir_path}; + // The filename is not important, it just needs to be under + // kApexReserveSpaceDir. We call it "full.tmp" because the current space + // estimation is simply adding up all decompressed sizes. + path = path.Append("full.tmp"); + return utils::ReserveStorageSpace(path.value().c_str(), size_required); +} + +android::sp<android::apex::IApexService> ApexHandlerAndroid::GetApexService() + const { + auto binder = android::defaultServiceManager()->waitForService( + android::String16("apexservice")); + if (binder == nullptr) { + return nullptr; + } + return android::interface_cast<android::apex::IApexService>(binder); +} + +} // namespace chromeos_update_engine diff --git a/aosp/apex_handler_android.h b/aosp/apex_handler_android.h new file mode 100644 index 00000000..aac1cd9c --- /dev/null +++ b/aosp/apex_handler_android.h @@ -0,0 +1,48 @@ +// +// 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 SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_ +#define SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_ + +#include <string> +#include <vector> + +#include <android/apex/IApexService.h> +#include <binder/IServiceManager.h> + +#include "update_engine/aosp/apex_handler_interface.h" +#include "update_engine/update_metadata.pb.h" + +namespace chromeos_update_engine { + +class ApexHandlerAndroid : virtual public ApexHandlerInterface { + public: + uint64_t CalculateSize(const std::vector<ApexInfo>& apex_infos) const; + bool AllocateSpace(const uint64_t size_required) const; + + private: + friend class ApexHandlerAndroidTest; + android::sp<android::apex::IApexService> GetApexService() const; + uint64_t CalculateSize( + const std::vector<ApexInfo>& apex_infos, + android::sp<android::apex::IApexService> apex_service) const; + bool AllocateSpace(const uint64_t size_required, + const std::string& dir_path) const; +}; + +} // namespace chromeos_update_engine + +#endif // SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_ANDROID_H_ diff --git a/aosp/apex_handler_android_unittest.cc b/aosp/apex_handler_android_unittest.cc new file mode 100644 index 00000000..3a99f79b --- /dev/null +++ b/aosp/apex_handler_android_unittest.cc @@ -0,0 +1,109 @@ +// +// 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 <utility> +#include <filesystem> + +#include "update_engine/aosp/apex_handler_android.h" + +#include <android-base/file.h> +#include <android-base/strings.h> +#include <gtest/gtest.h> + +using android::base::EndsWith; + +namespace chromeos_update_engine { + +namespace fs = std::filesystem; + +class ApexHandlerAndroidTest : public ::testing::Test { + protected: + ApexHandlerAndroidTest() = default; + + android::sp<android::apex::IApexService> GetApexService() const { + return apex_handler_.GetApexService(); + } + + uint64_t CalculateSize( + const std::vector<ApexInfo>& apex_infos, + android::sp<android::apex::IApexService> apex_service) const { + return apex_handler_.CalculateSize(apex_infos, apex_service); + } + + bool AllocateSpace(const uint64_t size_required, + const std::string& dir_path) const { + return apex_handler_.AllocateSpace(size_required, dir_path); + } + + ApexInfo CreateApexInfo(const std::string& package_name, + int version, + bool is_compressed, + int decompressed_size) { + ApexInfo result; + result.set_package_name(package_name); + result.set_version(version); + result.set_is_compressed(is_compressed); + result.set_decompressed_size(decompressed_size); + return std::move(result); + } + + ApexHandlerAndroid apex_handler_; +}; + +// TODO(b/172911822): Once apexd has more optimized response for CalculateSize, +// improve this test +TEST_F(ApexHandlerAndroidTest, CalculateSize) { + std::vector<ApexInfo> apex_infos; + ApexInfo compressed_apex_1 = CreateApexInfo("sample1", 1, true, 10); + ApexInfo compressed_apex_2 = CreateApexInfo("sample2", 2, true, 20); + apex_infos.push_back(compressed_apex_1); + apex_infos.push_back(compressed_apex_2); + auto apex_service = GetApexService(); + EXPECT_TRUE(apex_service != nullptr) << "Apexservice not found"; + int required_size = CalculateSize(apex_infos, apex_service); + EXPECT_EQ(required_size, 30); +} + +TEST_F(ApexHandlerAndroidTest, AllocateSpace) { + // Allocating 0 space should be a no op + TemporaryDir td; + EXPECT_TRUE(AllocateSpace(0, td.path)); + EXPECT_TRUE(fs::is_empty(td.path)); + + // Allocating non-zero space should create a file with tmp suffix + EXPECT_TRUE(AllocateSpace(2 << 20, td.path)); + EXPECT_FALSE(fs::is_empty(td.path)); + int num_of_file = 0; + for (const auto& entry : fs::directory_iterator(td.path)) { + num_of_file++; + EXPECT_TRUE(EndsWith(entry.path().string(), ".tmp")); + EXPECT_EQ(fs::file_size(entry.path()), 2u << 20); + } + EXPECT_EQ(num_of_file, 1); + + // AllocateSpace should be safe to call twice + EXPECT_TRUE(AllocateSpace(100, td.path)); + EXPECT_FALSE(fs::is_empty(td.path)); + num_of_file = 0; + for (const auto& entry : fs::directory_iterator(td.path)) { + num_of_file++; + EXPECT_TRUE(EndsWith(entry.path().string(), ".tmp")); + EXPECT_EQ(fs::file_size(entry.path()), 100u); + } + EXPECT_EQ(num_of_file, 1); +} + +} // namespace chromeos_update_engine diff --git a/aosp/apex_handler_interface.h b/aosp/apex_handler_interface.h new file mode 100644 index 00000000..c3399b61 --- /dev/null +++ b/aosp/apex_handler_interface.h @@ -0,0 +1,36 @@ +// +// 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 SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_ +#define SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_ + +#include <vector> + +#include "update_engine/update_metadata.pb.h" + +namespace chromeos_update_engine { + +class ApexHandlerInterface { + public: + virtual ~ApexHandlerInterface() = default; + virtual uint64_t CalculateSize( + const std::vector<ApexInfo>& apex_infos) const = 0; + virtual bool AllocateSpace(const uint64_t size_required) const = 0; +}; + +} // namespace chromeos_update_engine + +#endif // SYSTEM_UPDATE_ENGINE_AOSP_APEX_HANDLER_INTERFACE_H_ diff --git a/aosp/cleanup_previous_update_action.cc b/aosp/cleanup_previous_update_action.cc index 55c5a735..6eaa35b2 100644 --- a/aosp/cleanup_previous_update_action.cc +++ b/aosp/cleanup_previous_update_action.cc @@ -395,9 +395,9 @@ void CleanupPreviousUpdateAction::InitiateMergeAndWait() { return; } - uint64_t cow_file_size; - if (snapshot_->InitiateMerge(&cow_file_size)) { - merge_stats_->set_cow_file_size(cow_file_size); + snapshot_->UpdateCowStats(merge_stats_); + + if (snapshot_->InitiateMerge()) { WaitForMergeOrSchedule(); return; } diff --git a/aosp/cow_converter.cc b/aosp/cow_converter.cc new file mode 100644 index 00000000..8c641b8d --- /dev/null +++ b/aosp/cow_converter.cc @@ -0,0 +1,131 @@ +// +// 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 <stdio.h> +#include <string.h> + +#include <cstdint> +#include <cstdio> +#include <memory> + +#include <sys/fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <base/files/file_path.h> +#include <libsnapshot/cow_writer.h> + +#include "update_engine/common/cow_operation_convert.h" +#include "update_engine/common/utils.h" +#include "update_engine/payload_consumer/file_descriptor.h" +#include "update_engine/payload_consumer/payload_metadata.h" +#include "update_engine/payload_generator/cow_size_estimator.h" +#include "update_engine/update_metadata.pb.h" + +using android::snapshot::CowWriter; + +namespace chromeos_update_engine { + +bool ProcessPartition(const chromeos_update_engine::PartitionUpdate& partition, + const char* image_dir, + size_t block_size) { + base::FilePath img_dir{image_dir}; + auto target_img = img_dir.Append(partition.partition_name() + ".img"); + auto output_cow = img_dir.Append(partition.partition_name() + ".cow"); + FileDescriptorPtr target_img_fd = std::make_shared<EintrSafeFileDescriptor>(); + if (!target_img_fd->Open(target_img.value().c_str(), O_RDONLY)) { + PLOG(ERROR) << "Failed to open " << target_img.value(); + return false; + } + android::base::unique_fd output_fd{ + open(output_cow.value().c_str(), O_RDWR | O_CREAT, 0744)}; + if (output_fd < 0) { + PLOG(ERROR) << "Failed to open " << output_cow.value(); + return false; + } + + android::snapshot::CowWriter cow_writer{ + {.block_size = static_cast<uint32_t>(block_size), .compression = "gz"}}; + TEST_AND_RETURN_FALSE(cow_writer.Initialize(output_fd)); + TEST_AND_RETURN_FALSE(CowDryRun(target_img_fd, + partition.operations(), + partition.merge_operations(), + block_size, + &cow_writer)); + TEST_AND_RETURN_FALSE(cow_writer.Finalize()); + return true; +} + +} // namespace chromeos_update_engine + +using chromeos_update_engine::MetadataParseResult; +using chromeos_update_engine::PayloadMetadata; + +int main(int argc, const char* argv[]) { + if (argc != 3) { + printf("Usage: %s <payload.bin> <extracted target_file>\n", argv[0]); + return -1; + } + const char* payload_path = argv[1]; + const char* images_dir = argv[2]; + int payload_fd = open(payload_path, O_RDONLY); + if (payload_fd < 0) { + PLOG(ERROR) << "Failed to open payload file:"; + return 1; + } + chromeos_update_engine::ScopedFdCloser closer{&payload_fd}; + auto payload_size = chromeos_update_engine::utils::FileSize(payload_fd); + if (payload_size <= 0) { + PLOG(ERROR) + << "Couldn't determine size of payload file, or payload file is empty"; + return 2; + } + + PayloadMetadata payload_metadata; + auto payload = static_cast<unsigned char*>( + mmap(nullptr, payload_size, PROT_READ, MAP_PRIVATE, payload_fd, 0)); + + // C++ dark magic to ensure that |payload| is properly deallocated once the + // program exits. + auto munmap_deleter = [payload_size](auto payload) { + munmap(payload, payload_size); + }; + std::unique_ptr<unsigned char, decltype(munmap_deleter)> munmapper{ + payload, munmap_deleter}; + + if (payload == nullptr) { + PLOG(ERROR) << "Failed to mmap() payload file"; + return 3; + } + if (payload_metadata.ParsePayloadHeader(payload, payload_size, nullptr) != + chromeos_update_engine::MetadataParseResult::kSuccess) { + LOG(ERROR) << "Payload header parse failed!"; + return 4; + } + chromeos_update_engine::DeltaArchiveManifest manifest; + if (!payload_metadata.GetManifest(payload, payload_size, &manifest)) { + LOG(ERROR) << "Failed to parse manifest!"; + return 5; + } + + for (const auto& partition : manifest.partitions()) { + LOG(INFO) << partition.partition_name(); + if (!ProcessPartition(partition, images_dir, manifest.block_size())) { + return 6; + } + } + return 0; +} diff --git a/aosp/daemon_state_android.cc b/aosp/daemon_state_android.cc index 9bdd1750..fc89d73e 100644 --- a/aosp/daemon_state_android.cc +++ b/aosp/daemon_state_android.cc @@ -18,6 +18,7 @@ #include <base/logging.h> +#include "update_engine/aosp/apex_handler_android.h" #include "update_engine/aosp/update_attempter_android.h" #include "update_engine/common/boot_control.h" #include "update_engine/common/boot_control_stub.h" @@ -64,8 +65,12 @@ bool DaemonStateAndroid::Initialize() { certificate_checker_->Init(); // Initialize the UpdateAttempter before the UpdateManager. - update_attempter_.reset(new UpdateAttempterAndroid( - this, prefs_.get(), boot_control_.get(), hardware_.get())); + update_attempter_.reset( + new UpdateAttempterAndroid(this, + prefs_.get(), + boot_control_.get(), + hardware_.get(), + std::make_unique<ApexHandlerAndroid>())); return true; } diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc index 657eec9a..6bf896c5 100644 --- a/aosp/dynamic_partition_control_android.cc +++ b/aosp/dynamic_partition_control_android.cc @@ -939,6 +939,12 @@ bool DynamicPartitionControlAndroid::UpdatePartitionMetadata( << " to size " << partition_size << ". Not enough space?"; return false; } + if (p->size() < partition_size) { + LOG(ERROR) << "Partition " << partition_name_suffix + << " was expected to have size " << partition_size + << ", but instead has size " << p->size(); + return false; + } LOG(INFO) << "Added partition " << partition_name_suffix << " to group " << group_name_suffix << " with size " << partition_size; } @@ -1015,9 +1021,11 @@ DynamicPartitionControlAndroid::GetPartitionDevice( partition_name + SlotSuffixForSlotNumber(slot); if (UpdateUsesSnapshotCompression() && IsDynamicPartition(partition_name) && slot != current_slot) { - return {{.mountable_device_path = - GetStaticDevicePath(device_dir, partition_name_suffix), - .is_dynamic = true}}; + return { + {.mountable_device_path = base::FilePath{std::string{VABC_DEVICE_DIR}} + .Append(partition_name_suffix) + .value(), + .is_dynamic = true}}; } // When looking up target partition devices, treat them as static if the diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h index d7c8781a..a23827b4 100644 --- a/aosp/dynamic_partition_control_android.h +++ b/aosp/dynamic_partition_control_android.h @@ -20,6 +20,7 @@ #include <memory> #include <set> #include <string> +#include <string_view> #include <vector> #include <base/files/file_util.h> @@ -33,6 +34,10 @@ namespace chromeos_update_engine { class DynamicPartitionControlAndroid : public DynamicPartitionControlInterface { public: + // A directory where all partitions mapped by VABC is expected to be found. + // Per earlier discussion with VAB team, this directory is unlikely to change. + // So we declare it as a constant here. + static constexpr std::string_view VABC_DEVICE_DIR = "/dev/block/mapper/"; DynamicPartitionControlAndroid(); ~DynamicPartitionControlAndroid(); diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc index 2f290d7c..4a12b830 100644 --- a/aosp/dynamic_partition_control_android_unittest.cc +++ b/aosp/dynamic_partition_control_android_unittest.cc @@ -472,7 +472,10 @@ TEST_P(DynamicPartitionControlAndroidTestP, GetMountableDevicePathVABC) { auto device_info = dynamicControl().GetPartitionDevice("system", target(), source(), false); ASSERT_TRUE(device_info.has_value()); - ASSERT_EQ(device_info->mountable_device_path, GetDevice(T("system"))); + base::FilePath vabc_device_dir{ + std::string{DynamicPartitionControlAndroid::VABC_DEVICE_DIR}}; + ASSERT_EQ(device_info->mountable_device_path, + vabc_device_dir.Append(T("system")).value()); } TEST_P(DynamicPartitionControlAndroidTestP, @@ -1102,7 +1105,8 @@ TEST_P(SnapshotPartitionTestP, RecoveryErrorShouldDeleteSource) { SetMetadata( source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}}, 0, super_size); ExpectUnmap({T("system"), T("vendor")}); - // Expect that the source partitions aren't present in target super metadata. + // Expect that the source partitions aren't present in target super + // metadata. ExpectStoreMetadata({{T("system"), 3_GiB}, {T("vendor"), 1_GiB}}); uint64_t required_size = 0; diff --git a/aosp/sideload_main.cc b/aosp/sideload_main.cc index 3cbc0c7f..bf015c94 100644 --- a/aosp/sideload_main.cc +++ b/aosp/sideload_main.cc @@ -154,8 +154,11 @@ bool ApplyUpdatePayload(const string& payload, return false; } - UpdateAttempterAndroid update_attempter( - &sideload_daemon_state, &prefs, boot_control.get(), hardware.get()); + UpdateAttempterAndroid update_attempter(&sideload_daemon_state, + &prefs, + boot_control.get(), + hardware.get(), + nullptr); update_attempter.Init(); TEST_AND_RETURN_FALSE(update_attempter.ApplyPayload( diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc index 79840e8b..c685855f 100644 --- a/aosp/update_attempter_android.cc +++ b/aosp/update_attempter_android.cc @@ -133,11 +133,13 @@ UpdateAttempterAndroid::UpdateAttempterAndroid( DaemonStateInterface* daemon_state, PrefsInterface* prefs, BootControlInterface* boot_control, - HardwareInterface* hardware) + HardwareInterface* hardware, + std::unique_ptr<ApexHandlerInterface> apex_handler) : daemon_state_(daemon_state), prefs_(prefs), boot_control_(boot_control), hardware_(hardware), + apex_handler_android_(std::move(apex_handler)), processor_(new ActionProcessor()), clock_(new Clock()) { metrics_reporter_ = metrics::CreateMetricsReporter( @@ -977,6 +979,13 @@ uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload( return 0; } + std::vector<ApexInfo> apex_infos(manifest.apex_info().begin(), + manifest.apex_info().end()); + uint64_t apex_size_required = 0; + if (apex_handler_android_ != nullptr) { + apex_size_required = apex_handler_android_->CalculateSize(apex_infos); + } + string payload_id = GetPayloadId(headers); uint64_t required_size = 0; if (!DeltaPerformer::PreparePartitionsForUpdate(prefs_, @@ -990,11 +999,19 @@ uint64_t UpdateAttempterAndroid::AllocateSpaceForPayload( return 0; } else { LOG(ERROR) << "Insufficient space for payload: " << required_size + << " bytes, apex decompression: " << apex_size_required << " bytes"; - return required_size; + return required_size + apex_size_required; } } + if (apex_size_required > 0 && apex_handler_android_ != nullptr && + !apex_handler_android_->AllocateSpace(apex_size_required)) { + LOG(ERROR) << "Insufficient space for apex decompression: " + << apex_size_required << " bytes"; + return apex_size_required; + } + LOG(INFO) << "Successfully allocated space for payload."; return 0; } diff --git a/aosp/update_attempter_android.h b/aosp/update_attempter_android.h index 499f8f6b..70938bcd 100644 --- a/aosp/update_attempter_android.h +++ b/aosp/update_attempter_android.h @@ -26,6 +26,7 @@ #include <android-base/unique_fd.h> #include <base/time/time.h> +#include "update_engine/aosp/apex_handler_interface.h" #include "update_engine/aosp/service_delegate_android_interface.h" #include "update_engine/client_library/include/update_engine/update_status.h" #include "update_engine/common/action_processor.h" @@ -57,7 +58,8 @@ class UpdateAttempterAndroid UpdateAttempterAndroid(DaemonStateInterface* daemon_state, PrefsInterface* prefs, BootControlInterface* boot_control_, - HardwareInterface* hardware_); + HardwareInterface* hardware_, + std::unique_ptr<ApexHandlerInterface> apex_handler); ~UpdateAttempterAndroid() override; // Further initialization to be done post construction. @@ -205,6 +207,8 @@ class UpdateAttempterAndroid BootControlInterface* boot_control_; HardwareInterface* hardware_; + std::unique_ptr<ApexHandlerInterface> apex_handler_android_; + // Last status notification timestamp used for throttling. Use monotonic // TimeTicks to ensure that notifications are sent even if the system clock is // set back in the middle of an update. diff --git a/aosp/update_attempter_android_unittest.cc b/aosp/update_attempter_android_unittest.cc index 173e943d..f799df3e 100644 --- a/aosp/update_attempter_android_unittest.cc +++ b/aosp/update_attempter_android_unittest.cc @@ -69,7 +69,7 @@ class UpdateAttempterAndroidTest : public ::testing::Test { FakeHardware hardware_; UpdateAttempterAndroid update_attempter_android_{ - &daemon_state_, &prefs_, &boot_control_, &hardware_}; + &daemon_state_, &prefs_, &boot_control_, &hardware_, nullptr}; FakeClock* clock_; testing::NiceMock<MockMetricsReporter>* metrics_reporter_; diff --git a/common/cow_operation_convert.cc b/common/cow_operation_convert.cc index 4dc73a7c..2564abf0 100644 --- a/common/cow_operation_convert.cc +++ b/common/cow_operation_convert.cc @@ -20,6 +20,7 @@ #include "update_engine/payload_generator/extent_ranges.h" #include "update_engine/payload_generator/extent_utils.h" +#include "update_engine/update_metadata.pb.h" namespace chromeos_update_engine { @@ -40,6 +41,9 @@ std::vector<CowOperation> ConvertToCowOperations( // This loop handles CowCopy blocks within SOURCE_COPY, and the next loop // converts the leftover blocks to CowReplace? for (const auto& merge_op : merge_operations) { + if (merge_op.type() != CowMergeOperation::COW_COPY) { + continue; + } merge_extents.AddExtent(merge_op.dst_extent()); const auto& src_extent = merge_op.src_extent(); const auto& dst_extent = merge_op.dst_extent(); diff --git a/common/utils.cc b/common/utils.cc index 0f3b6c65..f5532ffe 100644 --- a/common/utils.cc +++ b/common/utils.cc @@ -28,6 +28,7 @@ #include <string.h> #include <sys/mount.h> #include <sys/resource.h> +#include <sys/sendfile.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> @@ -547,6 +548,31 @@ bool SetBlockDeviceReadOnly(const string& device, bool read_only) { return true; } +bool ReserveStorageSpace(const char* path, uint64_t size) { + int fd = HANDLE_EINTR(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600)); + + TEST_AND_RETURN_FALSE_ERRNO(fd >= 0); + ScopedFdCloser closer1{&fd}; + if (ftruncate(fd, size) < 0) { + PLOG(WARNING) << "Failed to ftruncate " << path; + } + // 1MB buffer + std::vector<unsigned char> buffer(1 << 20); + + while (size > 0) { + uint64_t bytes_to_write = std::min(size, (uint64_t)buffer.size()); + if (!utils::WriteAll(fd, buffer.data(), bytes_to_write)) { + auto off = lseek64(fd, 0, SEEK_CUR); + PLOG(ERROR) << "Failed to write 0 to " << path << "offset: " << off + << " size: " << size; + unlink(path); + return false; + } + size -= bytes_to_write; + } + return true; +} + bool MountFilesystem(const string& device, const string& mountpoint, unsigned long mountflags, // NOLINT(runtime/int) diff --git a/common/utils.h b/common/utils.h index 5f6e4757..9a278eb1 100644 --- a/common/utils.h +++ b/common/utils.h @@ -23,6 +23,7 @@ #include <unistd.h> #include <algorithm> +#include <cstdint> #include <limits> #include <map> #include <memory> @@ -181,6 +182,10 @@ std::string MakePartitionName(const std::string& disk_name, int partition_num); // in |read_only|. Return whether the operation succeeded. bool SetBlockDeviceReadOnly(const std::string& device, bool read_only); +// Reserve |size| bytes on space on |path| by creating a file at |path| and +// write 0s into it. Return true iff both creation and writing succeed. +[[nodiscard]] bool ReserveStorageSpace(const char* path, uint64_t size); + // Synchronously mount or unmount a filesystem. Return true on success. // When mounting, it will attempt to mount the device as the passed filesystem // type |type|, with the passed |flags| options. If |type| is empty, "ext2", diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc index c6d63437..f26dd482 100644 --- a/payload_consumer/delta_performer.cc +++ b/payload_consumer/delta_performer.cc @@ -802,6 +802,7 @@ bool DeltaPerformer::PreparePartitionsForUpdate( } else { LOG(INFO) << "Preparing partitions for new update. last hash = " << last_hash << ", new hash = " << update_check_response_hash; + ResetUpdateProgress(prefs, false); } if (!boot_control->GetDynamicPartitionControl()->PreparePartitionsForUpdate( diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc index e7ccff57..4fab9751 100644 --- a/payload_consumer/delta_performer_integration_test.cc +++ b/payload_consumer/delta_performer_integration_test.cc @@ -61,6 +61,9 @@ using test_utils::kRandomString; using test_utils::ScopedLoopMounter; using test_utils::System; using testing::_; +using testing::IsEmpty; +using testing::NiceMock; +using testing::Not; using testing::Return; extern const char* kUnittestPrivateKeyPath; @@ -717,7 +720,24 @@ static void ApplyDeltaFile(bool full_kernel, EXPECT_FALSE(rootfs_part.new_partition_info().hash().empty()); } - MockPrefs prefs; + NiceMock<MockPrefs> prefs; + ON_CALL(prefs, SetInt64(kPrefsManifestMetadataSize, -1)) + .WillByDefault(Return(true)); + ON_CALL(prefs, SetInt64(kPrefsUpdateCheckResponseHash, -1)) + .WillByDefault(Return(true)); + ON_CALL(prefs, GetString(kPrefsUpdateCheckResponseHash, _)) + .WillByDefault(Return(true)); + ON_CALL(prefs, GetString(kPrefsDynamicPartitionMetadataUpdated, _)) + .WillByDefault(Return(true)); + + // Set default expectation to ignore uninteresting calls to + // SetString/SetInt64. When starting an update delta_performer might reset + // update checkpoints, which results in a lot of calls with empty string or + // integer -1. Ignore these. + EXPECT_CALL(prefs, SetString(_, IsEmpty())).WillRepeatedly(Return(true)); + EXPECT_CALL(prefs, SetInt64(_, -1)).WillRepeatedly(Return(true)); + EXPECT_CALL(prefs, SetInt64(_, 0)).WillRepeatedly(Return(true)); + EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize, state->metadata_size)) .WillOnce(Return(true)); EXPECT_CALL( @@ -744,8 +764,9 @@ static void ApplyDeltaFile(bool full_kernel, state->metadata_size))) .WillRepeatedly(Return(true)); if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) { - EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _)) - .WillOnce(Return(true)); + EXPECT_CALL(prefs, + SetString(kPrefsUpdateStateSignatureBlob, Not(IsEmpty()))) + .WillRepeatedly(Return(true)); } EXPECT_CALL(state->mock_delegate_, ShouldCancel(_)) diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc index 2cb73eb1..f797723c 100644 --- a/payload_consumer/payload_metadata.cc +++ b/payload_consumer/payload_metadata.cc @@ -50,12 +50,17 @@ uint64_t PayloadMetadata::GetManifestOffset() const { MetadataParseResult PayloadMetadata::ParsePayloadHeader( const brillo::Blob& payload, ErrorCode* error) { + return ParsePayloadHeader(payload.data(), payload.size(), error); +} + +MetadataParseResult PayloadMetadata::ParsePayloadHeader( + const unsigned char* payload, size_t size, ErrorCode* error) { // Ensure we have data to cover the major payload version. - if (payload.size() < kDeltaManifestSizeOffset) + if (size < kDeltaManifestSizeOffset) return MetadataParseResult::kInsufficientData; // Validate the magic string. - if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) { + if (memcmp(payload, kDeltaMagic, sizeof(kDeltaMagic)) != 0) { LOG(ERROR) << "Bad payload format -- invalid delta magic: " << base::StringPrintf("%02x%02x%02x%02x", payload[0], @@ -74,7 +79,7 @@ MetadataParseResult PayloadMetadata::ParsePayloadHeader( uint64_t manifest_offset = GetManifestOffset(); // Check again with the manifest offset. - if (payload.size() < manifest_offset) + if (size < manifest_offset) return MetadataParseResult::kInsufficientData; // Extract the payload version from the metadata. @@ -136,8 +141,14 @@ bool PayloadMetadata::ParsePayloadHeader(const brillo::Blob& payload) { bool PayloadMetadata::GetManifest(const brillo::Blob& payload, DeltaArchiveManifest* out_manifest) const { + return GetManifest(payload.data(), payload.size(), out_manifest); +} + +bool PayloadMetadata::GetManifest(const unsigned char* payload, + size_t size, + DeltaArchiveManifest* out_manifest) const { uint64_t manifest_offset = GetManifestOffset(); - CHECK_GE(payload.size(), manifest_offset + manifest_size_); + CHECK_GE(size, manifest_offset + manifest_size_); return out_manifest->ParseFromArray(&payload[manifest_offset], manifest_size_); } diff --git a/payload_consumer/payload_metadata.h b/payload_consumer/payload_metadata.h index 8b36f533..f23b668f 100644 --- a/payload_consumer/payload_metadata.h +++ b/payload_consumer/payload_metadata.h @@ -56,6 +56,9 @@ class PayloadMetadata { // the payload. MetadataParseResult ParsePayloadHeader(const brillo::Blob& payload, ErrorCode* error); + MetadataParseResult ParsePayloadHeader(const unsigned char* payload, + size_t size, + ErrorCode* error); // Simpler version of the above, returns true on success. bool ParsePayloadHeader(const brillo::Blob& payload); @@ -88,6 +91,10 @@ class PayloadMetadata { bool GetManifest(const brillo::Blob& payload, DeltaArchiveManifest* out_manifest) const; + bool GetManifest(const unsigned char* payload, + size_t size, + DeltaArchiveManifest* out_manifest) const; + // Parses a payload file |payload_path| and prepares the metadata properties, // manifest and metadata signatures. Can be used as an easy to use utility to // get the payload information without manually the process. diff --git a/payload_consumer/vabc_partition_writer.cc b/payload_consumer/vabc_partition_writer.cc index aa8c3cef..0843fffb 100644 --- a/payload_consumer/vabc_partition_writer.cc +++ b/payload_consumer/vabc_partition_writer.cc @@ -102,11 +102,11 @@ bool VABCPartitionWriter::WriteAllCowOps( std::vector<uint8_t> buffer(block_size); for (const auto& cow_op : converted) { - if (cow_op.src_block == cow_op.dst_block) { - continue; - } switch (cow_op.op) { case CowOperation::CowCopy: + if (cow_op.src_block == cow_op.dst_block) { + continue; + } TEST_AND_RETURN_FALSE( cow_writer->AddCopy(cow_op.dst_block, cow_op.src_block)); break; diff --git a/payload_generator/cow_size_estimator.cc b/payload_generator/cow_size_estimator.cc index 3eb0acac..01e99653 100644 --- a/payload_generator/cow_size_estimator.cc +++ b/payload_generator/cow_size_estimator.cc @@ -16,20 +16,22 @@ #include "update_engine/payload_generator/cow_size_estimator.h" +#include <string> #include <utility> #include <vector> +#include <android-base/unique_fd.h> #include <libsnapshot/cow_writer.h> -#include "android-base/unique_fd.h" #include "update_engine/common/cow_operation_convert.h" -#include "update_engine/payload_consumer/vabc_partition_writer.h" +#include "update_engine/common/utils.h" #include "update_engine/update_metadata.pb.h" namespace chromeos_update_engine { using android::snapshot::CowWriter; -void PerformReplaceOp(const InstallOperation& op, +namespace { +bool PerformReplaceOp(const InstallOperation& op, CowWriter* writer, FileDescriptorPtr target_fd, size_t block_size) { @@ -44,47 +46,101 @@ void PerformReplaceOp(const InstallOperation& op, buffer.size(), extent.start_block() * block_size, &bytes_read); - CHECK(success); + TEST_AND_RETURN_FALSE(success); CHECK_EQ(static_cast<size_t>(bytes_read), buffer.size()); - writer->AddRawBlocks(extent.start_block(), buffer.data(), buffer.size()); + TEST_AND_RETURN_FALSE(writer->AddRawBlocks( + extent.start_block(), buffer.data(), buffer.size())); } + return true; } -void PerformZeroOp(const InstallOperation& op, +bool PerformZeroOp(const InstallOperation& op, CowWriter* writer, size_t block_size) { for (const auto& extent : op.dst_extents()) { - writer->AddZeroBlocks(extent.start_block(), extent.num_blocks()); + TEST_AND_RETURN_FALSE( + writer->AddZeroBlocks(extent.start_block(), extent.num_blocks())); } + return true; } +bool WriteAllCowOps(size_t block_size, + const std::vector<CowOperation>& converted, + android::snapshot::ICowWriter* cow_writer, + FileDescriptorPtr target_fd) { + std::vector<uint8_t> buffer(block_size); + + for (const auto& cow_op : converted) { + switch (cow_op.op) { + case CowOperation::CowCopy: + if (cow_op.src_block == cow_op.dst_block) { + continue; + } + TEST_AND_RETURN_FALSE( + cow_writer->AddCopy(cow_op.dst_block, cow_op.src_block)); + break; + case CowOperation::CowReplace: + ssize_t bytes_read = 0; + TEST_AND_RETURN_FALSE(chromeos_update_engine::utils::ReadAll( + target_fd, + buffer.data(), + block_size, + cow_op.dst_block * block_size, + &bytes_read)); + if (bytes_read <= 0 || static_cast<size_t>(bytes_read) != block_size) { + LOG(ERROR) << "source_fd->Read failed: " << bytes_read; + return false; + } + TEST_AND_RETURN_FALSE(cow_writer->AddRawBlocks( + cow_op.dst_block, buffer.data(), block_size)); + break; + } + } + + return true; +} +} // namespace + size_t EstimateCowSize( - FileDescriptorPtr source_fd, FileDescriptorPtr target_fd, const google::protobuf::RepeatedPtrField<InstallOperation>& operations, const google::protobuf::RepeatedPtrField<CowMergeOperation>& merge_operations, - size_t block_size) { + size_t block_size, + std::string compression) { android::snapshot::CowWriter cow_writer{ - {.block_size = static_cast<uint32_t>(block_size), .compression = "gz"}}; + {.block_size = static_cast<uint32_t>(block_size), + .compression = std::move(compression)}}; // CowWriter treats -1 as special value, will discard all the data but still // reports Cow size. Good for estimation purposes cow_writer.Initialize(android::base::borrowed_fd{-1}); + CHECK(CowDryRun( + target_fd, operations, merge_operations, block_size, &cow_writer)); + CHECK(cow_writer.Finalize()); + return cow_writer.GetCowSize(); +} +bool CowDryRun( + FileDescriptorPtr target_fd, + const google::protobuf::RepeatedPtrField<InstallOperation>& operations, + const google::protobuf::RepeatedPtrField<CowMergeOperation>& + merge_operations, + size_t block_size, + android::snapshot::CowWriter* cow_writer) { const auto converted = ConvertToCowOperations(operations, merge_operations); - VABCPartitionWriter::WriteAllCowOps( - block_size, converted, &cow_writer, source_fd); - cow_writer.AddLabel(0); + WriteAllCowOps(block_size, converted, cow_writer, target_fd); + cow_writer->AddLabel(0); for (const auto& op : operations) { switch (op.type()) { case InstallOperation::REPLACE: case InstallOperation::REPLACE_BZ: case InstallOperation::REPLACE_XZ: - PerformReplaceOp(op, &cow_writer, target_fd, block_size); + TEST_AND_RETURN_FALSE( + PerformReplaceOp(op, cow_writer, target_fd, block_size)); break; case InstallOperation::ZERO: case InstallOperation::DISCARD: - PerformZeroOp(op, &cow_writer, block_size); + TEST_AND_RETURN_FALSE(PerformZeroOp(op, cow_writer, block_size)); break; case InstallOperation::SOURCE_COPY: case InstallOperation::MOVE: @@ -96,15 +152,16 @@ size_t EstimateCowSize( case InstallOperation::BSDIFF: // We might do something special by adding CowBsdiff to CowWriter. // For now proceed the same way as normal REPLACE operation. - PerformReplaceOp(op, &cow_writer, target_fd, block_size); + TEST_AND_RETURN_FALSE( + PerformReplaceOp(op, cow_writer, target_fd, block_size)); break; } // Arbitrary label number, we won't be resuming use these labels here. // They are emitted just to keep size estimates accurate. As update_engine // emits 1 label for every op. - cow_writer.AddLabel(2); + cow_writer->AddLabel(2); } // TODO(zhangkelvin) Take FEC extents into account once VABC stabilizes - return cow_writer.GetCowSize(); + return true; } } // namespace chromeos_update_engine diff --git a/payload_generator/cow_size_estimator.h b/payload_generator/cow_size_estimator.h index cba89b54..850c8909 100644 --- a/payload_generator/cow_size_estimator.h +++ b/payload_generator/cow_size_estimator.h @@ -14,23 +14,34 @@ // limitations under the License. // #include <cstddef> +#include <string> +#include <libsnapshot/cow_writer.h> #include <update_engine/update_metadata.pb.h> #include "update_engine/payload_consumer/file_descriptor.h" namespace chromeos_update_engine { -// Given file descriptor to the source image, target image, and list of +// Given file descriptor to the target image, and list of // operations, estimate the size of COW image if the operations are applied on // Virtual AB Compression enabled device. This is intended to be used by update // generators to put an estimate cow size in OTA payload. When installing an OTA // update, libsnapshot will take this estimate as a hint to allocate spaces. size_t EstimateCowSize( - FileDescriptorPtr source_fd, FileDescriptorPtr target_fd, const google::protobuf::RepeatedPtrField<InstallOperation>& operations, const google::protobuf::RepeatedPtrField<CowMergeOperation>& merge_operations, - size_t block_size); + size_t block_size, + std::string compression); + +// Convert InstallOps to CowOps and apply the converted cow op to |cow_writer| +bool CowDryRun( + FileDescriptorPtr target_fd, + const google::protobuf::RepeatedPtrField<InstallOperation>& operations, + const google::protobuf::RepeatedPtrField<CowMergeOperation>& + merge_operations, + size_t block_size, + android::snapshot::CowWriter* cow_writer); } // namespace chromeos_update_engine diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc index 74014d91..a87dabf1 100644 --- a/payload_generator/delta_diff_generator.cc +++ b/payload_generator/delta_diff_generator.cc @@ -132,11 +132,11 @@ class PartitionProcessor : public base::DelegateSimpleThread::Delegate { *operations.Add() = aop.op; } *cow_size_ = EstimateCowSize( - source_fd, std::move(target_fd), - operations, + std::move(operations), {cow_merge_sequence_->begin(), cow_merge_sequence_->end()}, - config_.block_size); + config_.block_size, + config_.target.dynamic_partition_metadata->vabc_compression_param()); LOG(INFO) << "Estimated COW size for partition: " << new_part_.name << " " << *cow_size_; } diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc index 7288ecaa..b04fec0b 100644 --- a/payload_generator/generate_delta_main.cc +++ b/payload_generator/generate_delta_main.cc @@ -599,7 +599,6 @@ int Main(int argc, char** argv) { if (FLAGS_is_partial_update) { payload_config.is_partial_update = true; } - payload_config.disable_vabc = FLAGS_disable_vabc; if (!FLAGS_in_file.empty()) { return ApplyPayload(FLAGS_in_file, payload_config) ? 0 : 1; @@ -627,6 +626,12 @@ int Main(int argc, char** argv) { CHECK(store.Load(base::FilePath(FLAGS_dynamic_partition_info_file))); CHECK(payload_config.target.LoadDynamicPartitionMetadata(store)); CHECK(payload_config.target.ValidateDynamicPartitionMetadata()); + if (FLAGS_disable_vabc) { + LOG(INFO) << "Disabling VABC"; + payload_config.target.dynamic_partition_metadata->set_vabc_enabled(false); + payload_config.target.dynamic_partition_metadata + ->set_vabc_compression_param(""); + } } CHECK(!FLAGS_out_file.empty()); diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc index 4334066d..6ec219ff 100644 --- a/payload_generator/payload_file.cc +++ b/payload_generator/payload_file.cc @@ -72,9 +72,6 @@ bool PayloadFile::Init(const PayloadGenerationConfig& config) { *(manifest_.mutable_dynamic_partition_metadata()) = *(config.target.dynamic_partition_metadata); - if (config.disable_vabc) { - manifest_.mutable_dynamic_partition_metadata()->set_vabc_enabled(false); - } if (config.is_partial_update) { manifest_.set_partial_update(true); } diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc index f5a70626..d45de6a6 100644 --- a/payload_generator/payload_generation_config.cc +++ b/payload_generator/payload_generation_config.cc @@ -177,10 +177,15 @@ bool ImageConfig::LoadDynamicPartitionMetadata( store.GetBoolean("virtual_ab", &snapshot_enabled); metadata->set_snapshot_enabled(snapshot_enabled); bool vabc_enabled = false; - if (store.GetBoolean("virtual_ab_compression", &vabc_enabled)) { + if (store.GetBoolean("virtual_ab_compression", &vabc_enabled) && + vabc_enabled) { + LOG(INFO) << "Target build supports VABC"; metadata->set_vabc_enabled(vabc_enabled); } - + // We use "gz" compression by default for VABC. + if (metadata->vabc_enabled()) { + metadata->set_vabc_compression_param("gz"); + } dynamic_partition_metadata = std::move(metadata); return true; } diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h index f7d0b6b3..9c8c59f9 100644 --- a/payload_generator/payload_generation_config.h +++ b/payload_generator/payload_generation_config.h @@ -231,11 +231,6 @@ struct PayloadGenerationConfig { // The maximum timestamp of the OS allowed to apply this payload. int64_t max_timestamp = 0; - // Permit use of VABC by default. Even if this is set to true, the device must - // support VABC in order to use it. If this is set to false, device must not - // use VABC regardless. - bool disable_vabc = false; - // Path to apex_info.pb, extracted from target_file.zip std::string apex_info_file; }; diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload index 5b4f959f..746cefba 100755 --- a/scripts/brillo_update_payload +++ b/scripts/brillo_update_payload @@ -708,10 +708,11 @@ cmd_generate() { GENERATOR_ARGS+=( --disable_verity_computation="${FLAGS_disable_verity_computation}" ) fi - if [[ -n "${FLAGS_disable_vabc}" ]]; then - GENERATOR_ARGS+=( - --disable_vabc="${FLAGS_disable_vabc}" ) - fi + fi + + if [[ -n "${FLAGS_disable_vabc}" ]]; then + GENERATOR_ARGS+=( + --disable_vabc="${FLAGS_disable_vabc}" ) fi # minor version is set only for delta or partial payload. diff --git a/scripts/cow_converter.py b/scripts/cow_converter.py new file mode 100644 index 00000000..14e016cb --- /dev/null +++ b/scripts/cow_converter.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +# +# 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. +# + +"""Command-line tool for converting OTA payloads to VABC style COW images.""" + +import os +import sys +import tempfile +import zipfile +import subprocess + + +def IsSparseImage(filepath): + """Determine if an image is a sparse image + Args: + filepath: str, a path to an .img file + + Returns: + return true iff the filepath is a sparse image. + + """ + with open(filepath, 'rb') as fp: + # Magic for android sparse image format + # https://source.android.com/devices/bootloader/images + return fp.read(4) == b'\x3A\xFF\x26\xED' + + +def ConvertCOW(ota_path, target_file_path, tmp_dir, output_dir): + """Convert ota payload to COW IMAGE + Args: + ota_path: str, path to ota.zip + target_file_path: str, path to target_file.zip, + must be the target build for OTA. + tmp_dir: A temp dir as scratch space + output_dir: A directory where all converted COW images will be written. + """ + with zipfile.ZipFile(ota_path) as ota_zip: + payload_path = ota_zip.extract("payload.bin", output_dir) + with zipfile.ZipFile(target_file_path) as zfp: + for fileinfo in zfp.infolist(): + img_name = os.path.basename(fileinfo.filename) + if not fileinfo.filename.endswith(".img"): + continue + if fileinfo.filename.startswith("IMAGES/") or \ + fileinfo.filename.startswith("RADIO/"): + img_path = zfp.extract(fileinfo, tmp_dir) + target_img_path = os.path.join(output_dir, img_name) + if IsSparseImage(img_path): + subprocess.check_call(["simg2img", img_path, target_img_path]) + else: + os.rename(img_path, target_img_path) + print("Extracted", fileinfo.filename, "size:", fileinfo.file_size) + + subprocess.call(["cow_converter", payload_path, + output_dir]) + + +def main(): + if len(sys.argv) != 4: + print( + "Usage:", sys.argv[0], "<your_ota.zip> <target_file.zip> <output dir>") + return 1 + ota_path = sys.argv[1] + target_file_path = sys.argv[2] + output_dir = sys.argv[3] + os.makedirs(output_dir, exist_ok=True) + with tempfile.TemporaryDirectory() as tmp_dir: + ConvertCOW(ota_path, target_file_path, tmp_dir, output_dir) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/update_device.py b/scripts/update_device.py index 354972b7..074b91d6 100755 --- a/scripts/update_device.py +++ b/scripts/update_device.py @@ -29,6 +29,7 @@ import socket import subprocess import sys import struct +import tempfile import threading import xml.etree.ElementTree import zipfile @@ -384,6 +385,28 @@ class AdbHost(object): return subprocess.check_output(command, universal_newlines=True) +def PushMetadata(dut, otafile, metadata_path): + payload = update_payload.Payload(otafile) + payload.Init() + with tempfile.TemporaryDirectory() as tmpdir: + with zipfile.ZipFile(otafile, "r") as zfp: + extracted_path = os.path.join(tmpdir, "payload.bin") + with zfp.open("payload.bin") as payload_fp, \ + open(extracted_path, "wb") as output_fp: + # Only extract the first |data_offset| bytes from the payload. + # This is because allocateSpaceForPayload only needs to see + # the manifest, not the entire payload. + # Extracting the entire payload works, but is slow for full + # OTA. + output_fp.write(payload_fp.read(payload.data_offset)) + + return dut.adb([ + "push", + extracted_path, + metadata_path + ]) == 0 + + def main(): parser = argparse.ArgumentParser(description='Android A/B OTA helper.') parser.add_argument('otafile', metavar='PAYLOAD', type=str, @@ -405,6 +428,13 @@ def main(): help='Update with the secondary payload in the package.') parser.add_argument('--no-slot-switch', action='store_true', help='Do not perform slot switch after the update.') + parser.add_argument('--allocate_only', action='store_true', + help='Allocate space for this OTA, instead of actually \ + applying the OTA.') + parser.add_argument('--verify_only', action='store_true', + help='Verify metadata then exit, instead of applying the OTA.') + parser.add_argument('--no_care_map', action='store_true', + help='Do not push care_map.pb to device.') args = parser.parse_args() logging.basicConfig( level=logging.WARNING if args.no_verbose else logging.INFO) @@ -422,9 +452,38 @@ def main(): help_cmd = ['shell', 'su', '0', 'update_engine_client', '--help'] use_omaha = 'omaha' in dut.adb_output(help_cmd) + metadata_path = "/data/ota_package/metadata" + if args.allocate_only: + if PushMetadata(dut, args.otafile, metadata_path): + dut.adb([ + "shell", "update_engine_client", "--allocate", + "--metadata={}".format(metadata_path)]) + # Return 0, as we are executing ADB commands here, no work needed after + # this point + return 0 + if args.verify_only: + if PushMetadata(dut, args.otafile, metadata_path): + dut.adb([ + "shell", "update_engine_client", "--verify", + "--metadata={}".format(metadata_path)]) + # Return 0, as we are executing ADB commands here, no work needed after + # this point + return 0 + if args.no_slot_switch: args.extra_headers += "\nSWITCH_SLOT_ON_REBOOT=0" + with zipfile.ZipFile(args.otafile) as zfp: + CARE_MAP_ENTRY_NAME = "care_map.pb" + if CARE_MAP_ENTRY_NAME in zfp.namelist() and not args.no_care_map: + # Need root permission to push to /data + dut.adb(["root"]) + with tempfile.NamedTemporaryFile() as care_map_fp: + care_map_fp.write(zfp.read(CARE_MAP_ENTRY_NAME)) + care_map_fp.flush() + dut.adb(["push", care_map_fp.name, + "/data/ota_package/" + CARE_MAP_ENTRY_NAME]) + if args.file: # Update via pushing a file to /data. device_ota_file = os.path.join(OTA_PACKAGE_PATH, 'debug.zip') diff --git a/scripts/update_payload/update_metadata_pb2.py b/scripts/update_payload/update_metadata_pb2.py index 16412297..9aef9f2f 100644 --- a/scripts/update_payload/update_metadata_pb2.py +++ b/scripts/update_payload/update_metadata_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( package='chromeos_update_engine', syntax='proto2', serialized_options=_b('H\003'), - serialized_pb=_b('\n\x15update_metadata.proto\x12\x16\x63hromeos_update_engine\"1\n\x06\x45xtent\x12\x13\n\x0bstart_block\x18\x01 \x01(\x04\x12\x12\n\nnum_blocks\x18\x02 \x01(\x04\"\x9f\x01\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1aO\n\tSignature\x12\x13\n\x07version\x18\x01 \x01(\rB\x02\x18\x01\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x1f\n\x17unpadded_signature_size\x18\x03 \x01(\x07\"+\n\rPartitionInfo\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"\x8f\x01\n\tImageInfo\x12\x11\n\x05\x62oard\x18\x01 \x01(\tB\x02\x18\x01\x12\x0f\n\x03key\x18\x02 \x01(\tB\x02\x18\x01\x12\x13\n\x07\x63hannel\x18\x03 \x01(\tB\x02\x18\x01\x12\x13\n\x07version\x18\x04 \x01(\tB\x02\x18\x01\x12\x19\n\rbuild_channel\x18\x05 \x01(\tB\x02\x18\x01\x12\x19\n\rbuild_version\x18\x06 \x01(\tB\x02\x18\x01\"\xee\x03\n\x10InstallOperation\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.chromeos_update_engine.InstallOperation.Type\x12\x13\n\x0b\x64\x61ta_offset\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x61ta_length\x18\x03 \x01(\x04\x12\x33\n\x0bsrc_extents\x18\x04 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\nsrc_length\x18\x05 \x01(\x04\x12\x33\n\x0b\x64st_extents\x18\x06 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\ndst_length\x18\x07 \x01(\x04\x12\x18\n\x10\x64\x61ta_sha256_hash\x18\x08 \x01(\x0c\x12\x17\n\x0fsrc_sha256_hash\x18\t \x01(\x0c\"\xad\x01\n\x04Type\x12\x0b\n\x07REPLACE\x10\x00\x12\x0e\n\nREPLACE_BZ\x10\x01\x12\x0c\n\x04MOVE\x10\x02\x1a\x02\x08\x01\x12\x0e\n\x06\x42SDIFF\x10\x03\x1a\x02\x08\x01\x12\x0f\n\x0bSOURCE_COPY\x10\x04\x12\x11\n\rSOURCE_BSDIFF\x10\x05\x12\x0e\n\nREPLACE_XZ\x10\x08\x12\x08\n\x04ZERO\x10\x06\x12\x0b\n\x07\x44ISCARD\x10\x07\x12\x11\n\rBROTLI_BSDIFF\x10\n\x12\x0c\n\x08PUFFDIFF\x10\t\"\xcf\x01\n\x11\x43owMergeOperation\x12<\n\x04type\x18\x01 \x01(\x0e\x32..chromeos_update_engine.CowMergeOperation.Type\x12\x32\n\nsrc_extent\x18\x02 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\ndst_extent\x18\x03 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\"\x14\n\x04Type\x12\x0c\n\x08\x43OW_COPY\x10\x00\"\xc8\x06\n\x0fPartitionUpdate\x12\x16\n\x0epartition_name\x18\x01 \x02(\t\x12\x17\n\x0frun_postinstall\x18\x02 \x01(\x08\x12\x18\n\x10postinstall_path\x18\x03 \x01(\t\x12\x17\n\x0f\x66ilesystem_type\x18\x04 \x01(\t\x12M\n\x17new_partition_signature\x18\x05 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x12\x41\n\x12old_partition_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x41\n\x12new_partition_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12<\n\noperations\x18\x08 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x1c\n\x14postinstall_optional\x18\t \x01(\x08\x12=\n\x15hash_tree_data_extent\x18\n \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x38\n\x10hash_tree_extent\x18\x0b \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x1b\n\x13hash_tree_algorithm\x18\x0c \x01(\t\x12\x16\n\x0ehash_tree_salt\x18\r \x01(\x0c\x12\x37\n\x0f\x66\x65\x63_data_extent\x18\x0e \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\nfec_extent\x18\x0f \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x14\n\tfec_roots\x18\x10 \x01(\r:\x01\x32\x12\x0f\n\x07version\x18\x11 \x01(\t\x12\x43\n\x10merge_operations\x18\x12 \x03(\x0b\x32).chromeos_update_engine.CowMergeOperation\x12\x19\n\x11\x65stimate_cow_size\x18\x13 \x01(\x04\"L\n\x15\x44ynamicPartitionGroup\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fpartition_names\x18\x03 \x03(\t\"\x89\x01\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\x12\x18\n\x10snapshot_enabled\x18\x02 \x01(\x08\x12\x14\n\x0cvabc_enabled\x18\x03 \x01(\x08\"c\n\x08\x41pexInfo\x12\x14\n\x0cpackage_name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\x03\x12\x15\n\ris_compressed\x18\x03 \x01(\x08\x12\x19\n\x11\x64\x65\x63ompressed_size\x18\x04 \x01(\x03\"C\n\x0c\x41pexMetadata\x12\x33\n\tapex_info\x18\x01 \x03(\x0b\x32 .chromeos_update_engine.ApexInfo\"\x9e\x07\n\x14\x44\x65ltaArchiveManifest\x12H\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12O\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12\x18\n\nblock_size\x18\x03 \x01(\r:\x04\x34\x30\x39\x36\x12\x19\n\x11signatures_offset\x18\x04 \x01(\x04\x12\x17\n\x0fsignatures_size\x18\x05 \x01(\x04\x12\x42\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12=\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfoB\x02\x18\x01\x12=\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfoB\x02\x18\x01\x12\x18\n\rminor_version\x18\x0c \x01(\r:\x01\x30\x12;\n\npartitions\x18\r \x03(\x0b\x32\'.chromeos_update_engine.PartitionUpdate\x12\x15\n\rmax_timestamp\x18\x0e \x01(\x03\x12T\n\x1a\x64ynamic_partition_metadata\x18\x0f \x01(\x0b\x32\x30.chromeos_update_engine.DynamicPartitionMetadata\x12\x16\n\x0epartial_update\x18\x10 \x01(\x08\x12\x33\n\tapex_info\x18\x11 \x03(\x0b\x32 .chromeos_update_engine.ApexInfoB\x02H\x03') + serialized_pb=_b('\n\x15update_metadata.proto\x12\x16\x63hromeos_update_engine\"1\n\x06\x45xtent\x12\x13\n\x0bstart_block\x18\x01 \x01(\x04\x12\x12\n\nnum_blocks\x18\x02 \x01(\x04\"\x9f\x01\n\nSignatures\x12@\n\nsignatures\x18\x01 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x1aO\n\tSignature\x12\x13\n\x07version\x18\x01 \x01(\rB\x02\x18\x01\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12\x1f\n\x17unpadded_signature_size\x18\x03 \x01(\x07\"+\n\rPartitionInfo\x12\x0c\n\x04size\x18\x01 \x01(\x04\x12\x0c\n\x04hash\x18\x02 \x01(\x0c\"\x8f\x01\n\tImageInfo\x12\x11\n\x05\x62oard\x18\x01 \x01(\tB\x02\x18\x01\x12\x0f\n\x03key\x18\x02 \x01(\tB\x02\x18\x01\x12\x13\n\x07\x63hannel\x18\x03 \x01(\tB\x02\x18\x01\x12\x13\n\x07version\x18\x04 \x01(\tB\x02\x18\x01\x12\x19\n\rbuild_channel\x18\x05 \x01(\tB\x02\x18\x01\x12\x19\n\rbuild_version\x18\x06 \x01(\tB\x02\x18\x01\"\xee\x03\n\x10InstallOperation\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.chromeos_update_engine.InstallOperation.Type\x12\x13\n\x0b\x64\x61ta_offset\x18\x02 \x01(\x04\x12\x13\n\x0b\x64\x61ta_length\x18\x03 \x01(\x04\x12\x33\n\x0bsrc_extents\x18\x04 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\nsrc_length\x18\x05 \x01(\x04\x12\x33\n\x0b\x64st_extents\x18\x06 \x03(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x12\n\ndst_length\x18\x07 \x01(\x04\x12\x18\n\x10\x64\x61ta_sha256_hash\x18\x08 \x01(\x0c\x12\x17\n\x0fsrc_sha256_hash\x18\t \x01(\x0c\"\xad\x01\n\x04Type\x12\x0b\n\x07REPLACE\x10\x00\x12\x0e\n\nREPLACE_BZ\x10\x01\x12\x0c\n\x04MOVE\x10\x02\x1a\x02\x08\x01\x12\x0e\n\x06\x42SDIFF\x10\x03\x1a\x02\x08\x01\x12\x0f\n\x0bSOURCE_COPY\x10\x04\x12\x11\n\rSOURCE_BSDIFF\x10\x05\x12\x0e\n\nREPLACE_XZ\x10\x08\x12\x08\n\x04ZERO\x10\x06\x12\x0b\n\x07\x44ISCARD\x10\x07\x12\x11\n\rBROTLI_BSDIFF\x10\n\x12\x0c\n\x08PUFFDIFF\x10\t\"\xcf\x01\n\x11\x43owMergeOperation\x12<\n\x04type\x18\x01 \x01(\x0e\x32..chromeos_update_engine.CowMergeOperation.Type\x12\x32\n\nsrc_extent\x18\x02 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\ndst_extent\x18\x03 \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\"\x14\n\x04Type\x12\x0c\n\x08\x43OW_COPY\x10\x00\"\xc8\x06\n\x0fPartitionUpdate\x12\x16\n\x0epartition_name\x18\x01 \x02(\t\x12\x17\n\x0frun_postinstall\x18\x02 \x01(\x08\x12\x18\n\x10postinstall_path\x18\x03 \x01(\t\x12\x17\n\x0f\x66ilesystem_type\x18\x04 \x01(\t\x12M\n\x17new_partition_signature\x18\x05 \x03(\x0b\x32,.chromeos_update_engine.Signatures.Signature\x12\x41\n\x12old_partition_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12\x41\n\x12new_partition_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfo\x12<\n\noperations\x18\x08 \x03(\x0b\x32(.chromeos_update_engine.InstallOperation\x12\x1c\n\x14postinstall_optional\x18\t \x01(\x08\x12=\n\x15hash_tree_data_extent\x18\n \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x38\n\x10hash_tree_extent\x18\x0b \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x1b\n\x13hash_tree_algorithm\x18\x0c \x01(\t\x12\x16\n\x0ehash_tree_salt\x18\r \x01(\x0c\x12\x37\n\x0f\x66\x65\x63_data_extent\x18\x0e \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x32\n\nfec_extent\x18\x0f \x01(\x0b\x32\x1e.chromeos_update_engine.Extent\x12\x14\n\tfec_roots\x18\x10 \x01(\r:\x01\x32\x12\x0f\n\x07version\x18\x11 \x01(\t\x12\x43\n\x10merge_operations\x18\x12 \x03(\x0b\x32).chromeos_update_engine.CowMergeOperation\x12\x19\n\x11\x65stimate_cow_size\x18\x13 \x01(\x04\"L\n\x15\x44ynamicPartitionGroup\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0c\n\x04size\x18\x02 \x01(\x04\x12\x17\n\x0fpartition_names\x18\x03 \x03(\t\"\xa9\x01\n\x18\x44ynamicPartitionMetadata\x12=\n\x06groups\x18\x01 \x03(\x0b\x32-.chromeos_update_engine.DynamicPartitionGroup\x12\x18\n\x10snapshot_enabled\x18\x02 \x01(\x08\x12\x14\n\x0cvabc_enabled\x18\x03 \x01(\x08\x12\x1e\n\x16vabc_compression_param\x18\x04 \x01(\t\"c\n\x08\x41pexInfo\x12\x14\n\x0cpackage_name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\x03\x12\x15\n\ris_compressed\x18\x03 \x01(\x08\x12\x19\n\x11\x64\x65\x63ompressed_size\x18\x04 \x01(\x03\"C\n\x0c\x41pexMetadata\x12\x33\n\tapex_info\x18\x01 \x03(\x0b\x32 .chromeos_update_engine.ApexInfo\"\x9e\x07\n\x14\x44\x65ltaArchiveManifest\x12H\n\x12install_operations\x18\x01 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12O\n\x19kernel_install_operations\x18\x02 \x03(\x0b\x32(.chromeos_update_engine.InstallOperationB\x02\x18\x01\x12\x18\n\nblock_size\x18\x03 \x01(\r:\x04\x34\x30\x39\x36\x12\x19\n\x11signatures_offset\x18\x04 \x01(\x04\x12\x17\n\x0fsignatures_size\x18\x05 \x01(\x04\x12\x42\n\x0fold_kernel_info\x18\x06 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_kernel_info\x18\x07 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fold_rootfs_info\x18\x08 \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12\x42\n\x0fnew_rootfs_info\x18\t \x01(\x0b\x32%.chromeos_update_engine.PartitionInfoB\x02\x18\x01\x12=\n\x0eold_image_info\x18\n \x01(\x0b\x32!.chromeos_update_engine.ImageInfoB\x02\x18\x01\x12=\n\x0enew_image_info\x18\x0b \x01(\x0b\x32!.chromeos_update_engine.ImageInfoB\x02\x18\x01\x12\x18\n\rminor_version\x18\x0c \x01(\r:\x01\x30\x12;\n\npartitions\x18\r \x03(\x0b\x32\'.chromeos_update_engine.PartitionUpdate\x12\x15\n\rmax_timestamp\x18\x0e \x01(\x03\x12T\n\x1a\x64ynamic_partition_metadata\x18\x0f \x01(\x0b\x32\x30.chromeos_update_engine.DynamicPartitionMetadata\x12\x16\n\x0epartial_update\x18\x10 \x01(\x08\x12\x33\n\tapex_info\x18\x11 \x03(\x0b\x32 .chromeos_update_engine.ApexInfoB\x02H\x03') ) @@ -683,6 +683,13 @@ _DYNAMICPARTITIONMETADATA = _descriptor.Descriptor( message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='vabc_compression_param', full_name='chromeos_update_engine.DynamicPartitionMetadata.vabc_compression_param', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], @@ -696,7 +703,7 @@ _DYNAMICPARTITIONMETADATA = _descriptor.Descriptor( oneofs=[ ], serialized_start=2082, - serialized_end=2219, + serialized_end=2251, ) @@ -747,8 +754,8 @@ _APEXINFO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2221, - serialized_end=2320, + serialized_start=2253, + serialized_end=2352, ) @@ -778,8 +785,8 @@ _APEXMETADATA = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2322, - serialized_end=2389, + serialized_start=2354, + serialized_end=2421, ) @@ -921,8 +928,8 @@ _DELTAARCHIVEMANIFEST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2392, - serialized_end=3318, + serialized_start=2424, + serialized_end=3350, ) _SIGNATURES_SIGNATURE.containing_type = _SIGNATURES diff --git a/update_metadata.proto b/update_metadata.proto index a1f093c7..bc9e34ac 100644 --- a/update_metadata.proto +++ b/update_metadata.proto @@ -354,6 +354,11 @@ message DynamicPartitionMetadata { // supports it, but not guaranteed. // VABC stands for Virtual AB Compression optional bool vabc_enabled = 3; + + // The compression algorithm used by VABC. Available ones are "gz", "brotli". + // See system/core/fs_mgr/libsnapshot/cow_writer.cpp for available options, + // as this parameter is ultimated forwarded to libsnapshot's CowWriter + optional string vabc_compression_param = 4; } // Definition has been duplicated from |