[automerger skipped] Don't check for FEC offset if fec is disabled am: b5f0996071 -s ours
am skip reason: Merged-In I0e37136313914f1ee9a4eae0e5db59807adc7dc5 with SHA-1 5e5ad39a15 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/update_engine/+/15401790
Change-Id: I0608c32ebce408fff932ce26d838f2d43255c112
diff --git a/.gitignore b/.gitignore
index db4c370..84f29a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@
/update_engine_client
/update_engine_unittests
*.pyc
+.vscode
diff --git a/Android.bp b/Android.bp
index d74e78f..90e7698 100644
--- a/Android.bp
+++ b/Android.bp
@@ -147,6 +147,7 @@
cc_library_static {
name: "update_metadata-protos",
host_supported: true,
+ ramdisk_available: true,
recovery_available: true,
srcs: ["update_engine/update_metadata.proto"],
@@ -232,6 +233,7 @@
"payload_consumer/file_descriptor_utils.cc",
"payload_consumer/file_writer.cc",
"payload_consumer/filesystem_verifier_action.cc",
+ "payload_consumer/install_operation_executor.cc",
"payload_consumer/install_plan.cc",
"payload_consumer/mount_history.cc",
"payload_consumer/payload_constants.cc",
@@ -242,6 +244,7 @@
"payload_consumer/vabc_partition_writer.cc",
"payload_consumer/snapshot_extent_writer.cc",
"payload_consumer/postinstall_runner_action.cc",
+ "payload_consumer/verified_source_fd.cc",
"payload_consumer/verity_writer_android.cc",
"payload_consumer/xz_extent_writer.cc",
"payload_consumer/fec_file_descriptor.cc",
@@ -274,6 +277,7 @@
"libutils",
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
+ "android.hardware.boot@1.2",
],
header_libs: [
"avb_headers",
@@ -805,6 +809,7 @@
"payload_consumer/file_writer_unittest.cc",
"payload_consumer/filesystem_verifier_action_unittest.cc",
"payload_consumer/install_plan_unittest.cc",
+ "payload_consumer/install_operation_executor_unittest.cc",
"payload_consumer/partition_update_generator_android_unittest.cc",
"payload_consumer/postinstall_runner_action_unittest.cc",
"payload_consumer/verity_writer_android_unittest.cc",
diff --git a/aosp/binder_service_android.cc b/aosp/binder_service_android.cc
index ed76c4a..8e87588 100644
--- a/aosp/binder_service_android.cc
+++ b/aosp/binder_service_android.cc
@@ -157,6 +157,17 @@
return Status::ok();
}
+Status BinderUpdateEngineAndroidService::setShouldSwitchSlotOnReboot(
+ const android::String16& metadata_filename) {
+ // TODO(187321613) Call the service_delegate_ for the actual implementation
+ return Status::ok();
+}
+
+Status BinderUpdateEngineAndroidService::resetShouldSwitchSlotOnReboot() {
+ // TODO(187321613) Call the service_delegate_ for the actual implementation
+ return Status::ok();
+}
+
Status BinderUpdateEngineAndroidService::verifyPayloadApplicable(
const android::String16& metadata_filename, bool* return_value) {
const std::string payload_metadata{
diff --git a/aosp/binder_service_android.h b/aosp/binder_service_android.h
index f41fbdf..f1ce6b5 100644
--- a/aosp/binder_service_android.h
+++ b/aosp/binder_service_android.h
@@ -68,6 +68,9 @@
android::binder::Status resume() override;
android::binder::Status cancel() override;
android::binder::Status resetStatus() override;
+ android::binder::Status setShouldSwitchSlotOnReboot(
+ const android::String16& metadata_filename) override;
+ android::binder::Status resetShouldSwitchSlotOnReboot() override;
android::binder::Status verifyPayloadApplicable(
const android::String16& metadata_filename, bool* return_value) override;
android::binder::Status allocateSpaceForPayload(
diff --git a/aosp/boot_control_android.cc b/aosp/boot_control_android.cc
index c1ac0d4..88a9c17 100644
--- a/aosp/boot_control_android.cc
+++ b/aosp/boot_control_android.cc
@@ -20,6 +20,7 @@
#include <utility>
#include <vector>
+#include <android/hardware/boot/1.2/IBootControl.h>
#include <base/bind.h>
#include <base/logging.h>
#include <bootloader_message/bootloader_message.h>
@@ -177,6 +178,18 @@
return ret == BoolResult::TRUE;
}
+Slot BootControlAndroid::GetActiveBootSlot() {
+ namespace V1_2 = android::hardware::boot::V1_2;
+ using android::sp;
+ sp<V1_2::IBootControl> v1_2_module = V1_2::IBootControl::castFrom(module_);
+ if (v1_2_module != nullptr) {
+ return v1_2_module->getActiveBootSlot();
+ }
+ LOG(WARNING) << "BootControl module version is lower than 1.2, "
+ << __FUNCTION__ << " failed";
+ return kInvalidSlot;
+}
+
DynamicPartitionControlInterface*
BootControlAndroid::GetDynamicPartitionControl() {
return dynamic_control_.get();
diff --git a/aosp/boot_control_android.h b/aosp/boot_control_android.h
index 926023a..a65aa2e 100644
--- a/aosp/boot_control_android.h
+++ b/aosp/boot_control_android.h
@@ -62,6 +62,7 @@
bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
bool IsSlotMarkedSuccessful(BootControlInterface::Slot slot) const override;
+ Slot GetActiveBootSlot() override;
DynamicPartitionControlInterface* GetDynamicPartitionControl() override;
private:
diff --git a/aosp/cleanup_previous_update_action.cc b/aosp/cleanup_previous_update_action.cc
index 53cb993..55dba1e 100644
--- a/aosp/cleanup_previous_update_action.cc
+++ b/aosp/cleanup_previous_update_action.cc
@@ -276,7 +276,17 @@
void CleanupPreviousUpdateAction::WaitForMergeOrSchedule() {
AcknowledgeTaskExecuted();
TEST_AND_RETURN(running_);
+
auto update_uses_compression = snapshot_->UpdateUsesCompression();
+
+ // Propagate the merge failure code to the merge stats. If we wait until
+ // after ProcessUpdateState, then a successful merge could overwrite the
+ // state of the previous failure.
+ auto failure_code = snapshot_->ReadMergeFailureCode();
+ if (failure_code != android::snapshot::MergeFailureCode::Ok) {
+ merge_stats_->set_merge_failure_code(failure_code);
+ }
+
auto state = snapshot_->ProcessUpdateState(
std::bind(&CleanupPreviousUpdateAction::OnMergePercentageUpdate, this),
std::bind(&CleanupPreviousUpdateAction::BeforeCancel, this));
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index 4636c43..28c193e 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <map>
#include <memory>
+#include <ostream>
#include <utility>
#include <android-base/properties.h>
@@ -152,14 +153,50 @@
processor_->set_delegate(nullptr);
}
+[[nodiscard]] static bool DidSystemReboot(PrefsInterface* prefs) {
+ string boot_id;
+ TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
+ string old_boot_id;
+ // If no previous boot id found, treat as a reboot and write boot ID.
+ if (!prefs->GetString(kPrefsBootId, &old_boot_id)) {
+ return true;
+ }
+ return old_boot_id != boot_id;
+}
+
+std::ostream& operator<<(std::ostream& out, OTAResult result) {
+ switch (result) {
+ case OTAResult::NOT_ATTEMPTED:
+ out << "OTAResult::NOT_ATTEMPTED";
+ break;
+ case OTAResult::ROLLED_BACK:
+ out << "OTAResult::ROLLED_BACK";
+ break;
+ case OTAResult::UPDATED_NEED_REBOOT:
+ out << "OTAResult::UPDATED_NEED_REBOOT";
+ break;
+ case OTAResult::OTA_SUCCESSFUL:
+ out << "OTAResult::OTA_SUCCESSFUL";
+ break;
+ }
+ return out;
+}
+
void UpdateAttempterAndroid::Init() {
// In case of update_engine restart without a reboot we need to restore the
// reboot needed state.
if (UpdateCompletedOnThisBoot()) {
+ LOG(INFO) << "Updated installed but update_engine is restarted without "
+ "device reboot. Resuming old state.";
SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
} else {
+ const auto result = GetOTAUpdateResult();
+ LOG(INFO) << result;
SetStatusAndNotify(UpdateStatus::IDLE);
- UpdatePrefsAndReportUpdateMetricsOnReboot();
+ if (DidSystemReboot(prefs_)) {
+ UpdateStateAfterReboot(result);
+ }
+
#ifdef _UE_SIDELOAD
LOG(INFO) << "Skip ScheduleCleanupPreviousUpdate in sideload because "
<< "ApplyPayload will call it later.";
@@ -369,6 +406,15 @@
std::vector<ApexInfo> apex_infos_blank;
apex_handler_android_->AllocateSpace(apex_infos_blank);
}
+ // Remove the reboot marker so that if the machine is rebooted
+ // after resetting to idle state, it doesn't go back to
+ // UpdateStatus::UPDATED_NEED_REBOOT state.
+ if (!ClearUpdateCompletedMarker()) {
+ return LogAndSetError(error,
+ FROM_HERE,
+ "Failed to reset the status because "
+ "ClearUpdateCompletedMarker() failed");
+ }
switch (status_) {
case UpdateStatus::IDLE: {
@@ -402,11 +448,6 @@
if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_))
ret_value = false;
- // Remove the reboot marker so that if the machine is rebooted
- // after resetting to idle state, it doesn't go back to
- // UpdateStatus::UPDATED_NEED_REBOOT state.
- if (!prefs_->Delete(kPrefsUpdateCompletedOnBootId))
- ret_value = false;
ClearMetricsPrefs();
if (!ret_value) {
@@ -553,7 +594,9 @@
switch (code) {
case ErrorCode::kSuccess:
// Update succeeded.
- WriteUpdateCompletedMarker();
+ if (!WriteUpdateCompletedMarker()) {
+ LOG(ERROR) << "Failed to write update completion marker";
+ }
prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
LOG(INFO) << "Update successfully applied, waiting to reboot.";
@@ -691,6 +734,7 @@
}
if (status_ == UpdateStatus::CLEANUP_PREVIOUS_UPDATE) {
+ ClearUpdateCompletedMarker();
LOG(INFO) << "Terminating cleanup previous update.";
SetStatusAndNotify(UpdateStatus::IDLE);
for (auto observer : daemon_state_->service_observers())
@@ -784,9 +828,20 @@
}
bool UpdateAttempterAndroid::WriteUpdateCompletedMarker() {
+ LOG(INFO) << "Writing update complete marker.";
string boot_id;
TEST_AND_RETURN_FALSE(utils::GetBootId(&boot_id));
- prefs_->SetString(kPrefsUpdateCompletedOnBootId, boot_id);
+ TEST_AND_RETURN_FALSE(
+ prefs_->SetString(kPrefsUpdateCompletedOnBootId, boot_id));
+ TEST_AND_RETURN_FALSE(
+ prefs_->SetInt64(kPrefsPreviousSlot, boot_control_->GetCurrentSlot()));
+ return true;
+}
+
+bool UpdateAttempterAndroid::ClearUpdateCompletedMarker() {
+ LOG(INFO) << "Clearing update complete marker.";
+ TEST_AND_RETURN_FALSE(prefs_->Delete(kPrefsUpdateCompletedOnBootId));
+ TEST_AND_RETURN_FALSE(prefs_->Delete(kPrefsPreviousSlot));
return true;
}
@@ -883,53 +938,105 @@
}
}
-void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() {
- string current_boot_id;
- TEST_AND_RETURN(utils::GetBootId(¤t_boot_id));
+bool UpdateAttempterAndroid::OTARebootSucceeded() const {
+ const auto current_slot = boot_control_->GetCurrentSlot();
+ const string current_version =
+ android::base::GetProperty("ro.build.version.incremental", "");
+ int64_t previous_slot = -1;
+ TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsPreviousSlot, &previous_slot));
+ string previous_version;
+ TEST_AND_RETURN_FALSE(
+ prefs_->GetString(kPrefsPreviousVersion, &previous_version));
+ if (previous_slot != current_slot) {
+ LOG(INFO) << "Detected a slot switch, OTA succeeded, device updated from "
+ << previous_version << " to " << current_version;
+ if (previous_version == current_version) {
+ LOG(INFO) << "Previous version is the same as current version, this is "
+ "possibly a self-OTA.";
+ }
+ return true;
+ } else {
+ LOG(INFO) << "Slot didn't switch, either the OTA is rolled back, or slot "
+ "switch never happened, or system not rebooted at all.";
+ if (previous_version != current_version) {
+ LOG(INFO) << "Slot didn't change, but version changed from "
+ << previous_version << " to " << current_version
+ << " device could be flashed.";
+ }
+ return false;
+ }
+}
+
+OTAResult UpdateAttempterAndroid::GetOTAUpdateResult() const {
+ // We only set |kPrefsSystemUpdatedMarker| if slot is actually switched, so
+ // existence of this pref is sufficient indicator. Given that we have to
+ // delete this pref after checking it. This is done in
+ // |DeltaPerformer::ResetUpdateProgress|
+ auto slot_switch_attempted = prefs_->Exists(kPrefsUpdateCompletedOnBootId);
+ auto system_rebooted = DidSystemReboot(prefs_);
+ auto ota_successful = OTARebootSucceeded();
+ if (ota_successful) {
+ return OTAResult::OTA_SUCCESSFUL;
+ }
+ if (slot_switch_attempted) {
+ if (system_rebooted) {
+ // If we attempted slot switch, but still end up on the same slot, we
+ // probably rolled back.
+ return OTAResult::ROLLED_BACK;
+ } else {
+ return OTAResult::UPDATED_NEED_REBOOT;
+ }
+ }
+ return OTAResult::NOT_ATTEMPTED;
+}
+
+void UpdateAttempterAndroid::UpdateStateAfterReboot(const OTAResult result) {
// Example: [ro.build.version.incremental]: [4292972]
string current_version =
android::base::GetProperty("ro.build.version.incremental", "");
TEST_AND_RETURN(!current_version.empty());
- const auto current_slot = boot_control_->GetCurrentSlot();
+
+ // |UpdateStateAfterReboot()| is only called after system reboot, so record
+ // boot id unconditionally
+ string current_boot_id;
+ TEST_AND_RETURN(utils::GetBootId(¤t_boot_id));
+ prefs_->SetString(kPrefsBootId, current_boot_id);
// If there's no record of previous version (e.g. due to a data wipe), we
// save the info of current boot and skip the metrics report.
if (!prefs_->Exists(kPrefsPreviousVersion)) {
- prefs_->SetString(kPrefsBootId, current_boot_id);
prefs_->SetString(kPrefsPreviousVersion, current_version);
- prefs_->SetInt64(std::string{kPrefsPreviousSlot},
- boot_control_->GetCurrentSlot());
+ prefs_->SetInt64(kPrefsPreviousSlot, boot_control_->GetCurrentSlot());
ClearMetricsPrefs();
return;
}
- int64_t previous_slot = -1;
- prefs_->GetInt64(kPrefsPreviousSlot, &previous_slot);
- string previous_version;
// update_engine restarted under the same build and same slot.
- // TODO(xunchang) identify and report rollback by checking UpdateMarker.
- if (prefs_->GetString(kPrefsPreviousVersion, &previous_version) &&
- previous_version == current_version && previous_slot == current_slot) {
- string last_boot_id;
- bool is_reboot = prefs_->Exists(kPrefsBootId) &&
- (prefs_->GetString(kPrefsBootId, &last_boot_id) &&
- last_boot_id != current_boot_id);
+ if (result != OTAResult::OTA_SUCCESSFUL) {
// Increment the reboot number if |kPrefsNumReboots| exists. That pref is
// set when we start a new update.
- if (is_reboot && prefs_->Exists(kPrefsNumReboots)) {
- prefs_->SetString(kPrefsBootId, current_boot_id);
+ if (prefs_->Exists(kPrefsNumReboots)) {
int64_t reboot_count =
metrics_utils::GetPersistedValue(kPrefsNumReboots, prefs_);
metrics_utils::SetNumReboots(reboot_count + 1, prefs_);
}
+
+ if (result == OTAResult::ROLLED_BACK) {
+ // This will release all space previously allocated for apex
+ // decompression. If we detect a rollback, we should release space and
+ // return the space to user. Any subsequent attempt to install OTA will
+ // allocate space again anyway.
+ LOG(INFO) << "Detected a rollback, releasing space allocated for apex "
+ "deompression.";
+ apex_handler_android_->AllocateSpace({});
+ DeltaPerformer::ResetUpdateProgress(prefs_, false);
+ }
return;
}
// Now that the build version changes, report the update metrics.
// TODO(xunchang) check the build version is larger than the previous one.
- prefs_->SetString(kPrefsBootId, current_boot_id);
prefs_->SetString(kPrefsPreviousVersion, current_version);
- prefs_->SetInt64(std::string{kPrefsPreviousSlot},
- boot_control_->GetCurrentSlot());
+ prefs_->SetInt64(kPrefsPreviousSlot, boot_control_->GetCurrentSlot());
bool previous_attempt_exists = prefs_->Exists(kPrefsPayloadAttemptNumber);
// |kPrefsPayloadAttemptNumber| should be cleared upon successful update.
@@ -960,6 +1067,7 @@
}
metrics_utils::SetUpdateTimestampStart(clock_->GetMonotonicTime(), prefs_);
metrics_utils::SetUpdateBootTimestampStart(clock_->GetBootTime(), prefs_);
+ ClearUpdateCompletedMarker();
}
void UpdateAttempterAndroid::ClearMetricsPrefs() {
diff --git a/aosp/update_attempter_android.h b/aosp/update_attempter_android.h
index 70938bc..3633178 100644
--- a/aosp/update_attempter_android.h
+++ b/aosp/update_attempter_android.h
@@ -45,6 +45,13 @@
namespace chromeos_update_engine {
+enum class OTAResult {
+ NOT_ATTEMPTED,
+ ROLLED_BACK,
+ UPDATED_NEED_REBOOT,
+ OTA_SUCCESSFUL,
+};
+
class UpdateAttempterAndroid
: public ServiceDelegateAndroidInterface,
public ActionProcessorDelegate,
@@ -114,9 +121,24 @@
// CleanupPreviousUpdateActionDelegateInterface
void OnCleanupProgressUpdate(double progress) override;
+ // Check the result of an OTA update. Intended to be called after reboot, this
+ // will use prefs on disk to determine if OTA was installed, or rolledback.
+ [[nodiscard]] OTAResult GetOTAUpdateResult() const;
+ // Intended to be called:
+ // 1. When system rebooted and slot switch is attempted
+ // 2. When a new update is started
+ // 3. When user called |ResetStatus()|
+ bool ClearUpdateCompletedMarker();
+
private:
friend class UpdateAttempterAndroidTest;
+ // Return |true| only if slot switched successfully after an OTA reboot.
+ // This will return |false| if an downgrade OTA is applied. Because after a
+ // downgrade OTA, we wipe /data, and there's no way for update_engine to
+ // "remember" that a downgrade OTA took place.
+ [[nodiscard]] bool OTARebootSucceeded() const;
+
// Schedules an event loop callback to start the action processor. This is
// scheduled asynchronously to unblock the event loop.
void ScheduleProcessingStart();
@@ -136,10 +158,10 @@
// Writes to the processing completed marker. Does nothing if
// |update_completed_marker_| is empty.
- bool WriteUpdateCompletedMarker();
+ [[nodiscard]] bool WriteUpdateCompletedMarker();
// Returns whether an update was completed in the current boot.
- bool UpdateCompletedOnThisBoot();
+ [[nodiscard]] bool UpdateCompletedOnThisBoot();
// Prefs to use for metrics report
// |kPrefsPayloadAttemptNumber|: number of update attempts for the current
@@ -162,12 +184,16 @@
// |kPrefsSystemUpdatedMarker|
void CollectAndReportUpdateMetricsOnUpdateFinished(ErrorCode error_code);
+ // This function is called after update_engine is started after device
+ // reboots. If update_engine is restarted w/o device reboot, this function
+ // would not be called.
+
// Metrics report function to call:
// |ReportAbnormallyTerminatedUpdateAttemptMetrics|
// |ReportTimeToRebootMetrics|
// Prefs to update:
// |kPrefsBootId|, |kPrefsPreviousVersion|
- void UpdatePrefsAndReportUpdateMetricsOnReboot();
+ void UpdateStateAfterReboot(OTAResult result);
// Prefs to update:
// |kPrefsPayloadAttemptNumber|, |kPrefsUpdateTimestampStart|,
diff --git a/aosp/update_attempter_android_unittest.cc b/aosp/update_attempter_android_unittest.cc
index f73df16..969f191 100644
--- a/aosp/update_attempter_android_unittest.cc
+++ b/aosp/update_attempter_android_unittest.cc
@@ -106,6 +106,7 @@
prefs_.SetString(kPrefsPreviousVersion, "00001"); // Set the fake version
prefs_.SetInt64(kPrefsPayloadAttemptNumber, 1);
prefs_.SetInt64(kPrefsSystemUpdatedMarker, 23456);
+ prefs_.SetInt64(kPrefsPreviousSlot, 1);
EXPECT_CALL(*metrics_reporter_,
ReportAbnormallyTerminatedUpdateAttemptMetrics())
diff --git a/binder_bindings/android/os/IUpdateEngine.aidl b/binder_bindings/android/os/IUpdateEngine.aidl
index c9580da..4043b1a 100644
--- a/binder_bindings/android/os/IUpdateEngine.aidl
+++ b/binder_bindings/android/os/IUpdateEngine.aidl
@@ -44,6 +44,11 @@
/** @hide */
void resetStatus();
/** @hide */
+ void setShouldSwitchSlotOnReboot(in String metadataFilename);
+ /** @hide */
+ void resetShouldSwitchSlotOnReboot();
+
+ /** @hide */
boolean verifyPayloadApplicable(in String metadataFilename);
/**
* Allocate space on userdata partition.
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index 321174e..2de21a1 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -93,6 +93,11 @@
// bootloader will attempt to load the |slot| marked as active. Note that this
// method doesn't change the value of GetCurrentSlot() on the current boot.
virtual bool SetActiveBootSlot(Slot slot) = 0;
+ // Get the active slot. In other words, the slot which will be used on
+ // next system reboot. This should match the |slot| parameter of last
+ // successful call to |SetActiveBootSlot|.
+ // Return 0xFFFFFFFF if underlying HAL doesn't support this operation.
+ virtual Slot GetActiveBootSlot() = 0;
// Mark the current slot as successfully booted asynchronously. No other slot
// flags are modified. Returns false if it was not able to schedule the
diff --git a/common/boot_control_stub.h b/common/boot_control_stub.h
index dcddbae..3167115 100644
--- a/common/boot_control_stub.h
+++ b/common/boot_control_stub.h
@@ -56,6 +56,7 @@
bool IsSlotBootable(BootControlInterface::Slot slot) const override;
bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
+ Slot GetActiveBootSlot() override { return kInvalidSlot; }
bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override;
bool IsSlotMarkedSuccessful(BootControlInterface::Slot slot) const override;
DynamicPartitionControlInterface* GetDynamicPartitionControl() override;
diff --git a/common/constants.cc b/common/constants.cc
index 0677e66..ff46755 100644
--- a/common/constants.cc
+++ b/common/constants.cc
@@ -16,133 +16,4 @@
#include "update_engine/common/constants.h"
-namespace chromeos_update_engine {
-
-const char kExclusionPrefsSubDir[] = "exclusion";
-
-const char kDlcPrefsSubDir[] = "dlc";
-
-const char kPowerwashSafePrefsSubDirectory[] = "update_engine/prefs";
-
-const char kPrefsSubDirectory[] = "prefs";
-
-const char kStatefulPartition[] = "/mnt/stateful_partition";
-
-const char kPostinstallDefaultScript[] = "postinst";
-
-// Constants defining keys for the persisted state of update engine.
-const char kPrefsAttemptInProgress[] = "attempt-in-progress";
-const char kPrefsBackoffExpiryTime[] = "backoff-expiry-time";
-const char kPrefsBootId[] = "boot-id";
-const char kPrefsCurrentBytesDownloaded[] = "current-bytes-downloaded";
-const char kPrefsCurrentResponseSignature[] = "current-response-signature";
-const char kPrefsCurrentUrlFailureCount[] = "current-url-failure-count";
-const char kPrefsCurrentUrlIndex[] = "current-url-index";
-const char kPrefsDailyMetricsLastReportedAt[] =
- "daily-metrics-last-reported-at";
-const char kPrefsDeltaUpdateFailures[] = "delta-update-failures";
-const char kPrefsDynamicPartitionMetadataUpdated[] =
- "dynamic-partition-metadata-updated";
-const char kPrefsFullPayloadAttemptNumber[] = "full-payload-attempt-number";
-const char kPrefsInstallDateDays[] = "install-date-days";
-const char kPrefsLastActivePingDay[] = "last-active-ping-day";
-const char kPrefsLastRollCallPingDay[] = "last-roll-call-ping-day";
-const char kPrefsManifestMetadataSize[] = "manifest-metadata-size";
-const char kPrefsManifestSignatureSize[] = "manifest-signature-size";
-const char kPrefsMetricsAttemptLastReportingTime[] =
- "metrics-attempt-last-reporting-time";
-const char kPrefsMetricsCheckLastReportingTime[] =
- "metrics-check-last-reporting-time";
-const char kPrefsNoIgnoreBackoff[] = "no-ignore-backoff";
-const char kPrefsNumReboots[] = "num-reboots";
-const char kPrefsNumResponsesSeen[] = "num-responses-seen";
-const char kPrefsOmahaCohort[] = "omaha-cohort";
-const char kPrefsOmahaCohortHint[] = "omaha-cohort-hint";
-const char kPrefsOmahaCohortName[] = "omaha-cohort-name";
-const char kPrefsOmahaEolDate[] = "omaha-eol-date";
-const char kPrefsP2PEnabled[] = "p2p-enabled";
-const char kPrefsP2PFirstAttemptTimestamp[] = "p2p-first-attempt-timestamp";
-const char kPrefsP2PNumAttempts[] = "p2p-num-attempts";
-const char kPrefsPayloadAttemptNumber[] = "payload-attempt-number";
-const char kPrefsTestUpdateCheckIntervalTimeout[] =
- "test-update-check-interval-timeout";
-// Keep |kPrefsPingActive| in sync with |kDlcMetadataFilePingActive| in
-// dlcservice.
-const char kPrefsPingActive[] = "active";
-const char kPrefsPingLastActive[] = "date_last_active";
-const char kPrefsPingLastRollcall[] = "date_last_rollcall";
-const char kPrefsLastFp[] = "last-fp";
-const char kPrefsPostInstallSucceeded[] = "post-install-succeeded";
-const char kPrefsPreviousVersion[] = "previous-version";
-const char kPrefsResumedUpdateFailures[] = "resumed-update-failures";
-const char kPrefsRollbackHappened[] = "rollback-happened";
-const char kPrefsRollbackVersion[] = "rollback-version";
-const char kPrefsChannelOnSlotPrefix[] = "channel-on-slot-";
-const char kPrefsSystemUpdatedMarker[] = "system-updated-marker";
-const char kPrefsTargetVersionAttempt[] = "target-version-attempt";
-const char kPrefsTargetVersionInstalledFrom[] = "target-version-installed-from";
-const char kPrefsTargetVersionUniqueId[] = "target-version-unique-id";
-const char kPrefsTotalBytesDownloaded[] = "total-bytes-downloaded";
-const char kPrefsUpdateCheckCount[] = "update-check-count";
-const char kPrefsUpdateCheckResponseHash[] = "update-check-response-hash";
-const char kPrefsUpdateCompletedBootTime[] = "update-completed-boot-time";
-const char kPrefsUpdateCompletedOnBootId[] = "update-completed-on-boot-id";
-const char kPrefsUpdateDurationUptime[] = "update-duration-uptime";
-const char kPrefsUpdateFirstSeenAt[] = "update-first-seen-at";
-const char kPrefsUpdateOverCellularPermission[] =
- "update-over-cellular-permission";
-const char kPrefsUpdateOverCellularTargetVersion[] =
- "update-over-cellular-target-version";
-const char kPrefsUpdateOverCellularTargetSize[] =
- "update-over-cellular-target-size";
-const char kPrefsUpdateServerCertificate[] = "update-server-cert";
-const char kPrefsUpdateStateNextDataLength[] = "update-state-next-data-length";
-const char kPrefsUpdateStateNextDataOffset[] = "update-state-next-data-offset";
-const char kPrefsUpdateStateNextOperation[] = "update-state-next-operation";
-const char kPrefsUpdateStatePayloadIndex[] = "update-state-payload-index";
-const char kPrefsUpdateStateSHA256Context[] = "update-state-sha-256-context";
-const char kPrefsUpdateStateSignatureBlob[] = "update-state-signature-blob";
-const char kPrefsUpdateStateSignedSHA256Context[] =
- "update-state-signed-sha-256-context";
-const char kPrefsUpdateBootTimestampStart[] = "update-boot-timestamp-start";
-const char kPrefsUpdateTimestampStart[] = "update-timestamp-start";
-const char kPrefsUrlSwitchCount[] = "url-switch-count";
-const char kPrefsVerityWritten[] = "verity-written";
-const char kPrefsWallClockScatteringWaitPeriod[] = "wall-clock-wait-period";
-const char kPrefsWallClockStagingWaitPeriod[] =
- "wall-clock-staging-wait-period";
-const char kPrefsManifestBytes[] = "manifest-bytes";
-const char kPrefsPreviousSlot[] = "previous-slot";
-
-// These four fields are generated by scripts/brillo_update_payload.
-const char kPayloadPropertyFileSize[] = "FILE_SIZE";
-const char kPayloadPropertyFileHash[] = "FILE_HASH";
-const char kPayloadPropertyMetadataSize[] = "METADATA_SIZE";
-const char kPayloadPropertyMetadataHash[] = "METADATA_HASH";
-// The Authorization: HTTP header to be sent when downloading the payload.
-const char kPayloadPropertyAuthorization[] = "AUTHORIZATION";
-// The User-Agent HTTP header to be sent when downloading the payload.
-const char kPayloadPropertyUserAgent[] = "USER_AGENT";
-// Set "POWERWASH=1" to powerwash (factory data reset) the device after
-// applying the update.
-const char kPayloadPropertyPowerwash[] = "POWERWASH";
-// The network id to pass to android_setprocnetwork before downloading.
-// This can be used to zero-rate OTA traffic by sending it over the correct
-// network.
-const char kPayloadPropertyNetworkId[] = "NETWORK_ID";
-// Set "SWITCH_SLOT_ON_REBOOT=0" to skip marking the updated partitions active.
-// The default is 1 (always switch slot if update succeeded).
-const char kPayloadPropertySwitchSlotOnReboot[] = "SWITCH_SLOT_ON_REBOOT";
-// Set "RUN_POST_INSTALL=0" to skip running optional post install.
-// The default is 1 (always run post install).
-const char kPayloadPropertyRunPostInstall[] = "RUN_POST_INSTALL";
-
-const char kOmahaUpdaterVersion[] = "0.1.0.0";
-
-// X-Goog-Update headers.
-const char kXGoogleUpdateInteractivity[] = "X-Goog-Update-Interactivity";
-const char kXGoogleUpdateAppId[] = "X-Goog-Update-AppId";
-const char kXGoogleUpdateUpdater[] = "X-Goog-Update-Updater";
-const char kXGoogleUpdateSessionId[] = "X-Goog-SessionId";
-
-} // namespace chromeos_update_engine
+namespace chromeos_update_engine {} // namespace chromeos_update_engine
diff --git a/common/constants.h b/common/constants.h
index 68f720d..8c07fcf 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -20,116 +20,176 @@
#include <cstdint>
namespace chromeos_update_engine {
-
// The root path of all exclusion prefs.
-extern const char kExclusionPrefsSubDir[];
+static constexpr const auto& kExclusionPrefsSubDir = "exclusion";
// The root path of all DLC metadata.
-extern const char kDlcPrefsSubDir[];
+static constexpr const auto& kDlcPrefsSubDir = "dlc";
// Directory for AU prefs that are preserved across powerwash.
-extern const char kPowerwashSafePrefsSubDirectory[];
+static constexpr const auto& kPowerwashSafePrefsSubDirectory =
+ "update_engine/prefs";
// The location where we store the AU preferences (state etc).
-extern const char kPrefsSubDirectory[];
-
-// Path to the post install command, relative to the partition.
-extern const char kPostinstallDefaultScript[];
+static constexpr const auto& kPrefsSubDirectory = "prefs";
// Path to the stateful partition on the root filesystem.
-extern const char kStatefulPartition[];
+static constexpr const auto& kStatefulPartition = "/mnt/stateful_partition";
+
+// Path to the post install command, relative to the partition.
+static constexpr const auto& kPostinstallDefaultScript = "postinst";
// Constants related to preferences.
-extern const char kPrefsAttemptInProgress[];
-extern const char kPrefsBackoffExpiryTime[];
-extern const char kPrefsBootId[];
-extern const char kPrefsCurrentBytesDownloaded[];
-extern const char kPrefsCurrentResponseSignature[];
-extern const char kPrefsCurrentUrlFailureCount[];
-extern const char kPrefsCurrentUrlIndex[];
-extern const char kPrefsDailyMetricsLastReportedAt[];
-extern const char kPrefsDeltaUpdateFailures[];
-extern const char kPrefsDynamicPartitionMetadataUpdated[];
-extern const char kPrefsFullPayloadAttemptNumber[];
-extern const char kPrefsInstallDateDays[];
-extern const char kPrefsLastActivePingDay[];
-extern const char kPrefsLastRollCallPingDay[];
-extern const char kPrefsManifestMetadataSize[];
-extern const char kPrefsManifestSignatureSize[];
-extern const char kPrefsMetricsAttemptLastReportingTime[];
-extern const char kPrefsMetricsCheckLastReportingTime[];
-extern const char kPrefsNoIgnoreBackoff[];
-extern const char kPrefsNumReboots[];
-extern const char kPrefsNumResponsesSeen[];
-extern const char kPrefsOmahaCohort[];
-extern const char kPrefsOmahaCohortHint[];
-extern const char kPrefsOmahaCohortName[];
-extern const char kPrefsOmahaEolDate[];
-extern const char kPrefsP2PEnabled[];
-extern const char kPrefsP2PFirstAttemptTimestamp[];
-extern const char kPrefsP2PNumAttempts[];
-extern const char kPrefsPayloadAttemptNumber[];
-extern const char kPrefsTestUpdateCheckIntervalTimeout[];
-extern const char kPrefsPingActive[];
-extern const char kPrefsPingLastActive[];
-extern const char kPrefsPingLastRollcall[];
-extern const char kPrefsLastFp[];
-extern const char kPrefsPostInstallSucceeded[];
-extern const char kPrefsPreviousVersion[];
-extern const char kPrefsPreviousSlot[];
-extern const char kPrefsResumedUpdateFailures[];
-extern const char kPrefsRollbackHappened[];
-extern const char kPrefsRollbackVersion[];
-extern const char kPrefsChannelOnSlotPrefix[];
-extern const char kPrefsSystemUpdatedMarker[];
-extern const char kPrefsTargetVersionAttempt[];
-extern const char kPrefsTargetVersionInstalledFrom[];
-extern const char kPrefsTargetVersionUniqueId[];
-extern const char kPrefsTotalBytesDownloaded[];
-extern const char kPrefsUpdateCheckCount[];
-extern const char kPrefsUpdateCheckResponseHash[];
-extern const char kPrefsUpdateCompletedBootTime[];
-extern const char kPrefsUpdateCompletedOnBootId[];
-extern const char kPrefsUpdateDurationUptime[];
-extern const char kPrefsUpdateFirstSeenAt[];
-extern const char kPrefsUpdateOverCellularPermission[];
-extern const char kPrefsUpdateOverCellularTargetVersion[];
-extern const char kPrefsUpdateOverCellularTargetSize[];
-extern const char kPrefsUpdateServerCertificate[];
-extern const char kPrefsUpdateStateNextDataLength[];
-extern const char kPrefsUpdateStateNextDataOffset[];
-extern const char kPrefsUpdateStateNextOperation[];
-extern const char kPrefsUpdateStatePayloadIndex[];
-extern const char kPrefsUpdateStateSHA256Context[];
-extern const char kPrefsUpdateStateSignatureBlob[];
-extern const char kPrefsUpdateStateSignedSHA256Context[];
-extern const char kPrefsUpdateBootTimestampStart[];
-extern const char kPrefsUpdateTimestampStart[];
-extern const char kPrefsUrlSwitchCount[];
-extern const char kPrefsVerityWritten[];
-extern const char kPrefsWallClockScatteringWaitPeriod[];
-extern const char kPrefsWallClockStagingWaitPeriod[];
-extern const char kPrefsManifestBytes[];
+// Constants defining keys for the persisted state of update engine.
+static constexpr const auto& kPrefsAttemptInProgress = "attempt-in-progress";
+static constexpr const auto& kPrefsBackoffExpiryTime = "backoff-expiry-time";
+static constexpr const auto& kPrefsBootId = "boot-id";
+static constexpr const auto& kPrefsCurrentBytesDownloaded =
+ "current-bytes-downloaded";
+static constexpr const auto& kPrefsCurrentResponseSignature =
+ "current-response-signature";
+static constexpr const auto& kPrefsCurrentUrlFailureCount =
+ "current-url-failure-count";
+static constexpr const auto& kPrefsCurrentUrlIndex = "current-url-index";
+static constexpr const auto& kPrefsDailyMetricsLastReportedAt =
+ "daily-metrics-last-reported-at";
+static constexpr const auto& kPrefsDeltaUpdateFailures =
+ "delta-update-failures";
+static constexpr const auto& kPrefsDynamicPartitionMetadataUpdated =
+ "dynamic-partition-metadata-updated";
+static constexpr const auto& kPrefsFullPayloadAttemptNumber =
+ "full-payload-attempt-number";
+static constexpr const auto& kPrefsInstallDateDays = "install-date-days";
+static constexpr const auto& kPrefsLastActivePingDay = "last-active-ping-day";
+static constexpr const auto& kPrefsLastRollCallPingDay =
+ "last-roll-call-ping-day";
+static constexpr const auto& kPrefsManifestMetadataSize =
+ "manifest-metadata-size";
+static constexpr const auto& kPrefsManifestSignatureSize =
+ "manifest-signature-size";
+static constexpr const auto& kPrefsMetricsAttemptLastReportingTime =
+ "metrics-attempt-last-reporting-time";
+static constexpr const auto& kPrefsMetricsCheckLastReportingTime =
+ "metrics-check-last-reporting-time";
+static constexpr const auto& kPrefsNoIgnoreBackoff = "no-ignore-backoff";
+static constexpr const auto& kPrefsNumReboots = "num-reboots";
+static constexpr const auto& kPrefsNumResponsesSeen = "num-responses-seen";
+static constexpr const auto& kPrefsOmahaCohort = "omaha-cohort";
+static constexpr const auto& kPrefsOmahaCohortHint = "omaha-cohort-hint";
+static constexpr const auto& kPrefsOmahaCohortName = "omaha-cohort-name";
+static constexpr const auto& kPrefsOmahaEolDate = "omaha-eol-date";
+static constexpr const auto& kPrefsP2PEnabled = "p2p-enabled";
+static constexpr const auto& kPrefsP2PFirstAttemptTimestamp =
+ "p2p-first-attempt-timestamp";
+static constexpr const auto& kPrefsP2PNumAttempts = "p2p-num-attempts";
+static constexpr const auto& kPrefsPayloadAttemptNumber =
+ "payload-attempt-number";
+static constexpr const auto& kPrefsTestUpdateCheckIntervalTimeout =
+ "test-update-check-interval-timeout";
+// Keep |kPrefsPingActive| in sync with |kDlcMetadataFilePingActive| in
+// dlcservice.
+static constexpr const auto& kPrefsPingActive = "active";
+static constexpr const auto& kPrefsPingLastActive = "date_last_active";
+static constexpr const auto& kPrefsPingLastRollcall = "date_last_rollcall";
+static constexpr const auto& kPrefsLastFp = "last-fp";
+static constexpr const auto& kPrefsPostInstallSucceeded =
+ "post-install-succeeded";
+static constexpr const auto& kPrefsPreviousVersion = "previous-version";
+static constexpr const auto& kPrefsResumedUpdateFailures =
+ "resumed-update-failures";
+static constexpr const auto& kPrefsRollbackHappened = "rollback-happened";
+static constexpr const auto& kPrefsRollbackVersion = "rollback-version";
+static constexpr const auto& kPrefsChannelOnSlotPrefix = "channel-on-slot-";
+static constexpr const auto& kPrefsSystemUpdatedMarker =
+ "system-updated-marker";
+static constexpr const auto& kPrefsTargetVersionAttempt =
+ "target-version-attempt";
+static constexpr const auto& kPrefsTargetVersionInstalledFrom =
+ "target-version-installed-from";
+static constexpr const auto& kPrefsTargetVersionUniqueId =
+ "target-version-unique-id";
+static constexpr const auto& kPrefsTotalBytesDownloaded =
+ "total-bytes-downloaded";
+static constexpr const auto& kPrefsUpdateCheckCount = "update-check-count";
+static constexpr const auto& kPrefsUpdateCheckResponseHash =
+ "update-check-response-hash";
+static constexpr const auto& kPrefsUpdateCompletedBootTime =
+ "update-completed-boot-time";
+static constexpr const auto& kPrefsUpdateCompletedOnBootId =
+ "update-completed-on-boot-id";
+static constexpr const auto& kPrefsUpdateDurationUptime =
+ "update-duration-uptime";
+static constexpr const auto& kPrefsUpdateFirstSeenAt = "update-first-seen-at";
+static constexpr const auto& kPrefsUpdateOverCellularPermission =
+ "update-over-cellular-permission";
+static constexpr const auto& kPrefsUpdateOverCellularTargetVersion =
+ "update-over-cellular-target-version";
+static constexpr const auto& kPrefsUpdateOverCellularTargetSize =
+ "update-over-cellular-target-size";
+static constexpr const auto& kPrefsUpdateServerCertificate =
+ "update-server-cert";
+static constexpr const auto& kPrefsUpdateStateNextDataLength =
+ "update-state-next-data-length";
+static constexpr const auto& kPrefsUpdateStateNextDataOffset =
+ "update-state-next-data-offset";
+static constexpr const auto& kPrefsUpdateStateNextOperation =
+ "update-state-next-operation";
+static constexpr const auto& kPrefsUpdateStatePayloadIndex =
+ "update-state-payload-index";
+static constexpr const auto& kPrefsUpdateStateSHA256Context =
+ "update-state-sha-256-context";
+static constexpr const auto& kPrefsUpdateStateSignatureBlob =
+ "update-state-signature-blob";
+static constexpr const auto& kPrefsUpdateStateSignedSHA256Context =
+ "update-state-signed-sha-256-context";
+static constexpr const auto& kPrefsUpdateBootTimestampStart =
+ "update-boot-timestamp-start";
+static constexpr const auto& kPrefsUpdateTimestampStart =
+ "update-timestamp-start";
+static constexpr const auto& kPrefsUrlSwitchCount = "url-switch-count";
+static constexpr const auto& kPrefsVerityWritten = "verity-written";
+static constexpr const auto& kPrefsWallClockScatteringWaitPeriod =
+ "wall-clock-wait-period";
+static constexpr const auto& kPrefsWallClockStagingWaitPeriod =
+ "wall-clock-staging-wait-period";
+static constexpr const auto& kPrefsManifestBytes = "manifest-bytes";
+static constexpr const auto& kPrefsPreviousSlot = "previous-slot";
// Keys used when storing and loading payload properties.
-extern const char kPayloadPropertyFileSize[];
-extern const char kPayloadPropertyFileHash[];
-extern const char kPayloadPropertyMetadataSize[];
-extern const char kPayloadPropertyMetadataHash[];
-extern const char kPayloadPropertyAuthorization[];
-extern const char kPayloadPropertyUserAgent[];
-extern const char kPayloadPropertyPowerwash[];
-extern const char kPayloadPropertyNetworkId[];
-extern const char kPayloadPropertySwitchSlotOnReboot[];
-extern const char kPayloadPropertyRunPostInstall[];
+// These four fields are generated by scripts/brillo_update_payload.
+static constexpr const auto& kPayloadPropertyFileSize = "FILE_SIZE";
+static constexpr const auto& kPayloadPropertyFileHash = "FILE_HASH";
+static constexpr const auto& kPayloadPropertyMetadataSize = "METADATA_SIZE";
+static constexpr const auto& kPayloadPropertyMetadataHash = "METADATA_HASH";
+// The Authorization: HTTP header to be sent when downloading the payload.
+static constexpr const auto& kPayloadPropertyAuthorization = "AUTHORIZATION";
+// The User-Agent HTTP header to be sent when downloading the payload.
+static constexpr const auto& kPayloadPropertyUserAgent = "USER_AGENT";
+// Set "POWERWASH=1" to powerwash (factory data reset) the device after
+// applying the update.
+static constexpr const auto& kPayloadPropertyPowerwash = "POWERWASH";
+// The network id to pass to android_setprocnetwork before downloading.
+// This can be used to zero-rate OTA traffic by sending it over the correct
+// network.
+static constexpr const auto& kPayloadPropertyNetworkId = "NETWORK_ID";
+// Set "SWITCH_SLOT_ON_REBOOT=0" to skip marking the updated partitions active.
+// The default is 1 (always switch slot if update succeeded).
+static constexpr const auto& kPayloadPropertySwitchSlotOnReboot =
+ "SWITCH_SLOT_ON_REBOOT";
+// Set "RUN_POST_INSTALL=0" to skip running optional post install.
+// The default is 1 (always run post install).
+static constexpr const auto& kPayloadPropertyRunPostInstall =
+ "RUN_POST_INSTALL";
-extern const char kOmahaUpdaterVersion[];
+static constexpr const auto& kOmahaUpdaterVersion = "0.1.0.0";
// X-Goog-Update headers.
-extern const char kXGoogleUpdateInteractivity[];
-extern const char kXGoogleUpdateAppId[];
-extern const char kXGoogleUpdateUpdater[];
-extern const char kXGoogleUpdateSessionId[];
+// X-Goog-Update headers.
+static constexpr const auto& kXGoogleUpdateInteractivity =
+ "X-Goog-Update-Interactivity";
+static constexpr const auto& kXGoogleUpdateAppId = "X-Goog-Update-AppId";
+static constexpr const auto& kXGoogleUpdateUpdater = "X-Goog-Update-Updater";
+static constexpr const auto& kXGoogleUpdateSessionId = "X-Goog-SessionId";
// A download source is any combination of protocol and server (that's of
// interest to us when looking at UMA metrics) using which we may download
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 79e2139..8a68501 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -83,6 +83,7 @@
}
bool SetActiveBootSlot(Slot slot) override { return true; }
+ Slot GetActiveBootSlot() override { return kInvalidSlot; }
bool MarkBootSuccessfulAsync(base::Callback<void(bool)> callback) override {
// We run the callback directly from here to avoid having to setup a message
diff --git a/common/fake_prefs.cc b/common/fake_prefs.cc
index ea6ea60..e87e0ec 100644
--- a/common/fake_prefs.cc
+++ b/common/fake_prefs.cc
@@ -28,7 +28,7 @@
namespace {
-void CheckNotNull(const string& key, void* ptr) {
+void CheckNotNull(std::string_view key, void* ptr) {
EXPECT_NE(nullptr, ptr) << "Called Get*() for key \"" << key
<< "\" with a null parameter.";
}
@@ -63,41 +63,41 @@
bool FakePrefs::PrefValue::*const FakePrefs::PrefConsts<bool>::member =
&FakePrefs::PrefValue::as_bool;
-bool FakePrefs::GetString(const string& key, string* value) const {
+bool FakePrefs::GetString(std::string_view key, string* value) const {
return GetValue(key, value);
}
-bool FakePrefs::SetString(const string& key, std::string_view value) {
+bool FakePrefs::SetString(std::string_view key, std::string_view value) {
SetValue(key, std::string(value));
return true;
}
-bool FakePrefs::GetInt64(const string& key, int64_t* value) const {
+bool FakePrefs::GetInt64(std::string_view key, int64_t* value) const {
return GetValue(key, value);
}
-bool FakePrefs::SetInt64(const string& key, const int64_t value) {
+bool FakePrefs::SetInt64(std::string_view key, const int64_t value) {
SetValue(key, value);
return true;
}
-bool FakePrefs::GetBoolean(const string& key, bool* value) const {
+bool FakePrefs::GetBoolean(std::string_view key, bool* value) const {
return GetValue(key, value);
}
-bool FakePrefs::SetBoolean(const string& key, const bool value) {
+bool FakePrefs::SetBoolean(std::string_view key, const bool value) {
SetValue(key, value);
return true;
}
-bool FakePrefs::Exists(const string& key) const {
+bool FakePrefs::Exists(std::string_view key) const {
return values_.find(key) != values_.end();
}
-bool FakePrefs::Delete(const string& key) {
+bool FakePrefs::Delete(std::string_view key) {
if (values_.find(key) == values_.end())
return false;
- values_.erase(key);
+ values_.erase(std::string{key});
const auto observers_for_key = observers_.find(key);
if (observers_for_key != observers_.end()) {
std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
@@ -107,7 +107,7 @@
return true;
}
-bool FakePrefs::Delete(const string& key, const vector<string>& nss) {
+bool FakePrefs::Delete(std::string_view key, const vector<string>& nss) {
bool success = Delete(key);
for (const auto& ns : nss) {
vector<string> ns_keys;
@@ -123,7 +123,7 @@
return success;
}
-bool FakePrefs::GetSubKeys(const string& ns, vector<string>* keys) const {
+bool FakePrefs::GetSubKeys(std::string_view ns, vector<string>* keys) const {
for (const auto& pr : values_)
if (pr.first.compare(0, ns.length(), ns) == 0)
keys->push_back(pr.first);
@@ -142,7 +142,7 @@
return "Unknown";
}
-void FakePrefs::CheckKeyType(const string& key, PrefType type) const {
+void FakePrefs::CheckKeyType(std::string_view key, PrefType type) const {
auto it = values_.find(key);
EXPECT_TRUE(it == values_.end() || it->second.type == type)
<< "Key \"" << key << "\" if defined as " << GetTypeName(it->second.type)
@@ -150,10 +150,11 @@
}
template <typename T>
-void FakePrefs::SetValue(const string& key, T value) {
+void FakePrefs::SetValue(std::string_view key, T value) {
+ std::string str_key{key};
CheckKeyType(key, PrefConsts<T>::type);
- values_[key].type = PrefConsts<T>::type;
- values_[key].value.*(PrefConsts<T>::member) = std::move(value);
+ values_[str_key].type = PrefConsts<T>::type;
+ values_[str_key].value.*(PrefConsts<T>::member) = std::move(value);
const auto observers_for_key = observers_.find(key);
if (observers_for_key != observers_.end()) {
std::vector<ObserverInterface*> copy_observers(observers_for_key->second);
@@ -163,7 +164,7 @@
}
template <typename T>
-bool FakePrefs::GetValue(const string& key, T* value) const {
+bool FakePrefs::GetValue(std::string_view key, T* value) const {
CheckKeyType(key, PrefConsts<T>::type);
auto it = values_.find(key);
if (it == values_.end())
@@ -173,12 +174,14 @@
return true;
}
-void FakePrefs::AddObserver(const string& key, ObserverInterface* observer) {
- observers_[key].push_back(observer);
+void FakePrefs::AddObserver(std::string_view key, ObserverInterface* observer) {
+ observers_[string{key}].push_back(observer);
}
-void FakePrefs::RemoveObserver(const string& key, ObserverInterface* observer) {
- std::vector<ObserverInterface*>& observers_for_key = observers_[key];
+void FakePrefs::RemoveObserver(std::string_view key,
+ ObserverInterface* observer) {
+ string str_key{key};
+ std::vector<ObserverInterface*>& observers_for_key = observers_[str_key];
auto observer_it =
std::find(observers_for_key.begin(), observers_for_key.end(), observer);
EXPECT_NE(observer_it, observers_for_key.end())
@@ -186,7 +189,7 @@
if (observer_it != observers_for_key.end())
observers_for_key.erase(observer_it);
if (observers_for_key.empty())
- observers_.erase(key);
+ observers_.erase(str_key);
}
} // namespace chromeos_update_engine
diff --git a/common/fake_prefs.h b/common/fake_prefs.h
index 430c291..7ae9fb9 100644
--- a/common/fake_prefs.h
+++ b/common/fake_prefs.h
@@ -17,6 +17,7 @@
#ifndef UPDATE_ENGINE_COMMON_FAKE_PREFS_H_
#define UPDATE_ENGINE_COMMON_FAKE_PREFS_H_
+#include <functional>
#include <map>
#include <string>
#include <string_view>
@@ -40,24 +41,23 @@
~FakePrefs();
// PrefsInterface methods.
- bool GetString(const std::string& key, std::string* value) const override;
- bool SetString(const std::string& key, std::string_view value) override;
- bool GetInt64(const std::string& key, int64_t* value) const override;
- bool SetInt64(const std::string& key, const int64_t value) override;
- bool GetBoolean(const std::string& key, bool* value) const override;
- bool SetBoolean(const std::string& key, const bool value) override;
+ bool GetString(std::string_view key, std::string* value) const override;
+ bool SetString(std::string_view key, std::string_view value) override;
+ bool GetInt64(std::string_view key, int64_t* value) const override;
+ bool SetInt64(std::string_view key, const int64_t value) override;
+ bool GetBoolean(std::string_view key, bool* value) const override;
+ bool SetBoolean(std::string_view key, const bool value) override;
- bool Exists(const std::string& key) const override;
- bool Delete(const std::string& key) override;
- bool Delete(const std::string& key,
+ bool Exists(std::string_view key) const override;
+ bool Delete(std::string_view key) override;
+ bool Delete(std::string_view key,
const std::vector<std::string>& nss) override;
- bool GetSubKeys(const std::string& ns,
+ bool GetSubKeys(std::string_view ns,
std::vector<std::string>* keys) const override;
- void AddObserver(const std::string& key,
- ObserverInterface* observer) override;
- void RemoveObserver(const std::string& key,
+ void AddObserver(std::string_view key, ObserverInterface* observer) override;
+ void RemoveObserver(std::string_view key,
ObserverInterface* observer) override;
private:
@@ -92,24 +92,25 @@
static std::string GetTypeName(PrefType type);
// Checks that the |key| is either not present or has the given |type|.
- void CheckKeyType(const std::string& key, PrefType type) const;
+ void CheckKeyType(std::string_view key, PrefType type) const;
// Helper function to set a value of the passed |key|. It sets the type based
// on the template parameter T.
template <typename T>
- void SetValue(const std::string& key, T value);
+ void SetValue(std::string_view key, T value);
// Helper function to get a value from the map checking for invalid calls.
// The function fails the test if you attempt to read a value defined as a
// different type. Returns whether the get succeeded.
template <typename T>
- bool GetValue(const std::string& key, T* value) const;
+ bool GetValue(std::string_view key, T* value) const;
// Container for all the key/value pairs.
- std::map<std::string, PrefTypeValue> values_;
+ std::map<std::string, PrefTypeValue, std::less<>> values_;
// The registered observers watching for changes.
- std::map<std::string, std::vector<ObserverInterface*>> observers_;
+ std::map<std::string, std::vector<ObserverInterface*>, std::less<>>
+ observers_;
DISALLOW_COPY_AND_ASSIGN(FakePrefs);
};
diff --git a/common/mock_prefs.h b/common/mock_prefs.h
index 49431fb..f308074 100644
--- a/common/mock_prefs.h
+++ b/common/mock_prefs.h
@@ -29,27 +29,24 @@
class MockPrefs : public PrefsInterface {
public:
- MOCK_CONST_METHOD2(GetString,
- bool(const std::string& key, std::string* value));
- MOCK_METHOD2(SetString, bool(const std::string& key, std::string_view value));
- MOCK_CONST_METHOD2(GetInt64, bool(const std::string& key, int64_t* value));
- MOCK_METHOD2(SetInt64, bool(const std::string& key, const int64_t value));
+ MOCK_CONST_METHOD2(GetString, bool(std::string_view key, std::string* value));
+ MOCK_METHOD2(SetString, bool(std::string_view key, std::string_view value));
+ MOCK_CONST_METHOD2(GetInt64, bool(std::string_view key, int64_t* value));
+ MOCK_METHOD2(SetInt64, bool(std::string_view key, const int64_t value));
- MOCK_CONST_METHOD2(GetBoolean, bool(const std::string& key, bool* value));
- MOCK_METHOD2(SetBoolean, bool(const std::string& key, const bool value));
+ MOCK_CONST_METHOD2(GetBoolean, bool(std::string_view key, bool* value));
+ MOCK_METHOD2(SetBoolean, bool(std::string_view key, const bool value));
- MOCK_CONST_METHOD1(Exists, bool(const std::string& key));
- MOCK_METHOD1(Delete, bool(const std::string& key));
+ MOCK_CONST_METHOD1(Exists, bool(std::string_view key));
+ MOCK_METHOD1(Delete, bool(std::string_view key));
MOCK_METHOD2(Delete,
- bool(const std::string& key,
- const std::vector<std::string>& nss));
+ bool(std::string_view key, const std::vector<std::string>& nss));
MOCK_CONST_METHOD2(GetSubKeys,
- bool(const std::string&, std::vector<std::string>*));
+ bool(std::string_view, std::vector<std::string>*));
- MOCK_METHOD2(AddObserver, void(const std::string& key, ObserverInterface*));
- MOCK_METHOD2(RemoveObserver,
- void(const std::string& key, ObserverInterface*));
+ MOCK_METHOD2(AddObserver, void(std::string_view key, ObserverInterface*));
+ MOCK_METHOD2(RemoveObserver, void(std::string_view key, ObserverInterface*));
};
} // namespace chromeos_update_engine
diff --git a/common/prefs.cc b/common/prefs.cc
index 1e06be4..f33a8a9 100644
--- a/common/prefs.cc
+++ b/common/prefs.cc
@@ -51,11 +51,11 @@
} // namespace
-bool PrefsBase::GetString(const string& key, string* value) const {
+bool PrefsBase::GetString(const std::string_view key, string* value) const {
return storage_->GetKey(key, value);
}
-bool PrefsBase::SetString(const string& key, std::string_view value) {
+bool PrefsBase::SetString(std::string_view key, std::string_view value) {
TEST_AND_RETURN_FALSE(storage_->SetKey(key, value));
const auto observers_for_key = observers_.find(key);
if (observers_for_key != observers_.end()) {
@@ -66,7 +66,7 @@
return true;
}
-bool PrefsBase::GetInt64(const string& key, int64_t* value) const {
+bool PrefsBase::GetInt64(const std::string_view key, int64_t* value) const {
string str_value;
if (!GetString(key, &str_value))
return false;
@@ -75,11 +75,11 @@
return true;
}
-bool PrefsBase::SetInt64(const string& key, const int64_t value) {
+bool PrefsBase::SetInt64(std::string_view key, const int64_t value) {
return SetString(key, base::NumberToString(value));
}
-bool PrefsBase::GetBoolean(const string& key, bool* value) const {
+bool PrefsBase::GetBoolean(std::string_view key, bool* value) const {
string str_value;
if (!GetString(key, &str_value))
return false;
@@ -95,15 +95,15 @@
return false;
}
-bool PrefsBase::SetBoolean(const string& key, const bool value) {
+bool PrefsBase::SetBoolean(std::string_view key, const bool value) {
return SetString(key, value ? "true" : "false");
}
-bool PrefsBase::Exists(const string& key) const {
+bool PrefsBase::Exists(std::string_view key) const {
return storage_->KeyExists(key);
}
-bool PrefsBase::Delete(const string& key) {
+bool PrefsBase::Delete(std::string_view key) {
TEST_AND_RETURN_FALSE(storage_->DeleteKey(key));
const auto observers_for_key = observers_.find(key);
if (observers_for_key != observers_.end()) {
@@ -114,7 +114,7 @@
return true;
}
-bool PrefsBase::Delete(const string& pref_key, const vector<string>& nss) {
+bool PrefsBase::Delete(std::string_view pref_key, const vector<string>& nss) {
// Delete pref key for platform.
bool success = Delete(pref_key);
// Delete pref key in each namespace.
@@ -132,16 +132,18 @@
return success;
}
-bool PrefsBase::GetSubKeys(const string& ns, vector<string>* keys) const {
+bool PrefsBase::GetSubKeys(std::string_view ns, vector<string>* keys) const {
return storage_->GetSubKeys(ns, keys);
}
-void PrefsBase::AddObserver(const string& key, ObserverInterface* observer) {
- observers_[key].push_back(observer);
+void PrefsBase::AddObserver(std::string_view key, ObserverInterface* observer) {
+ observers_[std::string{key}].push_back(observer);
}
-void PrefsBase::RemoveObserver(const string& key, ObserverInterface* observer) {
- std::vector<ObserverInterface*>& observers_for_key = observers_[key];
+void PrefsBase::RemoveObserver(std::string_view key,
+ ObserverInterface* observer) {
+ std::vector<ObserverInterface*>& observers_for_key =
+ observers_[std::string{key}];
auto observer_it =
std::find(observers_for_key.begin(), observers_for_key.end(), observer);
if (observer_it != observers_for_key.end())
@@ -165,7 +167,7 @@
return true;
}
-bool Prefs::FileStorage::GetKey(const string& key, string* value) const {
+bool Prefs::FileStorage::GetKey(std::string_view key, string* value) const {
base::FilePath filename;
TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
if (!base::ReadFileToString(filename, value)) {
@@ -174,7 +176,7 @@
return true;
}
-bool Prefs::FileStorage::GetSubKeys(const string& ns,
+bool Prefs::FileStorage::GetSubKeys(std::string_view ns,
vector<string>* keys) const {
base::FilePath filename;
TEST_AND_RETURN_FALSE(GetFileNameForKey(ns, &filename));
@@ -192,7 +194,7 @@
return true;
}
-bool Prefs::FileStorage::SetKey(const string& key, std::string_view value) {
+bool Prefs::FileStorage::SetKey(std::string_view key, std::string_view value) {
base::FilePath filename;
TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
if (!base::DirectoryExists(filename.DirName())) {
@@ -205,13 +207,13 @@
return true;
}
-bool Prefs::FileStorage::KeyExists(const string& key) const {
+bool Prefs::FileStorage::KeyExists(std::string_view key) const {
base::FilePath filename;
TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
return base::PathExists(filename);
}
-bool Prefs::FileStorage::DeleteKey(const string& key) {
+bool Prefs::FileStorage::DeleteKey(std::string_view key) {
base::FilePath filename;
TEST_AND_RETURN_FALSE(GetFileNameForKey(key, &filename));
#if BASE_VER < 800000
@@ -222,20 +224,21 @@
return true;
}
-bool Prefs::FileStorage::GetFileNameForKey(const string& key,
+bool Prefs::FileStorage::GetFileNameForKey(std::string_view key,
base::FilePath* filename) const {
// Allows only non-empty keys containing [A-Za-z0-9_-/].
TEST_AND_RETURN_FALSE(!key.empty());
for (char c : key)
TEST_AND_RETURN_FALSE(base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) ||
c == '_' || c == '-' || c == kKeySeparator);
- *filename = prefs_dir_.Append(key);
+ *filename = prefs_dir_.Append(
+ base::FilePath::StringPieceType(key.data(), key.size()));
return true;
}
// MemoryPrefs
-bool MemoryPrefs::MemoryStorage::GetKey(const string& key,
+bool MemoryPrefs::MemoryStorage::GetKey(std::string_view key,
string* value) const {
auto it = values_.find(key);
if (it == values_.end())
@@ -244,15 +247,13 @@
return true;
}
-bool MemoryPrefs::MemoryStorage::GetSubKeys(const string& ns,
+bool MemoryPrefs::MemoryStorage::GetSubKeys(std::string_view ns,
vector<string>* keys) const {
- using value_type = decltype(values_)::value_type;
- using key_type = decltype(values_)::key_type;
- auto lower_comp = [](const value_type& pr, const key_type& ns) {
- return pr.first.substr(0, ns.length()) < ns;
+ auto lower_comp = [](const auto& pr, const auto& ns) {
+ return std::string_view{pr.first.data(), ns.length()} < ns;
};
- auto upper_comp = [](const key_type& ns, const value_type& pr) {
- return ns < pr.first.substr(0, ns.length());
+ auto upper_comp = [](const auto& ns, const auto& pr) {
+ return ns < std::string_view{pr.first.data(), ns.length()};
};
auto lower_it =
std::lower_bound(begin(values_), end(values_), ns, lower_comp);
@@ -262,17 +263,17 @@
return true;
}
-bool MemoryPrefs::MemoryStorage::SetKey(const string& key,
+bool MemoryPrefs::MemoryStorage::SetKey(std::string_view key,
std::string_view value) {
- values_[key] = value;
+ values_[std::string{key}] = value;
return true;
}
-bool MemoryPrefs::MemoryStorage::KeyExists(const string& key) const {
+bool MemoryPrefs::MemoryStorage::KeyExists(std::string_view key) const {
return values_.find(key) != values_.end();
}
-bool MemoryPrefs::MemoryStorage::DeleteKey(const string& key) {
+bool MemoryPrefs::MemoryStorage::DeleteKey(std::string_view key) {
auto it = values_.find(key);
if (it != values_.end())
values_.erase(it);
diff --git a/common/prefs.h b/common/prefs.h
index 93477dd..c3105c6 100644
--- a/common/prefs.h
+++ b/common/prefs.h
@@ -17,6 +17,7 @@
#ifndef UPDATE_ENGINE_COMMON_PREFS_H_
#define UPDATE_ENGINE_COMMON_PREFS_H_
+#include <functional>
#include <map>
#include <string>
#include <string_view>
@@ -41,23 +42,23 @@
// Get the key named |key| and store its value in the referenced |value|.
// Returns whether the operation succeeded.
- virtual bool GetKey(const std::string& key, std::string* value) const = 0;
+ virtual bool GetKey(std::string_view key, std::string* value) const = 0;
// Get the keys stored within the namespace. If there are no keys in the
// namespace, |keys| will be empty. Returns whether the operation succeeded.
- virtual bool GetSubKeys(const std::string& ns,
+ virtual bool GetSubKeys(std::string_view ns,
std::vector<std::string>* keys) const = 0;
// Set the value of the key named |key| to |value| regardless of the
// previous value. Returns whether the operation succeeded.
- virtual bool SetKey(const std::string& key, std::string_view value) = 0;
+ virtual bool SetKey(std::string_view key, std::string_view value) = 0;
// Returns whether the key named |key| exists.
- virtual bool KeyExists(const std::string& key) const = 0;
+ virtual bool KeyExists(std::string_view key) const = 0;
// Deletes the value associated with the key name |key|. Returns whether the
// key was deleted.
- virtual bool DeleteKey(const std::string& key) = 0;
+ virtual bool DeleteKey(std::string_view key) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(StorageInterface);
@@ -66,29 +67,29 @@
explicit PrefsBase(StorageInterface* storage) : storage_(storage) {}
// PrefsInterface methods.
- bool GetString(const std::string& key, std::string* value) const override;
- bool SetString(const std::string& key, std::string_view value) override;
- bool GetInt64(const std::string& key, int64_t* value) const override;
- bool SetInt64(const std::string& key, const int64_t value) override;
- bool GetBoolean(const std::string& key, bool* value) const override;
- bool SetBoolean(const std::string& key, const bool value) override;
+ bool GetString(std::string_view key, std::string* value) const override;
+ bool SetString(std::string_view key, std::string_view value) override;
+ bool GetInt64(std::string_view key, int64_t* value) const override;
+ bool SetInt64(std::string_view key, const int64_t value) override;
+ bool GetBoolean(std::string_view key, bool* value) const override;
+ bool SetBoolean(std::string_view key, const bool value) override;
- bool Exists(const std::string& key) const override;
- bool Delete(const std::string& key) override;
- bool Delete(const std::string& pref_key,
+ bool Exists(std::string_view key) const override;
+ bool Delete(std::string_view key) override;
+ bool Delete(std::string_view pref_key,
const std::vector<std::string>& nss) override;
- bool GetSubKeys(const std::string& ns,
+ bool GetSubKeys(std::string_view ns,
std::vector<std::string>* keys) const override;
- void AddObserver(const std::string& key,
- ObserverInterface* observer) override;
- void RemoveObserver(const std::string& key,
+ void AddObserver(std::string_view key, ObserverInterface* observer) override;
+ void RemoveObserver(std::string_view key,
ObserverInterface* observer) override;
private:
// The registered observers watching for changes.
- std::map<std::string, std::vector<ObserverInterface*>> observers_;
+ std::map<std::string, std::vector<ObserverInterface*>, std::less<>>
+ observers_;
// The concrete implementation of the storage used for the keys.
StorageInterface* storage_;
@@ -121,12 +122,12 @@
bool Init(const base::FilePath& prefs_dir);
// PrefsBase::StorageInterface overrides.
- bool GetKey(const std::string& key, std::string* value) const override;
- bool GetSubKeys(const std::string& ns,
+ bool GetKey(std::string_view key, std::string* value) const override;
+ bool GetSubKeys(std::string_view ns,
std::vector<std::string>* keys) const override;
- bool SetKey(const std::string& key, std::string_view value) override;
- bool KeyExists(const std::string& key) const override;
- bool DeleteKey(const std::string& key) override;
+ bool SetKey(std::string_view key, std::string_view value) override;
+ bool KeyExists(std::string_view key) const override;
+ bool DeleteKey(std::string_view key) override;
private:
FRIEND_TEST(PrefsTest, GetFileNameForKey);
@@ -135,7 +136,7 @@
// Sets |filename| to the full path to the file containing the data
// associated with |key|. Returns true on success, false otherwise.
- bool GetFileNameForKey(const std::string& key,
+ bool GetFileNameForKey(std::string_view key,
base::FilePath* filename) const;
// Preference store directory.
@@ -161,16 +162,16 @@
MemoryStorage() = default;
// PrefsBase::StorageInterface overrides.
- bool GetKey(const std::string& key, std::string* value) const override;
- bool GetSubKeys(const std::string& ns,
+ bool GetKey(std::string_view, std::string* value) const override;
+ bool GetSubKeys(std::string_view ns,
std::vector<std::string>* keys) const override;
- bool SetKey(const std::string& key, std::string_view value) override;
- bool KeyExists(const std::string& key) const override;
- bool DeleteKey(const std::string& key) override;
+ bool SetKey(std::string_view key, std::string_view value) override;
+ bool KeyExists(std::string_view key) const override;
+ bool DeleteKey(std::string_view key) override;
private:
// The std::map holding the values in memory.
- std::map<std::string, std::string> values_;
+ std::map<std::string, std::string, std::less<>> values_;
};
// The concrete memory storage implementation.
diff --git a/common/prefs_interface.h b/common/prefs_interface.h
index e773a35..69ccf68 100644
--- a/common/prefs_interface.h
+++ b/common/prefs_interface.h
@@ -37,10 +37,10 @@
virtual ~ObserverInterface() = default;
// Called when the value is set for the observed |key|.
- virtual void OnPrefSet(const std::string& key) = 0;
+ virtual void OnPrefSet(std::string_view key) = 0;
// Called when the observed |key| is deleted.
- virtual void OnPrefDeleted(const std::string& key) = 0;
+ virtual void OnPrefDeleted(std::string_view key) = 0;
};
virtual ~PrefsInterface() = default;
@@ -48,61 +48,61 @@
// Gets a string |value| associated with |key|. Returns true on
// success, false on failure (including when the |key| is not
// present in the store).
- virtual bool GetString(const std::string& key, std::string* value) const = 0;
+ virtual bool GetString(std::string_view key, std::string* value) const = 0;
// Associates |key| with a string |value|. Returns true on success,
// false otherwise.
- virtual bool SetString(const std::string& key, std::string_view value) = 0;
+ virtual bool SetString(std::string_view key, std::string_view value) = 0;
// Gets an int64_t |value| associated with |key|. Returns true on
// success, false on failure (including when the |key| is not
// present in the store).
- virtual bool GetInt64(const std::string& key, int64_t* value) const = 0;
+ virtual bool GetInt64(std::string_view key, int64_t* value) const = 0;
// Associates |key| with an int64_t |value|. Returns true on success,
// false otherwise.
- virtual bool SetInt64(const std::string& key, const int64_t value) = 0;
+ virtual bool SetInt64(std::string_view key, const int64_t value) = 0;
// Gets a boolean |value| associated with |key|. Returns true on
// success, false on failure (including when the |key| is not
// present in the store).
- virtual bool GetBoolean(const std::string& key, bool* value) const = 0;
+ virtual bool GetBoolean(std::string_view key, bool* value) const = 0;
// Associates |key| with a boolean |value|. Returns true on success,
// false otherwise.
- virtual bool SetBoolean(const std::string& key, const bool value) = 0;
+ virtual bool SetBoolean(std::string_view key, const bool value) = 0;
// Returns true if the setting exists (i.e. a file with the given key
// exists in the prefs directory)
- virtual bool Exists(const std::string& key) const = 0;
+ virtual bool Exists(std::string_view key) const = 0;
// Returns true if successfully deleted the file corresponding to
// this key. Calling with non-existent keys does nothing.
- virtual bool Delete(const std::string& key) = 0;
+ virtual bool Delete(std::string_view key) = 0;
// Deletes the pref key from platform and given namespace subdirectories.
// Keys are matched against end of pref keys in each namespace.
// Returns true if all deletes were successful.
- virtual bool Delete(const std::string& pref_key,
+ virtual bool Delete(std::string_view pref_key,
const std::vector<std::string>& nss) = 0;
// Creates a key which is part of a sub preference.
static std::string CreateSubKey(const std::vector<std::string>& ns_with_key);
// Returns a list of keys within the namespace.
- virtual bool GetSubKeys(const std::string& ns,
+ virtual bool GetSubKeys(std::string_view ns,
std::vector<std::string>* keys) const = 0;
// Add an observer to watch whenever the given |key| is modified. The
// OnPrefSet() and OnPrefDelete() methods will be called whenever any of the
// Set*() methods or the Delete() method are called on the given key,
// respectively.
- virtual void AddObserver(const std::string& key,
+ virtual void AddObserver(std::string_view key,
ObserverInterface* observer) = 0;
// Remove an observer added with AddObserver(). The observer won't be called
// anymore for future Set*() and Delete() method calls.
- virtual void RemoveObserver(const std::string& key,
+ virtual void RemoveObserver(std::string_view key,
ObserverInterface* observer) = 0;
protected:
diff --git a/common/prefs_unittest.cc b/common/prefs_unittest.cc
index a5f46e5..cef6d44 100644
--- a/common/prefs_unittest.cc
+++ b/common/prefs_unittest.cc
@@ -507,8 +507,8 @@
class MockPrefsObserver : public PrefsInterface::ObserverInterface {
public:
- MOCK_METHOD1(OnPrefSet, void(const string&));
- MOCK_METHOD1(OnPrefDeleted, void(const string& key));
+ MOCK_METHOD1(OnPrefSet, void(std::string_view));
+ MOCK_METHOD1(OnPrefDeleted, void(std::string_view));
};
TEST_F(PrefsTest, ObserversCalled) {
diff --git a/metrics_utils.cc b/metrics_utils.cc
index 34da5a1..ade024a 100644
--- a/metrics_utils.cc
+++ b/metrics_utils.cc
@@ -294,7 +294,7 @@
return metrics::ConnectionType::kUnknown;
}
-int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs) {
+int64_t GetPersistedValue(std::string_view key, PrefsInterface* prefs) {
CHECK(prefs);
if (!prefs->Exists(key))
return 0;
diff --git a/metrics_utils.h b/metrics_utils.h
index 3aac4e5..16e9eec 100644
--- a/metrics_utils.h
+++ b/metrics_utils.h
@@ -50,7 +50,7 @@
// Returns the persisted value from prefs for the given key. It also
// validates that the value returned is non-negative.
-int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs);
+int64_t GetPersistedValue(std::string_view key, PrefsInterface* prefs);
// Persists the reboot count of the update attempt to |kPrefsNumReboots|.
void SetNumReboots(int64_t num_reboots, PrefsInterface* prefs);
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index a57169b..31fa90e 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -32,8 +32,6 @@
#include <base/format_macros.h>
#include <base/metrics/histogram_macros.h>
#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <brillo/data_encoding.h>
#include <bsdiff/bspatch.h>
@@ -873,44 +871,6 @@
return partition_writer_->PerformZeroOrDiscardOperation(operation);
}
-bool PartitionWriter::ValidateSourceHash(const brillo::Blob& calculated_hash,
- const InstallOperation& operation,
- const FileDescriptorPtr source_fd,
- ErrorCode* error) {
- brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
- operation.src_sha256_hash().end());
- if (calculated_hash != expected_source_hash) {
- LOG(ERROR) << "The hash of the source data on disk for this operation "
- << "doesn't match the expected value. This could mean that the "
- << "delta update payload was targeted for another version, or "
- << "that the source partition was modified after it was "
- << "installed, for example, by mounting a filesystem.";
- LOG(ERROR) << "Expected: sha256|hex = "
- << base::HexEncode(expected_source_hash.data(),
- expected_source_hash.size());
- LOG(ERROR) << "Calculated: sha256|hex = "
- << base::HexEncode(calculated_hash.data(),
- calculated_hash.size());
-
- vector<string> source_extents;
- for (const Extent& ext : operation.src_extents()) {
- source_extents.push_back(
- base::StringPrintf("%" PRIu64 ":%" PRIu64,
- static_cast<uint64_t>(ext.start_block()),
- static_cast<uint64_t>(ext.num_blocks())));
- }
- LOG(ERROR) << "Operation source (offset:size) in blocks: "
- << base::JoinString(source_extents, ",");
-
- // Log remount history if this device is an ext4 partition.
- LogMountHistory(source_fd);
-
- *error = ErrorCode::kDownloadStateInitializationError;
- return false;
- }
- return true;
-}
-
bool DeltaPerformer::PerformSourceCopyOperation(
const InstallOperation& operation, ErrorCode* error) {
if (operation.has_src_length())
@@ -1531,7 +1491,7 @@
part_name, slot);
}
-std::unique_ptr<PartitionWriter> DeltaPerformer::CreatePartitionWriter(
+std::unique_ptr<PartitionWriterInterface> DeltaPerformer::CreatePartitionWriter(
const PartitionUpdate& partition_update,
const InstallPlan::Partition& install_part,
DynamicPartitionControlInterface* dynamic_control,
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index c54316b..30f5e9c 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -176,14 +176,6 @@
// Exposed for testing purposes.
bool CheckpointUpdateProgress(bool force);
- // Compare |calculated_hash| with source hash in |operation|, return false and
- // dump hash and set |error| if don't match.
- // |source_fd| is the file descriptor of the source partition.
- static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
- const InstallOperation& operation,
- const FileDescriptorPtr source_fd,
- ErrorCode* error);
-
// Initialize partitions and allocate required space for an update with the
// given |manifest|. |update_check_response_hash| is used to check if the
// previous call to this function corresponds to the same payload.
@@ -204,7 +196,7 @@
protected:
// Exposed as virtual for testing purposes.
- virtual std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+ virtual std::unique_ptr<PartitionWriterInterface> CreatePartitionWriter(
const PartitionUpdate& partition_update,
const InstallPlan::Partition& install_part,
DynamicPartitionControlInterface* dynamic_control,
@@ -434,7 +426,7 @@
base::TimeDelta::FromSeconds(kCheckpointFrequencySeconds)};
base::TimeTicks update_checkpoint_time_;
- std::unique_ptr<PartitionWriter> partition_writer_;
+ std::unique_ptr<PartitionWriterInterface> partition_writer_;
DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
};
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index 4fab975..a72b8ae 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -156,7 +156,7 @@
performer.manifest_.CopyFrom(manifest);
performer.major_payload_version_ = major_version;
- EXPECT_EQ(expected, performer.ValidateManifest());
+ ASSERT_EQ(expected, performer.ValidateManifest());
}
void AddPartition(DeltaArchiveManifest* manifest,
string name,
@@ -171,19 +171,19 @@
static void CompareFilesByBlock(const string& a_file,
const string& b_file,
size_t image_size) {
- EXPECT_EQ(0U, image_size % kBlockSize);
+ ASSERT_EQ(0U, image_size % kBlockSize);
brillo::Blob a_data, b_data;
- EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
- EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
+ ASSERT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
+ ASSERT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
EXPECT_GE(a_data.size(), image_size);
EXPECT_GE(b_data.size(), image_size);
for (size_t i = 0; i < image_size; i += kBlockSize) {
- EXPECT_EQ(0U, i % kBlockSize);
+ ASSERT_EQ(0U, i % kBlockSize);
brillo::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
brillo::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
- EXPECT_TRUE(a_sub == b_sub) << "Block " << (i / kBlockSize) << " differs";
+ ASSERT_EQ(a_sub, b_sub) << "Block " << (i / kBlockSize) << " differs";
}
if (::testing::Test::HasNonfatalFailure()) {
LOG(INFO) << "Compared filesystems with size " << image_size
@@ -207,8 +207,7 @@
int fd = open(path.c_str(), O_CREAT | O_WRONLY, 0644);
TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
ScopedFdCloser fd_closer(&fd);
- EXPECT_TRUE(utils::PWriteAll(fd, "\0", 1, offset));
- return true;
+ return utils::PWriteAll(fd, "\0", 1, offset);
}
static bool InsertSignaturePlaceholder(size_t signature_size,
@@ -245,7 +244,7 @@
{metadata_signature},
payload_path,
out_metadata_size));
- EXPECT_TRUE(PayloadSigner::VerifySignedPayload(
+ ASSERT_TRUE(PayloadSigner::VerifySignedPayload(
payload_path, GetBuildArtifactsPath(kUnittestPublicKeyPath)));
}
@@ -359,12 +358,12 @@
// openssl genrsa -out <private_key_path> 2048
RSA* rsa = RSA_new();
BIGNUM* e = BN_new();
- EXPECT_EQ(1, BN_set_word(e, RSA_F4));
- EXPECT_EQ(1, RSA_generate_key_ex(rsa, 2048, e, nullptr));
+ ASSERT_EQ(1, BN_set_word(e, RSA_F4));
+ ASSERT_EQ(1, RSA_generate_key_ex(rsa, 2048, e, nullptr));
BN_free(e);
FILE* fprikey = fopen(private_key_path.c_str(), "w");
EXPECT_NE(nullptr, fprikey);
- EXPECT_EQ(1,
+ ASSERT_EQ(1,
PEM_write_RSAPrivateKey(
fprikey, rsa, nullptr, nullptr, 0, nullptr, nullptr));
fclose(fprikey);
@@ -405,7 +404,7 @@
// in-place on A, we apply it to a new image, result_img.
state->result_img.reset(new ScopedTempFile("result_img.XXXXXX"));
- EXPECT_TRUE(
+ ASSERT_TRUE(
base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
base::FilePath(state->a_img->path())));
@@ -422,28 +421,28 @@
std::begin(kRandomString),
std::end(kRandomString));
}
- EXPECT_TRUE(utils::WriteFile(
+ ASSERT_TRUE(utils::WriteFile(
base::StringPrintf("%s/hardtocompress", a_mnt.c_str()).c_str(),
hardtocompress.data(),
hardtocompress.size()));
brillo::Blob zeros(16 * 1024, 0);
- EXPECT_EQ(static_cast<int>(zeros.size()),
+ ASSERT_EQ(static_cast<int>(zeros.size()),
base::WriteFile(base::FilePath(base::StringPrintf(
"%s/move-to-sparse", a_mnt.c_str())),
reinterpret_cast<const char*>(zeros.data()),
zeros.size()));
- EXPECT_TRUE(WriteSparseFile(
+ ASSERT_TRUE(WriteSparseFile(
base::StringPrintf("%s/move-from-sparse", a_mnt.c_str()), 16 * 1024));
- EXPECT_TRUE(WriteByteAtOffset(
+ ASSERT_TRUE(WriteByteAtOffset(
base::StringPrintf("%s/move-semi-sparse", a_mnt.c_str()), 4096));
// Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
// patch fails to zero out the final block.
brillo::Blob ones(1024 * 1024, 0xff);
- EXPECT_TRUE(
+ ASSERT_TRUE(
utils::WriteFile(base::StringPrintf("%s/ones", a_mnt.c_str()).c_str(),
ones.data(),
ones.size()));
@@ -451,12 +450,12 @@
// Create a result image with image_size bytes of garbage.
brillo::Blob ones(state->image_size, 0xff);
- EXPECT_TRUE(utils::WriteFile(
+ ASSERT_TRUE(utils::WriteFile(
state->result_img->path().c_str(), ones.data(), ones.size()));
- EXPECT_EQ(utils::FileSize(state->a_img->path()),
+ ASSERT_EQ(utils::FileSize(state->a_img->path()),
utils::FileSize(state->result_img->path()));
- EXPECT_TRUE(
+ ASSERT_TRUE(
base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
base::FilePath(state->b_img->path())));
{
@@ -465,45 +464,45 @@
ScopedLoopMounter b_mounter(state->b_img->path(), &b_mnt, 0);
base::FilePath mnt_path(b_mnt);
- EXPECT_TRUE(base::CopyFile(mnt_path.Append("regular-small"),
+ ASSERT_TRUE(base::CopyFile(mnt_path.Append("regular-small"),
mnt_path.Append("regular-small2")));
#if BASE_VER < 800000
- EXPECT_TRUE(base::DeleteFile(mnt_path.Append("regular-small"), false));
+ ASSERT_TRUE(base::DeleteFile(mnt_path.Append("regular-small"), false));
#else
- EXPECT_TRUE(base::DeleteFile(mnt_path.Append("regular-small")));
+ ASSERT_TRUE(base::DeleteFile(mnt_path.Append("regular-small")));
#endif
- EXPECT_TRUE(base::Move(mnt_path.Append("regular-small2"),
+ ASSERT_TRUE(base::Move(mnt_path.Append("regular-small2"),
mnt_path.Append("regular-small")));
- EXPECT_TRUE(
+ ASSERT_TRUE(
test_utils::WriteFileString(mnt_path.Append("foo").value(), "foo"));
- EXPECT_EQ(0, base::WriteFile(mnt_path.Append("emptyfile"), "", 0));
+ ASSERT_EQ(0, base::WriteFile(mnt_path.Append("emptyfile"), "", 0));
- EXPECT_TRUE(
+ ASSERT_TRUE(
WriteSparseFile(mnt_path.Append("fullsparse").value(), 1024 * 1024));
- EXPECT_TRUE(
+ ASSERT_TRUE(
WriteSparseFile(mnt_path.Append("move-to-sparse").value(), 16 * 1024));
brillo::Blob zeros(16 * 1024, 0);
- EXPECT_EQ(static_cast<int>(zeros.size()),
+ ASSERT_EQ(static_cast<int>(zeros.size()),
base::WriteFile(mnt_path.Append("move-from-sparse"),
reinterpret_cast<const char*>(zeros.data()),
zeros.size()));
- EXPECT_TRUE(
+ ASSERT_TRUE(
WriteByteAtOffset(mnt_path.Append("move-semi-sparse").value(), 4096));
- EXPECT_TRUE(WriteByteAtOffset(mnt_path.Append("partsparse").value(), 4096));
+ ASSERT_TRUE(WriteByteAtOffset(mnt_path.Append("partsparse").value(), 4096));
- EXPECT_TRUE(
+ ASSERT_TRUE(
base::CopyFile(mnt_path.Append("regular-16k"), mnt_path.Append("tmp")));
- EXPECT_TRUE(base::Move(mnt_path.Append("tmp"),
+ ASSERT_TRUE(base::Move(mnt_path.Append("tmp"),
mnt_path.Append("link-hard-regular-16k")));
#if BASE_VER < 800000
- EXPECT_TRUE(base::DeleteFile(mnt_path.Append("link-short_symlink"), false));
+ ASSERT_TRUE(base::DeleteFile(mnt_path.Append("link-short_symlink"), false));
#else
- EXPECT_TRUE(base::DeleteFile(mnt_path.Append("link-short_symlink")));
+ ASSERT_TRUE(base::DeleteFile(mnt_path.Append("link-short_symlink")));
#endif
- EXPECT_TRUE(test_utils::WriteFileString(
+ ASSERT_TRUE(test_utils::WriteFileString(
mnt_path.Append("link-short_symlink").value(), "foobar"));
brillo::Blob hardtocompress;
@@ -512,7 +511,7 @@
std::begin(kRandomString),
std::end(kRandomString));
}
- EXPECT_TRUE(utils::WriteFile(
+ ASSERT_TRUE(utils::WriteFile(
base::StringPrintf("%s/hardtocompress", b_mnt.c_str()).c_str(),
hardtocompress.data(),
hardtocompress.size()));
@@ -534,13 +533,13 @@
std::begin(kNewData), std::end(kNewData), state->new_kernel_data.begin());
// Write kernels to disk
- EXPECT_TRUE(utils::WriteFile(state->old_kernel->path().c_str(),
+ ASSERT_TRUE(utils::WriteFile(state->old_kernel->path().c_str(),
state->old_kernel_data.data(),
state->old_kernel_data.size()));
- EXPECT_TRUE(utils::WriteFile(state->new_kernel->path().c_str(),
+ ASSERT_TRUE(utils::WriteFile(state->new_kernel->path().c_str(),
state->new_kernel_data.data(),
state->new_kernel_data.size()));
- EXPECT_TRUE(utils::WriteFile(state->result_kernel->path().c_str(),
+ ASSERT_TRUE(utils::WriteFile(state->result_kernel->path().c_str(),
state->result_kernel_data.data(),
state->result_kernel_data.size()));
@@ -564,9 +563,9 @@
if (!full_kernel)
payload_config.source.partitions.back().path =
state->old_kernel->path();
- EXPECT_TRUE(payload_config.source.LoadImageSize());
+ ASSERT_TRUE(payload_config.source.LoadImageSize());
for (PartitionConfig& part : payload_config.source.partitions)
- EXPECT_TRUE(part.OpenFilesystem());
+ ASSERT_TRUE(part.OpenFilesystem());
} else {
if (payload_config.hard_chunk_size == -1)
// Use 1 MiB chunk size for the full unittests.
@@ -576,26 +575,26 @@
payload_config.target.partitions.back().path = state->b_img->path();
payload_config.target.partitions.emplace_back(kPartitionNameKernel);
payload_config.target.partitions.back().path = state->new_kernel->path();
- EXPECT_TRUE(payload_config.target.LoadImageSize());
+ ASSERT_TRUE(payload_config.target.LoadImageSize());
for (PartitionConfig& part : payload_config.target.partitions)
- EXPECT_TRUE(part.OpenFilesystem());
+ ASSERT_TRUE(part.OpenFilesystem());
- EXPECT_TRUE(payload_config.Validate());
- EXPECT_TRUE(GenerateUpdatePayloadFile(payload_config,
+ ASSERT_TRUE(payload_config.Validate());
+ ASSERT_TRUE(GenerateUpdatePayloadFile(payload_config,
state->delta_file->path(),
private_key,
&state->metadata_size));
}
// Extend the "partitions" holding the file system a bit.
- EXPECT_EQ(0,
+ ASSERT_EQ(0,
HANDLE_EINTR(truncate(state->a_img->path().c_str(),
state->image_size + 1024 * 1024)));
- EXPECT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
+ ASSERT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
utils::FileSize(state->a_img->path()));
- EXPECT_EQ(0,
+ ASSERT_EQ(0,
HANDLE_EINTR(truncate(state->b_img->path().c_str(),
state->image_size + 1024 * 1024)));
- EXPECT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
+ ASSERT_EQ(static_cast<off_t>(state->image_size + 1024 * 1024),
utils::FileSize(state->b_img->path()));
if (signature_test == kSignatureGeneratedPlaceholder ||
@@ -643,9 +642,9 @@
uint32_t minor_version) {
// Check the metadata.
{
- EXPECT_TRUE(utils::ReadFile(state->delta_file->path(), &state->delta));
+ ASSERT_TRUE(utils::ReadFile(state->delta_file->path(), &state->delta));
PayloadMetadata payload_metadata;
- EXPECT_TRUE(payload_metadata.ParsePayloadHeader(state->delta));
+ ASSERT_TRUE(payload_metadata.ParsePayloadHeader(state->delta));
state->metadata_size = payload_metadata.GetMetadataSize();
LOG(INFO) << "Metadata size: " << state->metadata_size;
state->metadata_signature_size =
@@ -653,23 +652,23 @@
LOG(INFO) << "Metadata signature size: " << state->metadata_signature_size;
DeltaArchiveManifest manifest;
- EXPECT_TRUE(payload_metadata.GetManifest(state->delta, &manifest));
+ ASSERT_TRUE(payload_metadata.GetManifest(state->delta, &manifest));
if (signature_test == kSignatureNone) {
- EXPECT_FALSE(manifest.has_signatures_offset());
- EXPECT_FALSE(manifest.has_signatures_size());
+ ASSERT_FALSE(manifest.has_signatures_offset());
+ ASSERT_FALSE(manifest.has_signatures_size());
} else {
- EXPECT_TRUE(manifest.has_signatures_offset());
- EXPECT_TRUE(manifest.has_signatures_size());
+ ASSERT_TRUE(manifest.has_signatures_offset());
+ ASSERT_TRUE(manifest.has_signatures_size());
Signatures sigs_message;
- EXPECT_TRUE(sigs_message.ParseFromArray(
+ ASSERT_TRUE(sigs_message.ParseFromArray(
&state->delta[state->metadata_size + state->metadata_signature_size +
manifest.signatures_offset()],
manifest.signatures_size()));
if (signature_test == kSignatureGeneratedShellRotateCl1 ||
signature_test == kSignatureGeneratedShellRotateCl2)
- EXPECT_EQ(2, sigs_message.signatures_size());
+ ASSERT_EQ(2, sigs_message.signatures_size());
else
- EXPECT_EQ(1, sigs_message.signatures_size());
+ ASSERT_EQ(1, sigs_message.signatures_size());
const Signatures::Signature& signature = sigs_message.signatures(0);
vector<string> key_paths{GetBuildArtifactsPath(kUnittestPrivateKeyPath)};
@@ -680,10 +679,10 @@
key_paths.push_back(GetBuildArtifactsPath(kUnittestPrivateKey2Path));
}
uint64_t expected_sig_data_length = 0;
- EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
+ ASSERT_TRUE(PayloadSigner::SignatureBlobLength(
key_paths, &expected_sig_data_length));
- EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
- EXPECT_FALSE(signature.data().empty());
+ ASSERT_EQ(expected_sig_data_length, manifest.signatures_size());
+ ASSERT_FALSE(signature.data().empty());
}
// TODO(ahassani): Make |DeltaState| into a partition list kind of struct
@@ -696,15 +695,15 @@
return partition.partition_name() == kPartitionNameKernel;
});
if (full_kernel) {
- EXPECT_FALSE(kernel_part.has_old_partition_info());
+ ASSERT_FALSE(kernel_part.has_old_partition_info());
} else {
- EXPECT_EQ(state->old_kernel_data.size(),
+ ASSERT_EQ(state->old_kernel_data.size(),
kernel_part.old_partition_info().size());
- EXPECT_FALSE(kernel_part.old_partition_info().hash().empty());
+ ASSERT_FALSE(kernel_part.old_partition_info().hash().empty());
}
- EXPECT_EQ(state->new_kernel_data.size(),
+ ASSERT_EQ(state->new_kernel_data.size(),
kernel_part.new_partition_info().size());
- EXPECT_FALSE(kernel_part.new_partition_info().hash().empty());
+ ASSERT_FALSE(kernel_part.new_partition_info().hash().empty());
const auto& rootfs_part =
*std::find_if(manifest.partitions().begin(),
@@ -713,11 +712,11 @@
return partition.partition_name() == kPartitionNameRoot;
});
if (full_rootfs) {
- EXPECT_FALSE(rootfs_part.has_old_partition_info());
+ ASSERT_FALSE(rootfs_part.has_old_partition_info());
} else {
- EXPECT_FALSE(rootfs_part.old_partition_info().hash().empty());
+ ASSERT_FALSE(rootfs_part.old_partition_info().hash().empty());
}
- EXPECT_FALSE(rootfs_part.new_partition_info().hash().empty());
+ ASSERT_FALSE(rootfs_part.new_partition_info().hash().empty());
}
NiceMock<MockPrefs> prefs;
@@ -798,7 +797,7 @@
? GetBuildArtifactsPath(kUnittestPrivateKeyECPath)
: GetBuildArtifactsPath(kUnittestPrivateKeyPath),
&install_plan->payloads[0].metadata_signature));
- EXPECT_FALSE(install_plan->payloads[0].metadata_signature.empty());
+ ASSERT_FALSE(install_plan->payloads[0].metadata_signature.empty());
*performer = new DeltaPerformer(&prefs,
&state->fake_boot_control_,
@@ -810,15 +809,15 @@
string public_key_path = signature_test == kSignatureGeneratedShellECKey
? GetBuildArtifactsPath(kUnittestPublicKeyECPath)
: GetBuildArtifactsPath(kUnittestPublicKeyPath);
- EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
+ ASSERT_TRUE(utils::FileExists(public_key_path.c_str()));
(*performer)->set_public_key_path(public_key_path);
(*performer)->set_update_certificates_path("");
- EXPECT_EQ(
+ ASSERT_EQ(
static_cast<off_t>(state->image_size),
HashCalculator::RawHashOfFile(
state->a_img->path(), state->image_size, &root_part.source_hash));
- EXPECT_TRUE(HashCalculator::RawHashOfData(state->old_kernel_data,
+ ASSERT_TRUE(HashCalculator::RawHashOfData(state->old_kernel_data,
&kernel_part.source_hash));
// The partitions should be empty before DeltaPerformer.
@@ -872,23 +871,23 @@
// we cannot proceed applying the delta.
if (!write_succeeded) {
LOG(INFO) << "Write failed. Checking if it failed with expected error";
- EXPECT_EQ(expected_error, actual_error);
+ ASSERT_EQ(expected_error, actual_error);
if (!continue_writing) {
LOG(INFO) << "Cannot continue writing. Bailing out.";
break;
}
}
- EXPECT_EQ(ErrorCode::kSuccess, actual_error);
+ ASSERT_EQ(ErrorCode::kSuccess, actual_error);
}
// If we had continued all the way through, Close should succeed.
// Otherwise, it should fail. Check appropriately.
bool close_result = (*performer)->Close();
if (continue_writing)
- EXPECT_EQ(0, close_result);
+ ASSERT_EQ(0, close_result);
else
- EXPECT_LE(0, close_result);
+ ASSERT_LE(0, close_result);
}
void VerifyPayloadResult(DeltaPerformer* performer,
@@ -896,14 +895,14 @@
ErrorCode expected_result,
uint32_t minor_version) {
if (!performer) {
- EXPECT_TRUE(!"Skipping payload verification since performer is null.");
+ ASSERT_TRUE(!"Skipping payload verification since performer is null.");
return;
}
LOG(INFO) << "Verifying payload for expected result " << expected_result;
brillo::Blob expected_hash;
HashCalculator::RawHashOfData(state->delta, &expected_hash);
- EXPECT_EQ(expected_result,
+ ASSERT_EQ(expected_result,
performer->VerifyPayload(expected_hash, state->delta.size()));
LOG(INFO) << "Verified payload.";
@@ -919,31 +918,31 @@
state->result_img->path(), state->b_img->path(), state->image_size);
brillo::Blob updated_kernel_partition;
- EXPECT_TRUE(
+ ASSERT_TRUE(
utils::ReadFile(state->result_kernel->path(), &updated_kernel_partition));
ASSERT_GE(updated_kernel_partition.size(), base::size(kNewData));
- EXPECT_TRUE(std::equal(std::begin(kNewData),
+ ASSERT_TRUE(std::equal(std::begin(kNewData),
std::end(kNewData),
updated_kernel_partition.begin()));
const auto& partitions = state->install_plan.partitions;
- EXPECT_EQ(2U, partitions.size());
- EXPECT_EQ(kPartitionNameRoot, partitions[0].name);
- EXPECT_EQ(kPartitionNameKernel, partitions[1].name);
+ ASSERT_EQ(2U, partitions.size());
+ ASSERT_EQ(kPartitionNameRoot, partitions[0].name);
+ ASSERT_EQ(kPartitionNameKernel, partitions[1].name);
- EXPECT_EQ(kDefaultKernelSize, partitions[1].target_size);
+ ASSERT_EQ(kDefaultKernelSize, partitions[1].target_size);
brillo::Blob expected_new_kernel_hash;
- EXPECT_TRUE(HashCalculator::RawHashOfData(state->new_kernel_data,
+ ASSERT_TRUE(HashCalculator::RawHashOfData(state->new_kernel_data,
&expected_new_kernel_hash));
- EXPECT_EQ(expected_new_kernel_hash, partitions[1].target_hash);
+ ASSERT_EQ(expected_new_kernel_hash, partitions[1].target_hash);
- EXPECT_EQ(state->image_size, partitions[0].target_size);
+ ASSERT_EQ(state->image_size, partitions[0].target_size);
brillo::Blob expected_new_rootfs_hash;
- EXPECT_EQ(
+ ASSERT_EQ(
static_cast<off_t>(state->image_size),
HashCalculator::RawHashOfFile(
state->b_img->path(), state->image_size, &expected_new_rootfs_hash));
- EXPECT_EQ(expected_new_rootfs_hash, partitions[0].target_hash);
+ ASSERT_EQ(expected_new_rootfs_hash, partitions[0].target_hash);
}
void VerifyPayload(DeltaPerformer* performer,
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index 840ecf6..ed89f89 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -654,7 +654,9 @@
brillo::Blob payload_data =
GeneratePayload(brillo::Blob(), {aop}, false, &old_part);
- EXPECT_EQ(actual_data, ApplyPayload(payload_data, source.path(), false));
+ // When source hash mismatches, PartitionWriter will refuse to write anything.
+ // Therefore we should expect an empty blob.
+ EXPECT_EQ(brillo::Blob{}, ApplyPayload(payload_data, source.path(), false));
}
TEST_F(DeltaPerformerTest, ExtentsToByteStringTest) {
@@ -1114,7 +1116,7 @@
public:
using DeltaPerformer::DeltaPerformer;
- std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+ std::unique_ptr<PartitionWriterInterface> CreatePartitionWriter(
const PartitionUpdate& partition_update,
const InstallPlan::Partition& install_part,
DynamicPartitionControlInterface* dynamic_control,
diff --git a/payload_consumer/file_descriptor_utils.cc b/payload_consumer/file_descriptor_utils.cc
index 9a6a601..91b5673 100644
--- a/payload_consumer/file_descriptor_utils.cc
+++ b/payload_consumer/file_descriptor_utils.cc
@@ -35,9 +35,12 @@
// Size of the buffer used to copy blocks.
const uint64_t kMaxCopyBufferSize = 1024 * 1024;
+} // namespace
+namespace fd_utils {
+
bool CommonHashExtents(FileDescriptorPtr source,
const RepeatedPtrField<Extent>& src_extents,
- DirectExtentWriter* writer,
+ ExtentWriter* writer,
uint64_t block_size,
brillo::Blob* hash_out) {
auto total_blocks = utils::BlocksInExtents(src_extents);
@@ -72,10 +75,6 @@
return true;
}
-} // namespace
-
-namespace fd_utils {
-
bool CopyAndHashExtents(FileDescriptorPtr source,
const RepeatedPtrField<Extent>& src_extents,
FileDescriptorPtr target,
diff --git a/payload_consumer/file_descriptor_utils.h b/payload_consumer/file_descriptor_utils.h
index 68fb001..3a4027d 100644
--- a/payload_consumer/file_descriptor_utils.h
+++ b/payload_consumer/file_descriptor_utils.h
@@ -19,12 +19,20 @@
#include <brillo/secure_blob.h>
+#include "update_engine/payload_consumer/extent_writer.h"
#include "update_engine/payload_consumer/file_descriptor.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
namespace fd_utils {
+bool CommonHashExtents(
+ FileDescriptorPtr source,
+ const google::protobuf::RepeatedPtrField<Extent>& src_extents,
+ ExtentWriter* writer,
+ uint64_t block_size,
+ brillo::Blob* hash_out);
+
// Copy blocks from the |source| file to the |target| file and hashes the
// contents. The blocks to copy from the |source| to the |target| files are
// specified by the |src_extents| and |tgt_extents| list of Extents, which
diff --git a/payload_consumer/install_operation_executor.cc b/payload_consumer/install_operation_executor.cc
new file mode 100644
index 0000000..4bfcb5d
--- /dev/null
+++ b/payload_consumer/install_operation_executor.cc
@@ -0,0 +1,316 @@
+//
+// 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/install_operation_executor.h"
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <fcntl.h>
+#include <glob.h>
+#include <linux/fs.h>
+
+#include <base/files/memory_mapped_file.h>
+#include <bsdiff/bspatch.h>
+#include <puffin/puffpatch.h>
+#include <sys/mman.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/bzip_extent_writer.h"
+#include "update_engine/payload_consumer/cached_file_descriptor.h"
+#include "update_engine/payload_consumer/extent_reader.h"
+#include "update_engine/payload_consumer/extent_writer.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/file_descriptor_utils.h"
+#include "update_engine/payload_consumer/xz_extent_writer.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class BsdiffExtentFile : public bsdiff::FileInterface {
+ public:
+ BsdiffExtentFile(std::unique_ptr<ExtentReader> reader, size_t size)
+ : BsdiffExtentFile(std::move(reader), nullptr, size) {}
+ BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer, size_t size)
+ : BsdiffExtentFile(nullptr, std::move(writer), size) {}
+
+ ~BsdiffExtentFile() override = default;
+
+ bool Read(void* buf, size_t count, size_t* bytes_read) override {
+ TEST_AND_RETURN_FALSE(reader_->Read(buf, count));
+ *bytes_read = count;
+ offset_ += count;
+ return true;
+ }
+
+ bool Write(const void* buf, size_t count, size_t* bytes_written) override {
+ TEST_AND_RETURN_FALSE(writer_->Write(buf, count));
+ *bytes_written = count;
+ offset_ += count;
+ return true;
+ }
+
+ bool Seek(off_t pos) override {
+ if (reader_ != nullptr) {
+ TEST_AND_RETURN_FALSE(reader_->Seek(pos));
+ offset_ = pos;
+ } else {
+ // For writes technically there should be no change of position, or it
+ // should be equivalent of current offset.
+ TEST_AND_RETURN_FALSE(offset_ == static_cast<uint64_t>(pos));
+ }
+ return true;
+ }
+
+ bool Close() override { return true; }
+
+ bool GetSize(uint64_t* size) override {
+ *size = size_;
+ return true;
+ }
+
+ private:
+ BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,
+ std::unique_ptr<ExtentWriter> writer,
+ size_t size)
+ : reader_(std::move(reader)),
+ writer_(std::move(writer)),
+ size_(size),
+ offset_(0) {}
+
+ std::unique_ptr<ExtentReader> reader_;
+ std::unique_ptr<ExtentWriter> writer_;
+ uint64_t size_;
+ uint64_t offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(BsdiffExtentFile);
+};
+// A class to be passed to |puffpatch| for reading from |source_fd_| and writing
+// into |target_fd_|.
+class PuffinExtentStream : public puffin::StreamInterface {
+ public:
+ // Constructor for creating a stream for reading from an |ExtentReader|.
+ PuffinExtentStream(std::unique_ptr<ExtentReader> reader, uint64_t size)
+ : PuffinExtentStream(std::move(reader), nullptr, size) {}
+
+ // Constructor for creating a stream for writing to an |ExtentWriter|.
+ PuffinExtentStream(std::unique_ptr<ExtentWriter> writer, uint64_t size)
+ : PuffinExtentStream(nullptr, std::move(writer), size) {}
+
+ ~PuffinExtentStream() override = default;
+
+ bool GetSize(uint64_t* size) const override {
+ *size = size_;
+ return true;
+ }
+
+ bool GetOffset(uint64_t* offset) const override {
+ *offset = offset_;
+ return true;
+ }
+
+ bool Seek(uint64_t offset) override {
+ if (is_read_) {
+ TEST_AND_RETURN_FALSE(reader_->Seek(offset));
+ offset_ = offset;
+ } else {
+ // For writes technically there should be no change of position, or it
+ // should equivalent of current offset.
+ TEST_AND_RETURN_FALSE(offset_ == offset);
+ }
+ return true;
+ }
+
+ bool Read(void* buffer, size_t count) override {
+ TEST_AND_RETURN_FALSE(is_read_);
+ TEST_AND_RETURN_FALSE(reader_->Read(buffer, count));
+ offset_ += count;
+ return true;
+ }
+
+ bool Write(const void* buffer, size_t count) override {
+ TEST_AND_RETURN_FALSE(!is_read_);
+ TEST_AND_RETURN_FALSE(writer_->Write(buffer, count));
+ offset_ += count;
+ return true;
+ }
+
+ bool Close() override { return true; }
+
+ private:
+ PuffinExtentStream(std::unique_ptr<ExtentReader> reader,
+ std::unique_ptr<ExtentWriter> writer,
+ uint64_t size)
+ : reader_(std::move(reader)),
+ writer_(std::move(writer)),
+ size_(size),
+ offset_(0),
+ is_read_(reader_ ? true : false) {}
+
+ std::unique_ptr<ExtentReader> reader_;
+ std::unique_ptr<ExtentWriter> writer_;
+ uint64_t size_;
+ uint64_t offset_;
+ bool is_read_;
+
+ DISALLOW_COPY_AND_ASSIGN(PuffinExtentStream);
+};
+
+bool InstallOperationExecutor::ExecuteInstallOp(
+ const InstallOperation& op,
+ std::unique_ptr<ExtentWriter> writer,
+ FileDescriptorPtr source_fd,
+ const void* data,
+ size_t size) {
+ switch (op.type()) {
+ case InstallOperation::REPLACE:
+ case InstallOperation::REPLACE_BZ:
+ case InstallOperation::REPLACE_XZ:
+ return ExecuteReplaceOperation(op, std::move(writer), data, size);
+ case InstallOperation::ZERO:
+ case InstallOperation::DISCARD:
+ return ExecuteZeroOrDiscardOperation(op, writer.get());
+ case InstallOperation::SOURCE_COPY:
+ return ExecuteSourceCopyOperation(op, writer.get(), source_fd);
+ case InstallOperation::SOURCE_BSDIFF:
+ case InstallOperation::BROTLI_BSDIFF:
+ return ExecuteSourceBsdiffOperation(
+ op, std::move(writer), source_fd, data, size);
+ case InstallOperation::PUFFDIFF:
+ return ExecutePuffDiffOperation(
+ op, std::move(writer), source_fd, data, size);
+ break;
+ default:
+ return false;
+ }
+ return false;
+}
+
+bool InstallOperationExecutor::ExecuteReplaceOperation(
+ const InstallOperation& operation,
+ std::unique_ptr<ExtentWriter> writer,
+ const void* data,
+ size_t count) {
+ TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::REPLACE ||
+ operation.type() == InstallOperation::REPLACE_BZ ||
+ operation.type() == InstallOperation::REPLACE_XZ);
+ // Setup the ExtentWriter stack based on the operation type.
+ if (operation.type() == InstallOperation::REPLACE_BZ) {
+ writer.reset(new BzipExtentWriter(std::move(writer)));
+ } else if (operation.type() == InstallOperation::REPLACE_XZ) {
+ writer.reset(new XzExtentWriter(std::move(writer)));
+ }
+ TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
+ TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
+
+ return true;
+}
+
+bool InstallOperationExecutor::ExecuteZeroOrDiscardOperation(
+ const InstallOperation& operation, ExtentWriter* writer) {
+ TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::ZERO ||
+ operation.type() == InstallOperation::DISCARD);
+ using base::MemoryMappedFile;
+ using Access = base::MemoryMappedFile::Access;
+ using Region = base::MemoryMappedFile::Region;
+ writer->Init(operation.dst_extents(), block_size_);
+ for (const auto& extent : operation.dst_extents()) {
+ // Mmap a region of /dev/zero, as we don't need any actual memory to store
+ // these 0s, so mmap a region of "free memory".
+ base::File dev_zero(base::FilePath("/dev/zero"),
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+ MemoryMappedFile buffer;
+ TEST_AND_RETURN_FALSE_ERRNO(buffer.Initialize(
+ std::move(dev_zero),
+ Region{0, static_cast<size_t>(extent.num_blocks() * block_size_)},
+ Access::READ_ONLY));
+ writer->Write(buffer.data(), buffer.length());
+ }
+ return true;
+}
+
+bool InstallOperationExecutor::ExecuteSourceCopyOperation(
+ const InstallOperation& operation,
+ ExtentWriter* writer,
+ FileDescriptorPtr source_fd) {
+ TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::SOURCE_COPY);
+ TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
+ return fd_utils::CommonHashExtents(
+ source_fd, operation.src_extents(), writer, block_size_, nullptr);
+}
+
+bool InstallOperationExecutor::ExecuteSourceBsdiffOperation(
+ const InstallOperation& operation,
+ std::unique_ptr<ExtentWriter> writer,
+ FileDescriptorPtr source_fd,
+ const void* data,
+ size_t count) {
+ TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::SOURCE_BSDIFF ||
+ operation.type() == InstallOperation::BROTLI_BSDIFF ||
+ operation.type() == InstallOperation::BSDIFF);
+ TEST_AND_RETURN_FALSE(source_fd != nullptr);
+
+ auto reader = std::make_unique<DirectExtentReader>();
+ TEST_AND_RETURN_FALSE(
+ reader->Init(source_fd, operation.src_extents(), block_size_));
+ auto src_file = std::make_unique<BsdiffExtentFile>(
+ std::move(reader),
+ utils::BlocksInExtents(operation.src_extents()) * block_size_);
+
+ TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
+ auto dst_file = std::make_unique<BsdiffExtentFile>(
+ std::move(writer),
+ utils::BlocksInExtents(operation.dst_extents()) * block_size_);
+
+ TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
+ std::move(dst_file),
+ reinterpret_cast<const uint8_t*>(data),
+ count) == 0);
+ return true;
+}
+
+bool InstallOperationExecutor::ExecutePuffDiffOperation(
+ const InstallOperation& operation,
+ std::unique_ptr<ExtentWriter> writer,
+ FileDescriptorPtr source_fd,
+ const void* data,
+ size_t count) {
+ TEST_AND_RETURN_FALSE(operation.type() == InstallOperation::PUFFDIFF);
+ TEST_AND_RETURN_FALSE(source_fd != nullptr);
+
+ auto reader = std::make_unique<DirectExtentReader>();
+ TEST_AND_RETURN_FALSE(
+ reader->Init(source_fd, operation.src_extents(), block_size_));
+ puffin::UniqueStreamPtr src_stream(new PuffinExtentStream(
+ std::move(reader),
+ utils::BlocksInExtents(operation.src_extents()) * block_size_));
+
+ TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
+ puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
+ std::move(writer),
+ utils::BlocksInExtents(operation.dst_extents()) * block_size_));
+
+ constexpr size_t kMaxCacheSize = 5 * 1024 * 1024; // Total 5MB cache.
+ TEST_AND_RETURN_FALSE(
+ puffin::PuffPatch(std::move(src_stream),
+ std::move(dst_stream),
+ reinterpret_cast<const uint8_t*>(data),
+ count,
+ kMaxCacheSize));
+ return true;
+}
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/install_operation_executor.h b/payload_consumer/install_operation_executor.h
new file mode 100644
index 0000000..a43139b
--- /dev/null
+++ b/payload_consumer/install_operation_executor.h
@@ -0,0 +1,64 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_INSTALL_OPERATION_EXECUTOR_H
+#define UPDATE_ENGINE_INSTALL_OPERATION_EXECUTOR_H
+
+#include <memory>
+
+#include "update_engine/payload_consumer/extent_writer.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+class InstallOperationExecutor {
+ public:
+ explicit InstallOperationExecutor(size_t block_size)
+ : block_size_(block_size) {}
+
+ bool ExecuteInstallOp(const InstallOperation& op,
+ std::unique_ptr<ExtentWriter> writer,
+ FileDescriptorPtr source_fd,
+ const void* data,
+ size_t size);
+ bool ExecuteReplaceOperation(const InstallOperation& operation,
+ std::unique_ptr<ExtentWriter> writer,
+ const void* data,
+ size_t count);
+ bool ExecuteZeroOrDiscardOperation(const InstallOperation& operation,
+ ExtentWriter* writer);
+ bool ExecuteSourceCopyOperation(const InstallOperation& operation,
+ ExtentWriter* writer,
+ FileDescriptorPtr source_fd);
+ bool ExecuteSourceBsdiffOperation(const InstallOperation& operation,
+ std::unique_ptr<ExtentWriter> writer,
+ FileDescriptorPtr source_fd,
+ const void* data,
+ size_t count);
+ bool ExecutePuffDiffOperation(const InstallOperation& operation,
+ std::unique_ptr<ExtentWriter> writer,
+ FileDescriptorPtr source_fd,
+ const void* data,
+ size_t count);
+
+ private:
+ size_t block_size_;
+};
+
+} // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/install_operation_executor_unittest.cc b/payload_consumer/install_operation_executor_unittest.cc
new file mode 100644
index 0000000..2a99782
--- /dev/null
+++ b/payload_consumer/install_operation_executor_unittest.cc
@@ -0,0 +1,208 @@
+//
+// 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/install_operation_executor.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <ostream>
+#include <utility>
+#include <vector>
+
+#include <brillo/secure_blob.h>
+#include <gtest/gtest.h>
+#include <update_engine/update_metadata.pb.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/extent_writer.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/extent_ranges.h"
+#include "update_engine/payload_generator/extent_utils.h"
+
+namespace chromeos_update_engine {
+
+std::ostream& operator<<(std::ostream& out,
+ const chromeos_update_engine::InstallOperation& op) {
+ out << InstallOperationTypeName(op.type())
+ << " SRC: " << ExtentsToString(op.src_extents())
+ << " DST: " << ExtentsToString(op.dst_extents());
+ return out;
+}
+
+namespace {} // namespace
+
+class InstallOperationExecutorTest : public ::testing::Test {
+ public:
+ static constexpr size_t NUM_BLOCKS = 10;
+ static constexpr size_t BLOCK_SIZE = 4096;
+ void SetUp() override {
+ // Fill source partition with arbitrary data.
+ std::array<uint8_t, BLOCK_SIZE> buffer{};
+ for (size_t i = 0; i < NUM_BLOCKS; i++) {
+ // Fill block with arbitrary data. We don't care about what data is being
+ // written to source partition, so as long as each block is slightly
+ // different.
+ std::fill(buffer.begin(), buffer.end(), i);
+ ASSERT_TRUE(utils::WriteAll(source_.fd(), buffer.data(), buffer.size()))
+ << "Failed to write to source partition file: " << strerror(errno);
+ std::fill(buffer.begin(), buffer.end(), NUM_BLOCKS + i);
+ ASSERT_TRUE(utils::WriteAll(target_.fd(), buffer.data(), buffer.size()))
+ << "Failed to write to target partition file: " << strerror(errno);
+ }
+ fsync(source_.fd());
+ fsync(target_.fd());
+
+ // set target partition to have same size as source partition.
+ // update_engine mostly assumes that target partition have the desired
+ // size, so we mock that.
+ ASSERT_GE(ftruncate64(target_.fd(), NUM_BLOCKS * BLOCK_SIZE), 0)
+ << strerror(errno) << " failed to set target partition size to "
+ << NUM_BLOCKS * BLOCK_SIZE;
+
+ source_fd_->Open(source_.path().c_str(), O_RDONLY);
+ target_fd_->Open(target_.path().c_str(), O_RDWR);
+ }
+
+ void VerityUntouchedExtents(const InstallOperation& op) {
+ ExtentRanges extent_set;
+ extent_set.AddExtent(ExtentForRange(0, 10));
+ extent_set.SubtractRepeatedExtents(op.dst_extents());
+ std::vector<Extent> untouched_extents{extent_set.extent_set().begin(),
+ extent_set.extent_set().end()};
+ brillo::Blob actual_data;
+ ASSERT_TRUE(utils::ReadExtents(target_.path(),
+ untouched_extents,
+ &actual_data,
+ extent_set.blocks() * BLOCK_SIZE,
+ BLOCK_SIZE));
+ const auto untouched_blocks = ExpandExtents(untouched_extents);
+ for (size_t i = 0; i < actual_data.size(); i++) {
+ const auto block_offset = i / BLOCK_SIZE;
+ const auto offset = i % BLOCK_SIZE;
+ ASSERT_EQ(
+ actual_data[i],
+ static_cast<uint8_t>(NUM_BLOCKS + untouched_blocks[block_offset]))
+ << "After performing op " << op << ", offset " << offset
+ << " in block " << GetNthBlock(untouched_extents, block_offset)
+ << " is modified but it shouldn't.";
+ }
+ }
+ ScopedTempFile source_{"source_partition.XXXXXXXX", true};
+ ScopedTempFile target_{"target_partition.XXXXXXXX", true};
+ FileDescriptorPtr source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
+ FileDescriptorPtr target_fd_ = std::make_shared<EintrSafeFileDescriptor>();
+ InstallOperationExecutor executor_{BLOCK_SIZE};
+};
+
+TEST_F(InstallOperationExecutorTest, ReplaceOpTest) {
+ InstallOperation op;
+ op.set_type(InstallOperation::REPLACE);
+ *op.mutable_dst_extents()->Add() = ExtentForRange(2, 2);
+ *op.mutable_dst_extents()->Add() = ExtentForRange(6, 2);
+ op.set_data_length(BLOCK_SIZE * 4);
+ brillo::Blob expected_data;
+ expected_data.resize(BLOCK_SIZE * 4);
+ // Fill buffer with arbitrary data. Doesn't matter what it is. Each block
+ // needs to be different so that we can ensure the InstallOperationExecutor
+ // is reading data from the correct offset.
+ for (int i = 0; i < 4; i++) {
+ std::fill(&expected_data[i * BLOCK_SIZE],
+ &expected_data[(i + 1) * BLOCK_SIZE],
+ i + 99);
+ }
+ auto writer = std::make_unique<DirectExtentWriter>(target_fd_);
+ ASSERT_TRUE(executor_.ExecuteReplaceOperation(
+ op, std::move(writer), expected_data.data(), expected_data.size()));
+
+ brillo::Blob actual_data;
+ utils::ReadExtents(
+ target_.path(),
+ std::vector<Extent>{op.dst_extents().begin(), op.dst_extents().end()},
+ &actual_data,
+ BLOCK_SIZE * 4,
+ BLOCK_SIZE);
+ ASSERT_EQ(actual_data, expected_data);
+ VerityUntouchedExtents(op);
+}
+
+TEST_F(InstallOperationExecutorTest, ZeroOrDiscardeOpTest) {
+ InstallOperation op;
+ op.set_type(InstallOperation::ZERO);
+ *op.mutable_dst_extents()->Add() = ExtentForRange(2, 2);
+ *op.mutable_dst_extents()->Add() = ExtentForRange(6, 2);
+ auto writer = std::make_unique<DirectExtentWriter>(target_fd_);
+ ASSERT_TRUE(executor_.ExecuteZeroOrDiscardOperation(op, writer.get()));
+ brillo::Blob actual_data;
+ utils::ReadExtents(
+ target_.path(),
+ std::vector<Extent>{op.dst_extents().begin(), op.dst_extents().end()},
+ &actual_data,
+ BLOCK_SIZE * 4,
+ BLOCK_SIZE);
+ for (size_t i = 0; i < actual_data.size(); i++) {
+ ASSERT_EQ(actual_data[i], 0U) << "position " << i << " isn't zeroed!";
+ }
+ VerityUntouchedExtents(op);
+}
+
+TEST_F(InstallOperationExecutorTest, SourceCopyOpTest) {
+ InstallOperation op;
+ op.set_type(InstallOperation::SOURCE_COPY);
+ *op.mutable_src_extents()->Add() = ExtentForRange(1, 2);
+ *op.mutable_src_extents()->Add() = ExtentForRange(5, 1);
+ *op.mutable_src_extents()->Add() = ExtentForRange(7, 1);
+
+ *op.mutable_dst_extents()->Add() = ExtentForRange(2, 2);
+ *op.mutable_dst_extents()->Add() = ExtentForRange(6, 2);
+
+ auto writer = std::make_unique<DirectExtentWriter>(target_fd_);
+ ASSERT_TRUE(
+ executor_.ExecuteSourceCopyOperation(op, writer.get(), source_fd_));
+ brillo::Blob actual_data;
+ utils::ReadExtents(
+ target_.path(),
+ std::vector<Extent>{op.dst_extents().begin(), op.dst_extents().end()},
+ &actual_data,
+ BLOCK_SIZE * 4,
+ BLOCK_SIZE);
+ brillo::Blob expected_data;
+ utils::ReadExtents(
+ source_.path(),
+ std::vector<Extent>{op.src_extents().begin(), op.src_extents().end()},
+ &expected_data,
+ BLOCK_SIZE * 4,
+ BLOCK_SIZE);
+
+ ASSERT_EQ(expected_data.size(), actual_data.size());
+ for (size_t i = 0; i < actual_data.size(); i++) {
+ const auto block_offset = i / BLOCK_SIZE;
+ const auto offset = i % BLOCK_SIZE;
+ ASSERT_EQ(actual_data[i], expected_data[i])
+ << "After performing op " << op << ", offset " << offset << " in ["
+ << GetNthBlock(op.src_extents(), block_offset) << " -> "
+ << GetNthBlock(op.dst_extents(), block_offset) << "]"
+ << " is not copied correctly";
+ }
+ VerityUntouchedExtents(op);
+}
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_writer.cc b/payload_consumer/partition_writer.cc
index 6f98ba3..7922eb2 100644
--- a/payload_consumer/partition_writer.cc
+++ b/payload_consumer/partition_writer.cc
@@ -17,18 +17,20 @@
#include <fcntl.h>
#include <linux/fs.h>
+#include <sys/mman.h>
+
+#include <inttypes.h>
#include <algorithm>
#include <initializer_list>
#include <memory>
+#include <string>
#include <utility>
#include <vector>
#include <base/strings/string_number_conversions.h>
-#include <bsdiff/bspatch.h>
-#include <puffin/puffpatch.h>
-#include <bsdiff/file_interface.h>
-#include <puffin/stream.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
#include "update_engine/common/terminator.h"
#include "update_engine/common/utils.h"
@@ -36,12 +38,13 @@
#include "update_engine/payload_consumer/cached_file_descriptor.h"
#include "update_engine/payload_consumer/extent_reader.h"
#include "update_engine/payload_consumer/extent_writer.h"
-#include "update_engine/payload_consumer/fec_file_descriptor.h"
#include "update_engine/payload_consumer/file_descriptor_utils.h"
+#include "update_engine/payload_consumer/install_operation_executor.h"
#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/payload_consumer/mount_history.h"
#include "update_engine/payload_consumer/payload_constants.h"
#include "update_engine/payload_consumer/xz_extent_writer.h"
+#include "update_engine/payload_generator/extent_utils.h"
namespace chromeos_update_engine {
@@ -83,6 +86,8 @@
} // namespace
+using google::protobuf::RepeatedPtrField;
+
// Opens path for read/write. On success returns an open FileDescriptor
// and sets *err to 0. On failure, sets *err to errno and returns nullptr.
FileDescriptorPtr OpenFile(const char* path,
@@ -108,135 +113,6 @@
return fd;
}
-class BsdiffExtentFile : public bsdiff::FileInterface {
- public:
- BsdiffExtentFile(std::unique_ptr<ExtentReader> reader, size_t size)
- : BsdiffExtentFile(std::move(reader), nullptr, size) {}
- BsdiffExtentFile(std::unique_ptr<ExtentWriter> writer, size_t size)
- : BsdiffExtentFile(nullptr, std::move(writer), size) {}
-
- ~BsdiffExtentFile() override = default;
-
- bool Read(void* buf, size_t count, size_t* bytes_read) override {
- TEST_AND_RETURN_FALSE(reader_->Read(buf, count));
- *bytes_read = count;
- offset_ += count;
- return true;
- }
-
- bool Write(const void* buf, size_t count, size_t* bytes_written) override {
- TEST_AND_RETURN_FALSE(writer_->Write(buf, count));
- *bytes_written = count;
- offset_ += count;
- return true;
- }
-
- bool Seek(off_t pos) override {
- if (reader_ != nullptr) {
- TEST_AND_RETURN_FALSE(reader_->Seek(pos));
- offset_ = pos;
- } else {
- // For writes technically there should be no change of position, or it
- // should be equivalent of current offset.
- TEST_AND_RETURN_FALSE(offset_ == static_cast<uint64_t>(pos));
- }
- return true;
- }
-
- bool Close() override { return true; }
-
- bool GetSize(uint64_t* size) override {
- *size = size_;
- return true;
- }
-
- private:
- BsdiffExtentFile(std::unique_ptr<ExtentReader> reader,
- std::unique_ptr<ExtentWriter> writer,
- size_t size)
- : reader_(std::move(reader)),
- writer_(std::move(writer)),
- size_(size),
- offset_(0) {}
-
- std::unique_ptr<ExtentReader> reader_;
- std::unique_ptr<ExtentWriter> writer_;
- uint64_t size_;
- uint64_t offset_;
-
- DISALLOW_COPY_AND_ASSIGN(BsdiffExtentFile);
-};
-// A class to be passed to |puffpatch| for reading from |source_fd_| and writing
-// into |target_fd_|.
-class PuffinExtentStream : public puffin::StreamInterface {
- public:
- // Constructor for creating a stream for reading from an |ExtentReader|.
- PuffinExtentStream(std::unique_ptr<ExtentReader> reader, uint64_t size)
- : PuffinExtentStream(std::move(reader), nullptr, size) {}
-
- // Constructor for creating a stream for writing to an |ExtentWriter|.
- PuffinExtentStream(std::unique_ptr<ExtentWriter> writer, uint64_t size)
- : PuffinExtentStream(nullptr, std::move(writer), size) {}
-
- ~PuffinExtentStream() override = default;
-
- bool GetSize(uint64_t* size) const override {
- *size = size_;
- return true;
- }
-
- bool GetOffset(uint64_t* offset) const override {
- *offset = offset_;
- return true;
- }
-
- bool Seek(uint64_t offset) override {
- if (is_read_) {
- TEST_AND_RETURN_FALSE(reader_->Seek(offset));
- offset_ = offset;
- } else {
- // For writes technically there should be no change of position, or it
- // should equivalent of current offset.
- TEST_AND_RETURN_FALSE(offset_ == offset);
- }
- return true;
- }
-
- bool Read(void* buffer, size_t count) override {
- TEST_AND_RETURN_FALSE(is_read_);
- TEST_AND_RETURN_FALSE(reader_->Read(buffer, count));
- offset_ += count;
- return true;
- }
-
- bool Write(const void* buffer, size_t count) override {
- TEST_AND_RETURN_FALSE(!is_read_);
- TEST_AND_RETURN_FALSE(writer_->Write(buffer, count));
- offset_ += count;
- return true;
- }
-
- bool Close() override { return true; }
-
- private:
- PuffinExtentStream(std::unique_ptr<ExtentReader> reader,
- std::unique_ptr<ExtentWriter> writer,
- uint64_t size)
- : reader_(std::move(reader)),
- writer_(std::move(writer)),
- size_(size),
- offset_(0),
- is_read_(reader_ ? true : false) {}
-
- std::unique_ptr<ExtentReader> reader_;
- std::unique_ptr<ExtentWriter> writer_;
- uint64_t size_;
- uint64_t offset_;
- bool is_read_;
-
- DISALLOW_COPY_AND_ASSIGN(PuffinExtentStream);
-};
-
PartitionWriter::PartitionWriter(
const PartitionUpdate& partition_update,
const InstallPlan::Partition& install_part,
@@ -246,8 +122,10 @@
: partition_update_(partition_update),
install_part_(install_part),
dynamic_control_(dynamic_control),
+ verified_source_fd_(block_size, install_part.source_path),
interactive_(is_interactive),
- block_size_(block_size) {}
+ block_size_(block_size),
+ install_op_executor_(block_size) {}
PartitionWriter::~PartitionWriter() {
Close();
@@ -261,9 +139,7 @@
}
if (install_part_.source_size > 0 && !install_part_.source_path.empty()) {
source_path_ = install_part_.source_path;
- int err;
- source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
- if (source_fd_ == nullptr) {
+ if (!verified_source_fd_.Open()) {
LOG(ERROR) << "Unable to open source partition " << install_part_.name
<< " on slot " << BootControlInterface::SlotName(source_slot)
<< ", file " << source_path_;
@@ -319,161 +195,67 @@
size_t count) {
// Setup the ExtentWriter stack based on the operation type.
std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
-
- if (operation.type() == InstallOperation::REPLACE_BZ) {
- writer.reset(new BzipExtentWriter(std::move(writer)));
- } else if (operation.type() == InstallOperation::REPLACE_XZ) {
- writer.reset(new XzExtentWriter(std::move(writer)));
- }
-
- TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
- TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
-
- return true;
+ writer->Init(operation.dst_extents(), block_size_);
+ return install_op_executor_.ExecuteReplaceOperation(
+ operation, std::move(writer), data, count);
}
bool PartitionWriter::PerformZeroOrDiscardOperation(
const InstallOperation& operation) {
#ifdef BLKZEROOUT
- bool attempt_ioctl = true;
int request =
(operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
#else // !defined(BLKZEROOUT)
- bool attempt_ioctl = false;
- int request = 0;
+ auto writer = CreateBaseExtentWriter();
+ return install_op_executor_.ExecuteZeroOrDiscardOperation(operation,
+ writer.get());
#endif // !defined(BLKZEROOUT)
- brillo::Blob zeros;
for (const Extent& extent : operation.dst_extents()) {
const uint64_t start = extent.start_block() * block_size_;
const uint64_t length = extent.num_blocks() * block_size_;
- if (attempt_ioctl) {
- int result = 0;
- if (target_fd_->BlkIoctl(request, start, length, &result) && result == 0)
- continue;
- attempt_ioctl = false;
+ int result = 0;
+ if (target_fd_->BlkIoctl(request, start, length, &result) && result == 0) {
+ continue;
}
- // In case of failure, we fall back to writing 0 to the selected region.
- zeros.resize(16 * block_size_);
- for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
- uint64_t chunk_length =
- std::min(length - offset, static_cast<uint64_t>(zeros.size()));
- TEST_AND_RETURN_FALSE(utils::WriteAll(
- target_fd_, zeros.data(), chunk_length, start + offset));
- }
+ // In case of failure, we fall back to writing 0 for the entire operation.
+ PLOG(WARNING) << "BlkIoctl failed. Falling back to write 0s for remainder "
+ "of this operation.";
+ auto writer = CreateBaseExtentWriter();
+ writer->Init(operation.dst_extents(), block_size_);
+ return install_op_executor_.ExecuteZeroOrDiscardOperation(operation,
+ writer.get());
}
return true;
}
bool PartitionWriter::PerformSourceCopyOperation(
const InstallOperation& operation, ErrorCode* error) {
- TEST_AND_RETURN_FALSE(source_fd_ != nullptr);
-
// The device may optimize the SOURCE_COPY operation.
// Being this a device-specific optimization let DynamicPartitionController
// decide it the operation should be skipped.
const PartitionUpdate& partition = partition_update_;
- const auto& partition_control = dynamic_control_;
InstallOperation buf;
- const bool should_optimize = partition_control->OptimizeOperation(
+ const bool should_optimize = dynamic_control_->OptimizeOperation(
partition.partition_name(), operation, &buf);
const InstallOperation& optimized = should_optimize ? buf : operation;
- if (operation.has_src_sha256_hash()) {
- bool read_ok;
- brillo::Blob source_hash;
- brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
- operation.src_sha256_hash().end());
-
- // We fall back to use the error corrected device if the hash of the raw
- // device doesn't match or there was an error reading the source partition.
- // Note that this code will also fall back if writing the target partition
- // fails.
- if (should_optimize) {
- // Hash operation.src_extents(), then copy optimized.src_extents to
- // optimized.dst_extents.
- read_ok =
- fd_utils::ReadAndHashExtents(
- source_fd_, operation.src_extents(), block_size_, &source_hash) &&
- fd_utils::CopyAndHashExtents(source_fd_,
- optimized.src_extents(),
- target_fd_,
- optimized.dst_extents(),
- block_size_,
- nullptr /* skip hashing */);
- } else {
- read_ok = fd_utils::CopyAndHashExtents(source_fd_,
- operation.src_extents(),
- target_fd_,
- operation.dst_extents(),
- block_size_,
- &source_hash);
- }
- if (read_ok && expected_source_hash == source_hash)
- return true;
- LOG(WARNING) << "Source hash from RAW device mismatched, attempting to "
- "correct using ECC";
- if (!OpenCurrentECCPartition()) {
- // The following function call will return false since the source hash
- // mismatches, but we still want to call it so it prints the appropriate
- // log message.
- return ValidateSourceHash(source_hash, operation, source_fd_, error);
- }
-
- LOG(WARNING) << "Source hash from RAW device mismatched: found "
- << base::HexEncode(source_hash.data(), source_hash.size())
- << ", expected "
- << base::HexEncode(expected_source_hash.data(),
- expected_source_hash.size());
- if (should_optimize) {
- TEST_AND_RETURN_FALSE(fd_utils::ReadAndHashExtents(
- source_ecc_fd_, operation.src_extents(), block_size_, &source_hash));
- TEST_AND_RETURN_FALSE(
- fd_utils::CopyAndHashExtents(source_ecc_fd_,
- optimized.src_extents(),
- target_fd_,
- optimized.dst_extents(),
- block_size_,
- nullptr /* skip hashing */));
- } else {
- TEST_AND_RETURN_FALSE(
- fd_utils::CopyAndHashExtents(source_ecc_fd_,
- operation.src_extents(),
- target_fd_,
- operation.dst_extents(),
- block_size_,
- &source_hash));
- }
- TEST_AND_RETURN_FALSE(
- ValidateSourceHash(source_hash, operation, source_ecc_fd_, error));
- // At this point reading from the error corrected device worked, but
- // reading from the raw device failed, so this is considered a recovered
- // failure.
- source_ecc_recovered_failures_++;
- } else {
- // When the operation doesn't include a source hash, we attempt the error
- // corrected device first since we can't verify the block in the raw device
- // at this point, but we fall back to the raw device since the error
- // corrected device can be shorter or not available.
-
- if (OpenCurrentECCPartition() &&
- fd_utils::CopyAndHashExtents(source_ecc_fd_,
- optimized.src_extents(),
- target_fd_,
- optimized.dst_extents(),
- block_size_,
- nullptr)) {
- return true;
- }
- TEST_AND_RETURN_FALSE(fd_utils::CopyAndHashExtents(source_fd_,
- optimized.src_extents(),
- target_fd_,
- optimized.dst_extents(),
- block_size_,
- nullptr));
+ // Invoke ChooseSourceFD with original operation, so that it can properly
+ // verify source hashes. Optimized operation might contain a smaller set of
+ // extents, or completely empty.
+ auto source_fd = ChooseSourceFD(operation, error);
+ if (source_fd == nullptr) {
+ LOG(ERROR) << "Unrecoverable source hash mismatch found on partition "
+ << partition.partition_name()
+ << " extents: " << ExtentsToString(operation.src_extents());
+ return false;
}
- return true;
+
+ auto writer = CreateBaseExtentWriter();
+ writer->Init(optimized.dst_extents(), block_size_);
+ return install_op_executor_.ExecuteSourceCopyOperation(
+ optimized, writer.get(), source_fd);
}
bool PartitionWriter::PerformSourceBsdiffOperation(
@@ -484,24 +266,10 @@
FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
TEST_AND_RETURN_FALSE(source_fd != nullptr);
- auto reader = std::make_unique<DirectExtentReader>();
- TEST_AND_RETURN_FALSE(
- reader->Init(source_fd, operation.src_extents(), block_size_));
- auto src_file = std::make_unique<BsdiffExtentFile>(
- std::move(reader),
- utils::BlocksInExtents(operation.src_extents()) * block_size_);
-
auto writer = CreateBaseExtentWriter();
- TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
- auto dst_file = std::make_unique<BsdiffExtentFile>(
- std::move(writer),
- utils::BlocksInExtents(operation.dst_extents()) * block_size_);
-
- TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
- std::move(dst_file),
- reinterpret_cast<const uint8_t*>(data),
- count) == 0);
- return true;
+ writer->Init(operation.dst_extents(), block_size_);
+ return install_op_executor_.ExecuteSourceBsdiffOperation(
+ operation, std::move(writer), source_fd, data, count);
}
bool PartitionWriter::PerformPuffDiffOperation(
@@ -512,122 +280,20 @@
FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
TEST_AND_RETURN_FALSE(source_fd != nullptr);
- auto reader = std::make_unique<DirectExtentReader>();
- TEST_AND_RETURN_FALSE(
- reader->Init(source_fd, operation.src_extents(), block_size_));
- puffin::UniqueStreamPtr src_stream(new PuffinExtentStream(
- std::move(reader),
- utils::BlocksInExtents(operation.src_extents()) * block_size_));
-
auto writer = CreateBaseExtentWriter();
- TEST_AND_RETURN_FALSE(writer->Init(operation.dst_extents(), block_size_));
- puffin::UniqueStreamPtr dst_stream(new PuffinExtentStream(
- std::move(writer),
- utils::BlocksInExtents(operation.dst_extents()) * block_size_));
-
- constexpr size_t kMaxCacheSize = 5 * 1024 * 1024; // Total 5MB cache.
- TEST_AND_RETURN_FALSE(
- puffin::PuffPatch(std::move(src_stream),
- std::move(dst_stream),
- reinterpret_cast<const uint8_t*>(data),
- count,
- kMaxCacheSize));
- return true;
+ writer->Init(operation.dst_extents(), block_size_);
+ return install_op_executor_.ExecutePuffDiffOperation(
+ operation, std::move(writer), source_fd, data, count);
}
FileDescriptorPtr PartitionWriter::ChooseSourceFD(
const InstallOperation& operation, ErrorCode* error) {
- if (source_fd_ == nullptr) {
- LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
- return nullptr;
- }
-
- if (!operation.has_src_sha256_hash()) {
- // When the operation doesn't include a source hash, we attempt the error
- // corrected device first since we can't verify the block in the raw device
- // at this point, but we first need to make sure all extents are readable
- // since the error corrected device can be shorter or not available.
- if (OpenCurrentECCPartition() &&
- fd_utils::ReadAndHashExtents(
- source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
- return source_ecc_fd_;
- }
- return source_fd_;
- }
-
- brillo::Blob source_hash;
- brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
- operation.src_sha256_hash().end());
- if (fd_utils::ReadAndHashExtents(
- source_fd_, operation.src_extents(), block_size_, &source_hash) &&
- source_hash == expected_source_hash) {
- return source_fd_;
- }
- // We fall back to use the error corrected device if the hash of the raw
- // device doesn't match or there was an error reading the source partition.
- if (!OpenCurrentECCPartition()) {
- // The following function call will return false since the source hash
- // mismatches, but we still want to call it so it prints the appropriate
- // log message.
- ValidateSourceHash(source_hash, operation, source_fd_, error);
- return nullptr;
- }
- LOG(WARNING) << "Source hash from RAW device mismatched: found "
- << base::HexEncode(source_hash.data(), source_hash.size())
- << ", expected "
- << base::HexEncode(expected_source_hash.data(),
- expected_source_hash.size());
-
- if (fd_utils::ReadAndHashExtents(
- source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
- ValidateSourceHash(source_hash, operation, source_ecc_fd_, error)) {
- // At this point reading from the error corrected device worked, but
- // reading from the raw device failed, so this is considered a recovered
- // failure.
- source_ecc_recovered_failures_++;
- return source_ecc_fd_;
- }
- return nullptr;
-}
-
-bool PartitionWriter::OpenCurrentECCPartition() {
- // No support for ECC for full payloads.
- // Full payload should not have any opeartion that requires ECC partitions.
- if (source_ecc_fd_)
- return true;
-
- if (source_ecc_open_failure_)
- return false;
-
-#if USE_FEC
- const PartitionUpdate& partition = partition_update_;
- const InstallPlan::Partition& install_part = install_part_;
- std::string path = install_part.source_path;
- FileDescriptorPtr fd(new FecFileDescriptor());
- if (!fd->Open(path.c_str(), O_RDONLY, 0)) {
- PLOG(ERROR) << "Unable to open ECC source partition "
- << partition.partition_name() << ", file " << path;
- source_ecc_open_failure_ = true;
- return false;
- }
- source_ecc_fd_ = fd;
-#else
- // No support for ECC compiled.
- source_ecc_open_failure_ = true;
-#endif // USE_FEC
-
- return !source_ecc_open_failure_;
+ return verified_source_fd_.ChooseSourceFD(operation, error);
}
int PartitionWriter::Close() {
int err = 0;
- if (source_fd_ && !source_fd_->Close()) {
- err = errno;
- PLOG(ERROR) << "Error closing source partition";
- if (!err)
- err = 1;
- }
- source_fd_.reset();
+
source_path_.clear();
if (target_fd_ && !target_fd_->Close()) {
@@ -639,14 +305,6 @@
target_fd_.reset();
target_path_.clear();
- if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
- err = errno;
- PLOG(ERROR) << "Error closing ECC source partition";
- if (!err)
- err = 1;
- }
- source_ecc_fd_.reset();
- source_ecc_open_failure_ = false;
return -err;
}
@@ -658,4 +316,44 @@
return std::make_unique<DirectExtentWriter>(target_fd_);
}
+bool PartitionWriter::ValidateSourceHash(const brillo::Blob& calculated_hash,
+ const InstallOperation& operation,
+ const FileDescriptorPtr source_fd,
+ ErrorCode* error) {
+ using std::string;
+ using std::vector;
+ brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
+ operation.src_sha256_hash().end());
+ if (calculated_hash != expected_source_hash) {
+ LOG(ERROR) << "The hash of the source data on disk for this operation "
+ << "doesn't match the expected value. This could mean that the "
+ << "delta update payload was targeted for another version, or "
+ << "that the source partition was modified after it was "
+ << "installed, for example, by mounting a filesystem.";
+ LOG(ERROR) << "Expected: sha256|hex = "
+ << base::HexEncode(expected_source_hash.data(),
+ expected_source_hash.size());
+ LOG(ERROR) << "Calculated: sha256|hex = "
+ << base::HexEncode(calculated_hash.data(),
+ calculated_hash.size());
+
+ vector<string> source_extents;
+ for (const Extent& ext : operation.src_extents()) {
+ source_extents.push_back(
+ base::StringPrintf("%" PRIu64 ":%" PRIu64,
+ static_cast<uint64_t>(ext.start_block()),
+ static_cast<uint64_t>(ext.num_blocks())));
+ }
+ LOG(ERROR) << "Operation source (offset:size) in blocks: "
+ << base::JoinString(source_extents, ",");
+
+ // Log remount history if this device is an ext4 partition.
+ LogMountHistory(source_fd);
+
+ *error = ErrorCode::kDownloadStateInitializationError;
+ return false;
+ }
+ return true;
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_consumer/partition_writer.h b/payload_consumer/partition_writer.h
index 82e557a..e11d987 100644
--- a/payload_consumer/partition_writer.h
+++ b/payload_consumer/partition_writer.h
@@ -27,18 +27,21 @@
#include "update_engine/common/dynamic_partition_control_interface.h"
#include "update_engine/payload_consumer/extent_writer.h"
#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/install_operation_executor.h"
#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/payload_consumer/partition_writer_interface.h"
+#include "update_engine/payload_consumer/verified_source_fd.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
-class PartitionWriter {
+class PartitionWriter : public PartitionWriterInterface {
public:
PartitionWriter(const PartitionUpdate& partition_update,
const InstallPlan::Partition& install_part,
DynamicPartitionControlInterface* dynamic_control,
size_t block_size,
bool is_interactive);
- virtual ~PartitionWriter();
+ ~PartitionWriter();
static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
const InstallOperation& operation,
const FileDescriptorPtr source_fd,
@@ -46,95 +49,81 @@
// Perform necessary initialization work before InstallOperation can be
// applied to this partition
- [[nodiscard]] virtual bool Init(const InstallPlan* install_plan,
- bool source_may_exist,
- size_t next_op_index);
+ [[nodiscard]] bool Init(const InstallPlan* install_plan,
+ bool source_may_exist,
+ size_t next_op_index) override;
// |CheckpointUpdateProgress| will be called after SetNextOpIndex(), but it's
// optional. DeltaPerformer may or may not call this everytime an operation is
// applied.
// |next_op_index| is index of next operation that should be applied.
// |next_op_index-1| is the last operation that is already applied.
- virtual void CheckpointUpdateProgress(size_t next_op_index);
+ void CheckpointUpdateProgress(size_t next_op_index) override;
// Close partition writer, when calling this function there's no guarantee
// that all |InstallOperations| are sent to |PartitionWriter|. This function
// will be called even if we are pausing/aborting the update.
- int Close();
+ int Close() override;
// These perform a specific type of operation and return true on success.
// |error| will be set if source hash mismatch, otherwise |error| might not be
// set even if it fails.
- [[nodiscard]] virtual bool PerformReplaceOperation(
- const InstallOperation& operation, const void* data, size_t count);
- [[nodiscard]] virtual bool PerformZeroOrDiscardOperation(
- const InstallOperation& operation);
+ [[nodiscard]] bool PerformReplaceOperation(const InstallOperation& operation,
+ const void* data,
+ size_t count) override;
+ [[nodiscard]] bool PerformZeroOrDiscardOperation(
+ const InstallOperation& operation) override;
- [[nodiscard]] virtual bool PerformSourceCopyOperation(
- const InstallOperation& operation, ErrorCode* error);
- [[nodiscard]] virtual bool PerformSourceBsdiffOperation(
+ [[nodiscard]] bool PerformSourceCopyOperation(
+ const InstallOperation& operation, ErrorCode* error) override;
+ [[nodiscard]] bool PerformSourceBsdiffOperation(
const InstallOperation& operation,
ErrorCode* error,
const void* data,
- size_t count);
- [[nodiscard]] virtual bool PerformPuffDiffOperation(
- const InstallOperation& operation,
- ErrorCode* error,
- const void* data,
- size_t count);
+ size_t count) override;
+ [[nodiscard]] bool PerformPuffDiffOperation(const InstallOperation& operation,
+ ErrorCode* error,
+ const void* data,
+ size_t count) override;
// |DeltaPerformer| calls this when all Install Ops are sent to partition
// writer. No |Perform*Operation| methods will be called in the future, and
// the partition writer is expected to be closed soon.
- [[nodiscard]] virtual bool FinishedInstallOps() { return true; }
+ [[nodiscard]] bool FinishedInstallOps() override { return true; }
- protected:
+ private:
friend class PartitionWriterTest;
FRIEND_TEST(PartitionWriterTest, ChooseSourceFDTest);
- bool OpenSourcePartition(uint32_t source_slot, bool source_may_exist);
-
- bool OpenCurrentECCPartition();
- // For a given operation, choose the source fd to be used (raw device or error
- // correction device) based on the source operation hash.
- // Returns nullptr if the source hash mismatch cannot be corrected, and set
- // the |error| accordingly.
- FileDescriptorPtr ChooseSourceFD(const InstallOperation& operation,
+ [[nodiscard]] bool OpenSourcePartition(uint32_t source_slot,
+ bool source_may_exist);
+ FileDescriptorPtr ChooseSourceFD(const InstallOperation& op,
ErrorCode* error);
- [[nodiscard]] virtual std::unique_ptr<ExtentWriter> CreateBaseExtentWriter();
+
+ [[nodiscard]] std::unique_ptr<ExtentWriter> CreateBaseExtentWriter();
const PartitionUpdate& partition_update_;
const InstallPlan::Partition& install_part_;
DynamicPartitionControlInterface* dynamic_control_;
// Path to source partition
std::string source_path_;
+ VerifiedSourceFd verified_source_fd_;
// Path to target partition
std::string target_path_;
- FileDescriptorPtr source_fd_;
FileDescriptorPtr target_fd_;
const bool interactive_;
const size_t block_size_;
- // File descriptor of the error corrected source partition. Only set while
- // updating partition using a delta payload for a partition where error
- // correction is available. The size of the error corrected device is smaller
- // than the underlying raw device, since it doesn't include the error
- // correction blocks.
- FileDescriptorPtr source_ecc_fd_{nullptr};
- // The total number of operations that failed source hash verification but
- // passed after falling back to the error-corrected |source_ecc_fd_| device.
- uint64_t source_ecc_recovered_failures_{0};
-
- // Whether opening the current partition as an error-corrected device failed.
- // Used to avoid re-opening the same source partition if it is not actually
- // error corrected.
- bool source_ecc_open_failure_{false};
+ // This instance handles decompression/bsdfif/puffdiff. It's responsible for
+ // constructing data which should be written to target partition, actual
+ // "writing" is handled by |PartitionWriter|
+ InstallOperationExecutor install_op_executor_;
};
namespace partition_writer {
// Return a PartitionWriter instance for perform InstallOps on this partition.
// Uses VABCPartitionWriter for Virtual AB Compression
-std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+std::unique_ptr<PartitionWriterInterface> CreatePartitionWriter(
const PartitionUpdate& partition_update,
const InstallPlan::Partition& install_part,
DynamicPartitionControlInterface* dynamic_control,
diff --git a/payload_consumer/partition_writer_factory_android.cc b/payload_consumer/partition_writer_factory_android.cc
index 184e2d5..0a9a3fb 100644
--- a/payload_consumer/partition_writer_factory_android.cc
+++ b/payload_consumer/partition_writer_factory_android.cc
@@ -23,7 +23,7 @@
namespace chromeos_update_engine::partition_writer {
-std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+std::unique_ptr<PartitionWriterInterface> CreatePartitionWriter(
const PartitionUpdate& partition_update,
const InstallPlan::Partition& install_part,
DynamicPartitionControlInterface* dynamic_control,
diff --git a/payload_consumer/partition_writer_factory_chromeos.cc b/payload_consumer/partition_writer_factory_chromeos.cc
index 609f043..0f7a11c 100644
--- a/payload_consumer/partition_writer_factory_chromeos.cc
+++ b/payload_consumer/partition_writer_factory_chromeos.cc
@@ -22,7 +22,7 @@
#include "update_engine/payload_consumer/partition_writer.h"
namespace chromeos_update_engine::partition_writer {
-std::unique_ptr<PartitionWriter> CreatePartitionWriter(
+std::unique_ptr<PartitionWriterInterface> CreatePartitionWriter(
const PartitionUpdate& partition_update,
const InstallPlan::Partition& install_part,
DynamicPartitionControlInterface* dynamic_control,
diff --git a/payload_consumer/partition_writer_interface.h b/payload_consumer/partition_writer_interface.h
new file mode 100644
index 0000000..f8d6b9c
--- /dev/null
+++ b/payload_consumer/partition_writer_interface.h
@@ -0,0 +1,83 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_PARTITION_WRITER_INTERFACE_H_
+#define UPDATE_ENGINE_PARTITION_WRITER_INTERFACE_H_
+
+#include <cstdint>
+#include <string>
+
+#include <brillo/secure_blob.h>
+#include <gtest/gtest_prod.h>
+
+#include "update_engine/common/dynamic_partition_control_interface.h"
+#include "update_engine/payload_consumer/extent_writer.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+class PartitionWriterInterface {
+ public:
+ virtual ~PartitionWriterInterface() = default;
+
+ // Perform necessary initialization work before InstallOperation can be
+ // applied to this partition
+ [[nodiscard]] virtual bool Init(const InstallPlan* install_plan,
+ bool source_may_exist,
+ size_t next_op_index) = 0;
+
+ // |CheckpointUpdateProgress| will be called after SetNextOpIndex(), but it's
+ // optional. DeltaPerformer may or may not call this everytime an operation is
+ // applied.
+ // |next_op_index| is index of next operation that should be applied.
+ // |next_op_index-1| is the last operation that is already applied.
+ virtual void CheckpointUpdateProgress(size_t next_op_index) = 0;
+
+ // Close partition writer, when calling this function there's no guarantee
+ // that all |InstallOperations| are sent to |PartitionWriter|. This function
+ // will be called even if we are pausing/aborting the update.
+ virtual int Close() = 0;
+
+ // These perform a specific type of operation and return true on success.
+ // |error| will be set if source hash mismatch, otherwise |error| might not be
+ // set even if it fails.
+ [[nodiscard]] virtual bool PerformReplaceOperation(
+ const InstallOperation& operation, const void* data, size_t count) = 0;
+ [[nodiscard]] virtual bool PerformZeroOrDiscardOperation(
+ const InstallOperation& operation) = 0;
+
+ [[nodiscard]] virtual bool PerformSourceCopyOperation(
+ const InstallOperation& operation, ErrorCode* error) = 0;
+ [[nodiscard]] virtual bool PerformSourceBsdiffOperation(
+ const InstallOperation& operation,
+ ErrorCode* error,
+ const void* data,
+ size_t count) = 0;
+ [[nodiscard]] virtual bool PerformPuffDiffOperation(
+ const InstallOperation& operation,
+ ErrorCode* error,
+ const void* data,
+ size_t count) = 0;
+
+ // |DeltaPerformer| calls this when all Install Ops are sent to partition
+ // writer. No |Perform*Operation| methods will be called in the future, and
+ // the partition writer is expected to be closed soon.
+ [[nodiscard]] virtual bool FinishedInstallOps() = 0;
+};
+} // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/partition_writer_unittest.cc b/payload_consumer/partition_writer_unittest.cc
index 263f338..331a061 100644
--- a/payload_consumer/partition_writer_unittest.cc
+++ b/payload_consumer/partition_writer_unittest.cc
@@ -46,18 +46,19 @@
// Helper function to pretend that the ECC file descriptor was already opened.
// Returns a pointer to the created file descriptor.
FakeFileDescriptor* SetFakeECCFile(size_t size) {
- EXPECT_FALSE(writer_.source_ecc_fd_) << "source_ecc_fd_ already open.";
+ EXPECT_FALSE(writer_.verified_source_fd_.source_ecc_fd_)
+ << "source_ecc_fdb already open.";
FakeFileDescriptor* ret = new FakeFileDescriptor();
fake_ecc_fd_.reset(ret);
// Call open to simulate it was already opened.
ret->Open("", 0);
ret->SetFileSize(size);
- writer_.source_ecc_fd_ = fake_ecc_fd_;
+ writer_.verified_source_fd_.source_ecc_fd_ = fake_ecc_fd_;
return ret;
}
uint64_t GetSourceEccRecoveredFailures() const {
- return writer_.source_ecc_recovered_failures_;
+ return writer_.verified_source_fd_.source_ecc_recovered_failures_;
}
AnnotatedOperation GenerateSourceCopyOp(const brillo::Blob& copied_data,
@@ -81,22 +82,31 @@
brillo::Blob PerformSourceCopyOp(const InstallOperation& op,
const brillo::Blob blob_data) {
- ScopedTempFile source_partition("Blob-XXXXXX");
+ LOG(INFO) << "Using source part " << source_partition.path();
FileDescriptorPtr fd(new EintrSafeFileDescriptor());
DirectExtentWriter extent_writer{fd};
EXPECT_TRUE(fd->Open(source_partition.path().c_str(), O_RDWR));
+ if (HasFailure()) {
+ return {};
+ }
EXPECT_TRUE(extent_writer.Init(op.src_extents(), kBlockSize));
+ if (HasFailure()) {
+ return {};
+ }
EXPECT_TRUE(extent_writer.Write(blob_data.data(), blob_data.size()));
+ if (HasFailure()) {
+ return {};
+ }
+ fd->Flush();
- ScopedTempFile target_partition("Blob-XXXXXX");
-
- install_part_.source_path = source_partition.path();
- install_part_.target_path = target_partition.path();
install_part_.source_size = blob_data.size();
install_part_.target_size = blob_data.size();
ErrorCode error;
EXPECT_TRUE(writer_.Init(&install_plan_, true, 0));
+ if (HasFailure()) {
+ return {};
+ }
EXPECT_TRUE(writer_.PerformSourceCopyOperation(op, &error));
writer_.CheckpointUpdateProgress(1);
@@ -111,8 +121,11 @@
DynamicPartitionControlStub dynamic_control_{};
FileDescriptorPtr fake_ecc_fd_{};
DeltaArchiveManifest manifest_{};
+ ScopedTempFile source_partition{"source-part-XXXXXX"};
+ ScopedTempFile target_partition{"target-part-XXXXXX"};
+ InstallPlan::Partition install_part_{.source_path = source_partition.path(),
+ .target_path = target_partition.path()};
PartitionUpdate partition_update_{};
- InstallPlan::Partition install_part_{};
PartitionWriter writer_{
partition_update_, install_part_, &dynamic_control_, kBlockSize, false};
};
@@ -124,7 +137,7 @@
ScopedTempFile source("Source-XXXXXX");
// Setup the source path with the right expected data.
brillo::Blob expected_data = FakeFileDescriptorData(kCopyOperationSize);
- EXPECT_TRUE(test_utils::WriteFileVector(source.path(), expected_data));
+ ASSERT_TRUE(test_utils::WriteFileVector(source.path(), expected_data));
// Setup the fec file descriptor as the fake stream, with smaller data than
// the expected.
@@ -136,17 +149,18 @@
// The payload operation doesn't include an operation hash.
auto source_copy_op = GenerateSourceCopyOp(expected_data, false, &old_part);
-
+ ASSERT_NO_FATAL_FAILURE();
auto output_data = PerformSourceCopyOp(source_copy_op.op, expected_data);
+ ASSERT_NO_FATAL_FAILURE();
ASSERT_EQ(output_data, expected_data);
// Verify that the fake_fec was attempted to be used. Since the file
// descriptor is shorter it can actually do more than one read to realize it
// reached the EOF.
- EXPECT_LE(1U, fake_fec->GetReadOps().size());
+ ASSERT_LE(1U, fake_fec->GetReadOps().size());
// This fallback doesn't count as an error-corrected operation since the
// operation hash was not available.
- EXPECT_EQ(0U, GetSourceEccRecoveredFailures());
+ ASSERT_EQ(0U, GetSourceEccRecoveredFailures());
}
// Test that the error-corrected file descriptor is used to read the partition
@@ -163,11 +177,13 @@
brillo::Blob expected_data = FakeFileDescriptorData(kCopyOperationSize);
auto source_copy_op = GenerateSourceCopyOp(expected_data, true);
+ ASSERT_NO_FATAL_FAILURE();
auto output_data = PerformSourceCopyOp(source_copy_op.op, invalid_data);
+ ASSERT_NO_FATAL_FAILURE();
ASSERT_EQ(output_data, expected_data);
// Verify that the fake_fec was actually used.
- EXPECT_EQ(1U, fake_fec->GetReadOps().size());
+ EXPECT_GE(fake_fec->GetReadOps().size(), 1U);
EXPECT_EQ(1U, GetSourceEccRecoveredFailures());
}
@@ -177,10 +193,11 @@
// Write invalid data to the source image, which doesn't match the expected
// hash.
brillo::Blob invalid_data(kSourceSize, 0x55);
- EXPECT_TRUE(test_utils::WriteFileVector(source.path(), invalid_data));
+ ASSERT_TRUE(test_utils::WriteFileVector(source.path(), invalid_data));
- writer_.source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
- writer_.source_fd_->Open(source.path().c_str(), O_RDONLY);
+ writer_.verified_source_fd_.source_fd_ =
+ std::make_shared<EintrSafeFileDescriptor>();
+ writer_.verified_source_fd_.source_fd_->Open(source.path().c_str(), O_RDONLY);
// Setup the fec file descriptor as the fake stream, which matches
// |expected_data|.
@@ -190,15 +207,16 @@
InstallOperation op;
*(op.add_src_extents()) = ExtentForRange(0, kSourceSize / 4096);
brillo::Blob src_hash;
- EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
+ ASSERT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
op.set_src_sha256_hash(src_hash.data(), src_hash.size());
ErrorCode error = ErrorCode::kSuccess;
- EXPECT_EQ(writer_.source_ecc_fd_, writer_.ChooseSourceFD(op, &error));
- EXPECT_EQ(ErrorCode::kSuccess, error);
+ ASSERT_EQ(writer_.verified_source_fd_.source_ecc_fd_,
+ writer_.ChooseSourceFD(op, &error));
+ ASSERT_EQ(ErrorCode::kSuccess, error);
// Verify that the fake_fec was actually used.
- EXPECT_EQ(1U, fake_fec->GetReadOps().size());
- EXPECT_EQ(1U, GetSourceEccRecoveredFailures());
+ ASSERT_EQ(1U, fake_fec->GetReadOps().size());
+ ASSERT_EQ(1U, GetSourceEccRecoveredFailures());
}
} // namespace chromeos_update_engine
diff --git a/payload_consumer/snapshot_extent_writer_unittest.cc b/payload_consumer/snapshot_extent_writer_unittest.cc
index 2201043..51672ca 100644
--- a/payload_consumer/snapshot_extent_writer_unittest.cc
+++ b/payload_consumer/snapshot_extent_writer_unittest.cc
@@ -70,6 +70,10 @@
return true;
}
+ bool EmitSequenceData(size_t num_ops, const uint32_t* data) override {
+ return false;
+ }
+
// Return number of bytes the cow image occupies on disk.
uint64_t GetCowSize() override {
return std::accumulate(
diff --git a/payload_consumer/vabc_partition_writer.cc b/payload_consumer/vabc_partition_writer.cc
index 0843fff..d181c75 100644
--- a/payload_consumer/vabc_partition_writer.cc
+++ b/payload_consumer/vabc_partition_writer.cc
@@ -18,6 +18,7 @@
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include <libsnapshot/cow_writer.h>
@@ -55,12 +56,27 @@
// label 3, Which contains all operation 2's data, but none of operation 3's
// data.
+VABCPartitionWriter::VABCPartitionWriter(
+ const PartitionUpdate& partition_update,
+ const InstallPlan::Partition& install_part,
+ DynamicPartitionControlInterface* dynamic_control,
+ size_t block_size,
+ bool is_interactive)
+ : partition_update_(partition_update),
+ install_part_(install_part),
+ dynamic_control_(dynamic_control),
+ interactive_(is_interactive),
+ block_size_(block_size),
+ executor_(block_size),
+ verified_source_fd_(block_size, install_part.source_path) {}
+
bool VABCPartitionWriter::Init(const InstallPlan* install_plan,
bool source_may_exist,
size_t next_op_index) {
TEST_AND_RETURN_FALSE(install_plan != nullptr);
- TEST_AND_RETURN_FALSE(
- OpenSourcePartition(install_plan->source_slot, source_may_exist));
+ if (source_may_exist) {
+ TEST_AND_RETURN_FALSE(verified_source_fd_.Open());
+ }
std::optional<std::string> source_path;
if (!install_part_.source_path.empty()) {
// TODO(zhangkelvin) Make |source_path| a std::optional<std::string>
@@ -90,7 +106,15 @@
auto converted = ConvertToCowOperations(partition_update_.operations(),
partition_update_.merge_operations());
- WriteAllCowOps(block_size_, converted, cow_writer_.get(), source_fd_);
+ if (!converted.empty()) {
+ // Use source fd directly. Ideally we want to verify all extents used in
+ // source copy, but then what do we do if some extents contain correct
+ // hashes and some don't?
+ auto source_fd = std::make_shared<EintrSafeFileDescriptor>();
+ TEST_AND_RETURN_FALSE_ERRNO(
+ source_fd->Open(install_part_.source_path.c_str(), O_RDONLY));
+ WriteAllCowOps(block_size_, converted, cow_writer_.get(), source_fd);
+ }
return true;
}
@@ -150,6 +174,43 @@
return true;
}
+bool VABCPartitionWriter::PerformReplaceOperation(const InstallOperation& op,
+ const void* data,
+ size_t count) {
+ // Setup the ExtentWriter stack based on the operation type.
+ std::unique_ptr<ExtentWriter> writer = CreateBaseExtentWriter();
+
+ return executor_.ExecuteReplaceOperation(op, std::move(writer), data, count);
+}
+
+bool VABCPartitionWriter::PerformSourceBsdiffOperation(
+ const InstallOperation& operation,
+ ErrorCode* error,
+ const void* data,
+ size_t count) {
+ FileDescriptorPtr source_fd =
+ verified_source_fd_.ChooseSourceFD(operation, error);
+ TEST_AND_RETURN_FALSE(source_fd != nullptr);
+
+ auto writer = CreateBaseExtentWriter();
+ return executor_.ExecuteSourceBsdiffOperation(
+ operation, std::move(writer), source_fd, data, count);
+}
+
+bool VABCPartitionWriter::PerformPuffDiffOperation(
+ const InstallOperation& operation,
+ ErrorCode* error,
+ const void* data,
+ size_t count) {
+ FileDescriptorPtr source_fd =
+ verified_source_fd_.ChooseSourceFD(operation, error);
+ TEST_AND_RETURN_FALSE(source_fd != nullptr);
+
+ auto writer = CreateBaseExtentWriter();
+ return executor_.ExecutePuffDiffOperation(
+ operation, std::move(writer), source_fd, data, count);
+}
+
void VABCPartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
// No need to call fsync/sync, as CowWriter flushes after a label is added
// added.
@@ -167,9 +228,15 @@
}
VABCPartitionWriter::~VABCPartitionWriter() {
+ Close();
+}
+
+int VABCPartitionWriter::Close() {
if (cow_writer_) {
cow_writer_->Finalize();
+ cow_writer_ = nullptr;
}
+ return 0;
}
} // namespace chromeos_update_engine
diff --git a/payload_consumer/vabc_partition_writer.h b/payload_consumer/vabc_partition_writer.h
index 7fb2a2c..e8601a9 100644
--- a/payload_consumer/vabc_partition_writer.h
+++ b/payload_consumer/vabc_partition_writer.h
@@ -18,25 +18,29 @@
#define UPDATE_ENGINE_VABC_PARTITION_WRITER_H_
#include <memory>
+#include <string>
#include <vector>
#include <libsnapshot/snapshot_writer.h>
#include "update_engine/common/cow_operation_convert.h"
+#include "update_engine/payload_consumer/install_operation_executor.h"
#include "update_engine/payload_consumer/install_plan.h"
#include "update_engine/payload_consumer/partition_writer.h"
namespace chromeos_update_engine {
-class VABCPartitionWriter final : public PartitionWriter {
+class VABCPartitionWriter final : public PartitionWriterInterface {
public:
- using PartitionWriter::PartitionWriter;
+ VABCPartitionWriter(const PartitionUpdate& partition_update,
+ const InstallPlan::Partition& install_part,
+ DynamicPartitionControlInterface* dynamic_control,
+ size_t block_size,
+ bool is_interactive);
[[nodiscard]] bool Init(const InstallPlan* install_plan,
bool source_may_exist,
size_t next_op_index) override;
~VABCPartitionWriter() override;
- [[nodiscard]] std::unique_ptr<ExtentWriter> CreateBaseExtentWriter() override;
-
// Only ZERO and SOURCE_COPY InstallOperations are treated special by VABC
// Partition Writer. These operations correspond to COW_ZERO and COW_COPY. All
// other operations just get converted to COW_REPLACE.
@@ -45,6 +49,20 @@
[[nodiscard]] bool PerformSourceCopyOperation(
const InstallOperation& operation, ErrorCode* error) override;
+ [[nodiscard]] bool PerformReplaceOperation(const InstallOperation& operation,
+ const void* data,
+ size_t count) override;
+
+ [[nodiscard]] bool PerformSourceBsdiffOperation(
+ const InstallOperation& operation,
+ ErrorCode* error,
+ const void* data,
+ size_t count) override;
+ [[nodiscard]] bool PerformPuffDiffOperation(const InstallOperation& operation,
+ ErrorCode* error,
+ const void* data,
+ size_t count) override;
+
void CheckpointUpdateProgress(size_t next_op_index) override;
static bool WriteAllCowOps(size_t block_size,
@@ -53,9 +71,24 @@
FileDescriptorPtr source_fd);
[[nodiscard]] bool FinishedInstallOps() override;
+ int Close() override;
private:
std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer_;
+
+ bool OpenCurrentECCPartition();
+ [[nodiscard]] std::unique_ptr<ExtentWriter> CreateBaseExtentWriter();
+
+ const PartitionUpdate& partition_update_;
+ const InstallPlan::Partition& install_part_;
+ DynamicPartitionControlInterface* dynamic_control_;
+ // Path to source partition
+ std::string source_path_;
+
+ const bool interactive_;
+ const size_t block_size_;
+ InstallOperationExecutor executor_;
+ VerifiedSourceFd verified_source_fd_;
};
} // namespace chromeos_update_engine
diff --git a/payload_consumer/verified_source_fd.cc b/payload_consumer/verified_source_fd.cc
new file mode 100644
index 0000000..002bd07
--- /dev/null
+++ b/payload_consumer/verified_source_fd.cc
@@ -0,0 +1,124 @@
+//
+// 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
+// limi
+
+#include "update_engine/payload_consumer/verified_source_fd.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/fec_file_descriptor.h"
+#include "update_engine/payload_consumer/file_descriptor_utils.h"
+#include "update_engine/payload_consumer/mount_history.h"
+#include "update_engine/payload_consumer/partition_writer.h"
+
+namespace chromeos_update_engine {
+using std::string;
+
+bool VerifiedSourceFd::OpenCurrentECCPartition() {
+ // No support for ECC for full payloads.
+ // Full payload should not have any opeartion that requires ECC partitions.
+ if (source_ecc_fd_)
+ return true;
+
+ if (source_ecc_open_failure_)
+ return false;
+
+#if USE_FEC
+ FileDescriptorPtr fd(new FecFileDescriptor());
+ if (!fd->Open(source_path_.c_str(), O_RDONLY, 0)) {
+ PLOG(ERROR) << "Unable to open ECC source partition " << source_path_;
+ source_ecc_open_failure_ = true;
+ return false;
+ }
+ source_ecc_fd_ = fd;
+#else
+ // No support for ECC compiled.
+ source_ecc_open_failure_ = true;
+#endif // USE_FEC
+
+ return !source_ecc_open_failure_;
+}
+
+FileDescriptorPtr VerifiedSourceFd::ChooseSourceFD(
+ const InstallOperation& operation, ErrorCode* error) {
+ if (source_fd_ == nullptr) {
+ LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
+ return nullptr;
+ }
+ if (!operation.has_src_sha256_hash()) {
+ // When the operation doesn't include a source hash, we attempt the error
+ // corrected device first since we can't verify the block in the raw device
+ // at this point, but we first need to make sure all extents are readable
+ // since the error corrected device can be shorter or not available.
+ if (OpenCurrentECCPartition() &&
+ fd_utils::ReadAndHashExtents(
+ source_ecc_fd_, operation.src_extents(), block_size_, nullptr)) {
+ return source_ecc_fd_;
+ }
+ return source_fd_;
+ }
+
+ brillo::Blob source_hash;
+ brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
+ operation.src_sha256_hash().end());
+ if (fd_utils::ReadAndHashExtents(
+ source_fd_, operation.src_extents(), block_size_, &source_hash) &&
+ source_hash == expected_source_hash) {
+ return source_fd_;
+ }
+ // We fall back to use the error corrected device if the hash of the raw
+ // device doesn't match or there was an error reading the source partition.
+ if (!OpenCurrentECCPartition()) {
+ // The following function call will return false since the source hash
+ // mismatches, but we still want to call it so it prints the appropriate
+ // log message.
+ PartitionWriter::ValidateSourceHash(
+ source_hash, operation, source_fd_, error);
+ return nullptr;
+ }
+ LOG(WARNING) << "Source hash from RAW device mismatched: found "
+ << base::HexEncode(source_hash.data(), source_hash.size())
+ << ", expected "
+ << base::HexEncode(expected_source_hash.data(),
+ expected_source_hash.size());
+
+ if (fd_utils::ReadAndHashExtents(
+ source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
+ PartitionWriter::ValidateSourceHash(
+ source_hash, operation, source_ecc_fd_, error)) {
+ source_ecc_recovered_failures_++;
+ return source_ecc_fd_;
+ }
+ return nullptr;
+}
+
+bool VerifiedSourceFd::Open() {
+ source_fd_ = std::make_shared<EintrSafeFileDescriptor>();
+ if (source_fd_ == nullptr)
+ return false;
+ TEST_AND_RETURN_FALSE_ERRNO(source_fd_->Open(source_path_.c_str(), O_RDONLY));
+ return true;
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/verified_source_fd.h b/payload_consumer/verified_source_fd.h
new file mode 100644
index 0000000..f7d0620
--- /dev/null
+++ b/payload_consumer/verified_source_fd.h
@@ -0,0 +1,61 @@
+//
+// 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
+// limi
+
+#ifndef UPDATE_ENGINE_VERIFIED_SOURCE_FD_H__
+#define UPDATE_ENGINE_VERIFIED_SOURCE_FD_H__
+
+#include <cstddef>
+
+#include <string>
+#include <utility>
+
+#include <gtest/gtest_prod.h>
+#include <update_engine/update_metadata.pb.h>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+
+class VerifiedSourceFd {
+ public:
+ explicit VerifiedSourceFd(size_t block_size, std::string source_path)
+ : block_size_(block_size), source_path_(std::move(source_path)) {}
+ FileDescriptorPtr ChooseSourceFD(const InstallOperation& operation,
+ ErrorCode* error);
+
+ [[nodiscard]] bool Open();
+
+ private:
+ bool OpenCurrentECCPartition();
+ const size_t block_size_;
+ const std::string source_path_;
+ FileDescriptorPtr source_ecc_fd_;
+ FileDescriptorPtr source_fd_;
+
+ friend class PartitionWriterTest;
+ FRIEND_TEST(PartitionWriterTest, ChooseSourceFDTest);
+ // The total number of operations that failed source hash verification but
+ // passed after falling back to the error-corrected |source_ecc_fd_| device.
+ uint64_t source_ecc_recovered_failures_{0};
+
+ // Whether opening the current partition as an error-corrected device failed.
+ // Used to avoid re-opening the same source partition if it is not actually
+ // error corrected.
+ bool source_ecc_open_failure_{false};
+};
+} // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_generator/ab_generator.cc b/payload_generator/ab_generator.cc
index d9b9d88..25cafe3 100644
--- a/payload_generator/ab_generator.cc
+++ b/payload_generator/ab_generator.cc
@@ -54,7 +54,7 @@
new_part,
hard_chunk_blocks,
soft_chunk_blocks,
- config.version,
+ config,
blob_file));
LOG(INFO) << "done reading " << new_part.name;
diff --git a/payload_generator/annotated_operation.h b/payload_generator/annotated_operation.h
index 653bab0..c57f249 100644
--- a/payload_generator/annotated_operation.h
+++ b/payload_generator/annotated_operation.h
@@ -19,6 +19,7 @@
#include <ostream> // NOLINT(readability/streams)
#include <string>
+#include <vector>
#include <brillo/secure_blob.h>
@@ -35,6 +36,11 @@
// The InstallOperation, as defined by the protobuf.
InstallOperation op;
+ // Array of blocks which should be converted to XOR during OTA install.
+ // All elements in this array should have |merge_op.type() == COW_XOR|.
+ // This information is typically derived from BSDIFF patch data.
+ std::vector<CowMergeOperation> xor_ops;
+
// Writes |blob| to the end of |blob_file|. It sets the data_offset and
// data_length in AnnotatedOperation to match the offset and size of |blob|
// in |blob_file|.
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index 3c025e1..077fbfe 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -35,15 +35,17 @@
#include <memory>
#include <numeric>
#include <utility>
+#include <vector>
#include <base/files/file_util.h>
#include <base/format_macros.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/threading/simple_thread.h>
-#include <base/time/time.h>
#include <brillo/data_encoding.h>
#include <bsdiff/bsdiff.h>
+#include <bsdiff/control_entry.h>
+#include <bsdiff/patch_reader.h>
#include <bsdiff/patch_writer_factory.h>
#include <puffin/utils.h>
@@ -131,6 +133,44 @@
}
return distances.back();
}
+
+static bool ShouldCreateNewOp(const std::vector<CowMergeOperation>& ops,
+ size_t src_block,
+ size_t dst_block,
+ size_t src_offset) {
+ if (ops.empty()) {
+ return true;
+ }
+ const auto& op = ops.back();
+ if (op.src_offset() != src_offset) {
+ return true;
+ }
+ const auto& src_extent = op.src_extent();
+ const auto& dst_extent = op.dst_extent();
+ return src_extent.start_block() + src_extent.num_blocks() != src_block ||
+ dst_extent.start_block() + dst_extent.num_blocks() != dst_block;
+}
+
+static void AppendXorBlock(std::vector<CowMergeOperation>* ops,
+ size_t src_block,
+ size_t dst_block,
+ size_t src_offset) {
+ if (ShouldCreateNewOp(*ops, src_block, dst_block, src_offset)) {
+ auto& op = ops->emplace_back();
+ op.mutable_src_extent()->set_start_block(src_block);
+ op.mutable_src_extent()->set_num_blocks(1);
+ op.mutable_dst_extent()->set_start_block(dst_block);
+ op.mutable_dst_extent()->set_num_blocks(1);
+ op.set_src_offset(src_offset);
+ } else {
+ auto& op = ops->back();
+ auto& src_extent = *op.mutable_src_extent();
+ auto& dst_extent = *op.mutable_dst_extent();
+ src_extent.set_num_blocks(src_extent.num_blocks() + 1);
+ dst_extent.set_num_blocks(dst_extent.num_blocks() + 1);
+ }
+}
+
} // namespace
namespace diff_utils {
@@ -142,7 +182,7 @@
public:
FileDeltaProcessor(const string& old_part,
const string& new_part,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& config,
const vector<Extent>& old_extents,
const vector<Extent>& new_extents,
const vector<puffin::BitExtent>& old_deflates,
@@ -152,7 +192,7 @@
BlobFileWriter* blob_file)
: old_part_(old_part),
new_part_(new_part),
- version_(version),
+ config_(config),
old_extents_(old_extents),
new_extents_(new_extents),
new_extents_blocks_(utils::BlocksInExtents(new_extents)),
@@ -179,7 +219,7 @@
private:
const string& old_part_; // NOLINT(runtime/member_string_references)
const string& new_part_; // NOLINT(runtime/member_string_references)
- const PayloadVersion& version_;
+ const PayloadGenerationConfig& config_;
// The block ranges of the old/new file within the src/tgt image
const vector<Extent> old_extents_;
@@ -213,7 +253,7 @@
new_deflates_,
name_,
chunk_blocks_,
- version_,
+ config_,
blob_file_)) {
LOG(ERROR) << "Failed to generate delta for " << name_ << " ("
<< new_extents_blocks_ << " blocks)";
@@ -222,7 +262,7 @@
}
if (!ABGenerator::FragmentOperations(
- version_, &file_aops_, new_part_, blob_file_)) {
+ config_.version, &file_aops_, new_part_, blob_file_)) {
LOG(ERROR) << "Failed to fragment operations for " << name_;
failed_ = true;
return;
@@ -273,8 +313,9 @@
const PartitionConfig& new_part,
ssize_t hard_chunk_blocks,
size_t soft_chunk_blocks,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& config,
BlobFileWriter* blob_file) {
+ const auto& version = config.version;
ExtentRanges old_visited_blocks;
ExtentRanges new_visited_blocks;
@@ -297,7 +338,7 @@
old_part.size / kBlockSize,
new_part.size / kBlockSize,
soft_chunk_blocks,
- version,
+ config,
blob_file,
&old_visited_blocks,
&new_visited_blocks,
@@ -355,7 +396,7 @@
file_delta_processors.emplace_back(old_part.path,
new_part.path,
- version,
+ config,
std::move(old_file_extents),
std::move(new_file_extents),
old_file.deflates,
@@ -385,7 +426,7 @@
file_delta_processors.emplace_back(
old_part.path,
new_part.path,
- version,
+ config,
std::move(old_unvisited),
std::move(new_unvisited),
vector<puffin::BitExtent>{}, // old_deflates,
@@ -424,11 +465,12 @@
size_t old_num_blocks,
size_t new_num_blocks,
ssize_t chunk_blocks,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& config,
BlobFileWriter* blob_file,
ExtentRanges* old_visited_blocks,
ExtentRanges* new_visited_blocks,
ExtentRanges* old_zero_blocks) {
+ const auto& version = config.version;
vector<BlockMapping::BlockId> old_block_ids;
vector<BlockMapping::BlockId> new_block_ids;
TEST_AND_RETURN_FALSE(MapPartitionBlocks(old_part,
@@ -516,7 +558,7 @@
{}, // new_deflates
"<zeros>",
chunk_blocks,
- version,
+ config,
blob_file));
}
}
@@ -575,10 +617,9 @@
const vector<puffin::BitExtent>& new_deflates,
const string& name,
ssize_t chunk_blocks,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& config,
BlobFileWriter* blob_file) {
brillo::Blob data;
- InstallOperation operation;
uint64_t total_blocks = utils::BlocksInExtents(new_extents);
if (chunk_blocks == 0) {
@@ -602,30 +643,29 @@
NormalizeExtents(&old_extents_chunk);
NormalizeExtents(&new_extents_chunk);
+ // Now, insert into the list of operations.
+ AnnotatedOperation aop;
TEST_AND_RETURN_FALSE(ReadExtentsToDiff(old_part,
new_part,
old_extents_chunk,
new_extents_chunk,
old_deflates,
new_deflates,
- version,
+ config,
&data,
- &operation));
+ &aop));
// Check if the operation writes nothing.
- if (operation.dst_extents_size() == 0) {
+ if (aop.op.dst_extents_size() == 0) {
LOG(ERROR) << "Empty non-MOVE operation";
return false;
}
- // Now, insert into the list of operations.
- AnnotatedOperation aop;
aop.name = name;
if (static_cast<uint64_t>(chunk_blocks) < total_blocks) {
aop.name = base::StringPrintf(
"%s:%" PRIu64, name.c_str(), block_offset / chunk_blocks);
}
- aop.op = operation;
// Write the data
TEST_AND_RETURN_FALSE(aop.SetOperationBlob(data, blob_file));
@@ -688,16 +728,84 @@
return true;
}
+// Decide which blocks are similar from bsdiff patch.
+// Blocks included in out_op->xor_map will be converted to COW_XOR during OTA
+// installation
+bool PopulateXorOps(AnnotatedOperation* aop, const uint8_t* data, size_t size) {
+ bsdiff::BsdiffPatchReader patch_reader;
+ TEST_AND_RETURN_FALSE(patch_reader.Init(data, size));
+ ControlEntry entry;
+ size_t new_off = 0;
+ int64_t old_off = 0;
+ auto& xor_ops = aop->xor_ops;
+ size_t total_xor_blocks = 0;
+ const auto new_file_size =
+ utils::BlocksInExtents(aop->op.dst_extents()) * kBlockSize;
+ while (new_off < new_file_size) {
+ if (!patch_reader.ParseControlEntry(&entry)) {
+ LOG(ERROR)
+ << "Exhausted bsdiff patch data before reaching end of new file. "
+ "Current position: "
+ << new_off << " new file size: " << new_file_size;
+ return false;
+ }
+ if (old_off >= 0) {
+ auto dst_off_aligned = utils::RoundUp(new_off, kBlockSize);
+ const auto skip = dst_off_aligned - new_off;
+ auto src_off = old_off + skip;
+ const size_t chunk_size =
+ entry.diff_size - std::min(skip, entry.diff_size);
+ const auto xor_blocks = (chunk_size + kBlockSize / 2) / kBlockSize;
+ total_xor_blocks += xor_blocks;
+ // Append chunk_size/kBlockSize number of XOR blocks, subject to rounding
+ // rules: if decimal part of that division is >= 0.5, round up.
+ for (size_t i = 0; i < xor_blocks; i++) {
+ AppendXorBlock(
+ &xor_ops,
+ GetNthBlock(aop->op.src_extents(), src_off / kBlockSize),
+ GetNthBlock(aop->op.dst_extents(), dst_off_aligned / kBlockSize),
+ src_off % kBlockSize);
+ src_off += kBlockSize;
+ dst_off_aligned += kBlockSize;
+ }
+ }
+
+ old_off += entry.diff_size + entry.offset_increment;
+ new_off += entry.diff_size + entry.extra_size;
+ }
+
+ for (auto& op : xor_ops) {
+ CHECK_EQ(op.src_extent().num_blocks(), op.dst_extent().num_blocks());
+ // If |src_offset| is greater than 0, then we are reading 1
+ // extra block at the end of src_extent. This dependency must
+ // be honored during merge sequence generation, or we can end
+ // up with a corrupted device after merge.
+ if (op.src_offset() > 0) {
+ op.mutable_src_extent()->set_num_blocks(op.dst_extent().num_blocks() + 1);
+ }
+ }
+
+ if (xor_ops.size() > 0) {
+ // TODO(177104308) Filter out duplicate blocks in XOR op
+ LOG(INFO) << "Added " << total_xor_blocks << " XOR blocks, "
+ << total_xor_blocks * 100.0f / new_off * kBlockSize
+ << "% of blocks in this InstallOp are XOR";
+ }
+ return true;
+}
+
bool ReadExtentsToDiff(const string& old_part,
const string& new_part,
const vector<Extent>& old_extents,
const vector<Extent>& new_extents,
const vector<puffin::BitExtent>& old_deflates,
const vector<puffin::BitExtent>& new_deflates,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& config,
brillo::Blob* out_data,
- InstallOperation* out_op) {
- InstallOperation operation;
+ AnnotatedOperation* out_op) {
+ const auto& version = config.version;
+ AnnotatedOperation aop;
+ InstallOperation& operation = aop.op;
// We read blocks from old_extents and write blocks to new_extents.
uint64_t blocks_to_read = utils::BlocksInExtents(old_extents);
@@ -721,9 +829,10 @@
puffdiff_allowed = false;
}
- // Make copies of the extents so we can modify them.
- vector<Extent> src_extents = old_extents;
- vector<Extent> dst_extents = new_extents;
+ const vector<Extent>& src_extents = old_extents;
+ const vector<Extent>& dst_extents = new_extents;
+ // All operations have dst_extents.
+ StoreExtents(dst_extents, operation.mutable_dst_extents());
// Read in bytes from new data.
brillo::Blob new_data;
@@ -786,11 +895,15 @@
nullptr));
TEST_AND_RETURN_FALSE(utils::ReadFile(patch.value(), &bsdiff_delta));
+
CHECK_GT(bsdiff_delta.size(), static_cast<brillo::Blob::size_type>(0));
if (IsDiffOperationBetter(operation,
data_blob.size(),
bsdiff_delta.size(),
src_extents.size())) {
+ if (config.enable_vabc_xor) {
+ PopulateXorOps(&aop, bsdiff_delta);
+ }
operation.set_type(operation_type);
data_blob = std::move(bsdiff_delta);
}
@@ -860,11 +973,9 @@
if (!IsNoSourceOperation(operation.type())) {
StoreExtents(src_extents, operation.mutable_src_extents());
}
- // All operations have dst_extents.
- StoreExtents(dst_extents, operation.mutable_dst_extents());
*out_data = std::move(data_blob);
- *out_op = operation;
+ *out_op = aop;
return true;
}
diff --git a/payload_generator/delta_diff_utils.h b/payload_generator/delta_diff_utils.h
index c75d16d..f284530 100644
--- a/payload_generator/delta_diff_utils.h
+++ b/payload_generator/delta_diff_utils.h
@@ -48,7 +48,7 @@
const PartitionConfig& new_part,
ssize_t hard_chunk_blocks,
size_t soft_chunk_blocks,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& version,
BlobFileWriter* blob_file);
// Create operations in |aops| for identical blocks that moved around in the old
@@ -67,7 +67,7 @@
size_t old_num_blocks,
size_t new_num_blocks,
ssize_t chunk_blocks,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& version,
BlobFileWriter* blob_file,
ExtentRanges* old_visited_blocks,
ExtentRanges* new_visited_blocks,
@@ -90,7 +90,7 @@
const std::vector<puffin::BitExtent>& new_deflates,
const std::string& name,
ssize_t chunk_blocks,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& config,
BlobFileWriter* blob_file);
// Reads the blocks |old_extents| from |old_part| (if it exists) and the
@@ -108,9 +108,9 @@
const std::vector<Extent>& new_extents,
const std::vector<puffin::BitExtent>& old_deflates,
const std::vector<puffin::BitExtent>& new_deflates,
- const PayloadVersion& version,
+ const PayloadGenerationConfig& version,
brillo::Blob* out_data,
- InstallOperation* out_op);
+ AnnotatedOperation* out_op);
// Generates the best allowed full operation to produce |new_data|. The allowed
// operations are based on |payload_version|. The operation blob will be stored
@@ -149,6 +149,15 @@
const std::map<std::string, FilesystemInterface::File>& old_files_map,
const std::string& new_file_name);
+// Read BSDIFF patch data in |data|, compute list of blocks that can be COW_XOR,
+// store these blocks in |aop|.
+bool PopulateXorOps(AnnotatedOperation* aop, const uint8_t* data, size_t size);
+
+inline bool PopulateXorOps(AnnotatedOperation* aop,
+ const brillo::Blob& patch_data) {
+ return PopulateXorOps(aop, patch_data.data(), patch_data.size());
+}
+
} // namespace diff_utils
} // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_utils_unittest.cc b/payload_generator/delta_diff_utils_unittest.cc
index f2db1bd..436e265 100644
--- a/payload_generator/delta_diff_utils_unittest.cc
+++ b/payload_generator/delta_diff_utils_unittest.cc
@@ -24,6 +24,7 @@
#include <base/files/scoped_file.h>
#include <base/format_macros.h>
#include <base/strings/stringprintf.h>
+#include <bsdiff/patch_writer.h>
#include <gtest/gtest.h>
#include "update_engine/common/test_utils.h"
@@ -133,7 +134,7 @@
old_part_.size / block_size_,
new_part_.size / block_size_,
chunk_blocks,
- version,
+ {.version = version},
&blob_file,
&old_visited_blocks_,
&new_visited_blocks_,
@@ -164,22 +165,22 @@
new_part_.verity.fec_extent = ExtentForRange(40, 50);
BlobFileWriter blob_file(tmp_blob_file_.fd(), &blob_size_);
- EXPECT_TRUE(diff_utils::DeltaReadPartition(
+ ASSERT_TRUE(diff_utils::DeltaReadPartition(
&aops_,
old_part_,
new_part_,
-1,
-1,
- PayloadVersion(kMaxSupportedMajorPayloadVersion,
- kVerityMinorPayloadVersion),
+ {.version = PayloadVersion(kMaxSupportedMajorPayloadVersion,
+ kVerityMinorPayloadVersion)},
&blob_file));
for (const auto& aop : aops_) {
new_visited_blocks_.AddRepeatedExtents(aop.op.dst_extents());
}
for (const auto& extent : new_visited_blocks_.extent_set()) {
- EXPECT_FALSE(ExtentRanges::ExtentsOverlap(
+ ASSERT_FALSE(ExtentRanges::ExtentsOverlap(
extent, new_part_.verity.hash_tree_extent));
- EXPECT_FALSE(
+ ASSERT_FALSE(
ExtentRanges::ExtentsOverlap(extent, new_part_.verity.fec_extent));
}
}
@@ -203,34 +204,36 @@
for (int i = 0; i < 2; i++) {
brillo::Blob data_to_test = i == 0 ? random_data : ones;
// The old_extents will be initialized with 0.
- EXPECT_TRUE(
+ ASSERT_TRUE(
WriteExtents(new_part_.path, new_extents, kBlockSize, data_to_test));
brillo::Blob data;
- InstallOperation op;
- EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
+ AnnotatedOperation aop;
+ InstallOperation& op = aop.op;
+ ASSERT_TRUE(diff_utils::ReadExtentsToDiff(
old_part_.path,
new_part_.path,
old_extents,
new_extents,
{}, // old_deflates
{}, // new_deflates
- PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
+ {.version = PayloadVersion(kBrilloMajorPayloadVersion,
+ kSourceMinorPayloadVersion)},
&data,
- &op));
- EXPECT_FALSE(data.empty());
+ &aop));
+ ASSERT_FALSE(data.empty());
- EXPECT_TRUE(op.has_type());
+ ASSERT_TRUE(op.has_type());
const InstallOperation::Type expected_type =
(i == 0 ? InstallOperation::REPLACE : InstallOperation::REPLACE_BZ);
- EXPECT_EQ(expected_type, op.type());
- EXPECT_FALSE(op.has_data_offset());
- EXPECT_FALSE(op.has_data_length());
- EXPECT_EQ(0, op.src_extents_size());
- EXPECT_FALSE(op.has_src_length());
- EXPECT_EQ(1, op.dst_extents_size());
- EXPECT_FALSE(op.has_dst_length());
- EXPECT_EQ(1U, utils::BlocksInExtents(op.dst_extents()));
+ ASSERT_EQ(expected_type, op.type());
+ ASSERT_FALSE(op.has_data_offset());
+ ASSERT_FALSE(op.has_data_length());
+ ASSERT_EQ(0, op.src_extents_size());
+ ASSERT_FALSE(op.has_src_length());
+ ASSERT_EQ(1, op.dst_extents_size());
+ ASSERT_FALSE(op.has_dst_length());
+ ASSERT_EQ(1U, utils::BlocksInExtents(op.dst_extents()));
}
}
@@ -245,25 +248,27 @@
vector<Extent> old_extents = {ExtentForRange(11, 1)};
vector<Extent> new_extents = {ExtentForRange(1, 1)};
- EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
- EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
+ ASSERT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+ ASSERT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
brillo::Blob data;
- InstallOperation op;
- EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
+ AnnotatedOperation aop;
+ ASSERT_TRUE(diff_utils::ReadExtentsToDiff(
old_part_.path,
new_part_.path,
old_extents,
new_extents,
{}, // old_deflates
{}, // new_deflates
- PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
+ {.version = PayloadVersion(kBrilloMajorPayloadVersion,
+ kSourceMinorPayloadVersion)},
&data,
- &op));
- EXPECT_TRUE(data.empty());
+ &aop));
+ InstallOperation& op = aop.op;
+ ASSERT_TRUE(data.empty());
- EXPECT_TRUE(op.has_type());
- EXPECT_EQ(InstallOperation::SOURCE_COPY, op.type());
+ ASSERT_TRUE(op.has_type());
+ ASSERT_EQ(InstallOperation::SOURCE_COPY, op.type());
}
TEST_F(DeltaDiffUtilsTest, SourceBsdiffTest) {
@@ -277,27 +282,28 @@
vector<Extent> old_extents = {ExtentForRange(1, 1)};
vector<Extent> new_extents = {ExtentForRange(2, 1)};
- EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+ ASSERT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
// Modify one byte in the new file.
data_blob[0]++;
- EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
+ ASSERT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
brillo::Blob data;
- InstallOperation op;
- EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
+ AnnotatedOperation aop;
+ ASSERT_TRUE(diff_utils::ReadExtentsToDiff(
old_part_.path,
new_part_.path,
old_extents,
new_extents,
{}, // old_deflates
{}, // new_deflates
- PayloadVersion(kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion),
+ {.version = PayloadVersion(kBrilloMajorPayloadVersion,
+ kSourceMinorPayloadVersion)},
&data,
- &op));
-
- EXPECT_FALSE(data.empty());
- EXPECT_TRUE(op.has_type());
- EXPECT_EQ(InstallOperation::SOURCE_BSDIFF, op.type());
+ &aop));
+ auto& op = aop.op;
+ ASSERT_FALSE(data.empty());
+ ASSERT_TRUE(op.has_type());
+ ASSERT_EQ(InstallOperation::SOURCE_BSDIFF, op.type());
}
TEST_F(DeltaDiffUtilsTest, PreferReplaceTest) {
@@ -307,28 +313,28 @@
// Write something in the first 50 bytes so that REPLACE_BZ will be slightly
// larger than BROTLI_BSDIFF.
std::iota(data_blob.begin(), data_blob.begin() + 50, 0);
- EXPECT_TRUE(WriteExtents(old_part_.path, extents, kBlockSize, data_blob));
+ ASSERT_TRUE(WriteExtents(old_part_.path, extents, kBlockSize, data_blob));
// Shift the first 50 bytes in the new file by one.
std::iota(data_blob.begin(), data_blob.begin() + 50, 1);
- EXPECT_TRUE(WriteExtents(new_part_.path, extents, kBlockSize, data_blob));
+ ASSERT_TRUE(WriteExtents(new_part_.path, extents, kBlockSize, data_blob));
brillo::Blob data;
- InstallOperation op;
- EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
+ AnnotatedOperation aop;
+ ASSERT_TRUE(diff_utils::ReadExtentsToDiff(
old_part_.path,
new_part_.path,
extents,
extents,
{}, // old_deflates
{}, // new_deflates
- PayloadVersion(kMaxSupportedMajorPayloadVersion,
- kMaxSupportedMinorPayloadVersion),
+ {.version = PayloadVersion(kMaxSupportedMajorPayloadVersion,
+ kMaxSupportedMinorPayloadVersion)},
&data,
- &op));
-
- EXPECT_FALSE(data.empty());
- EXPECT_TRUE(op.has_type());
- EXPECT_EQ(InstallOperation::REPLACE_BZ, op.type());
+ &aop));
+ auto& op = aop.op;
+ ASSERT_FALSE(data.empty());
+ ASSERT_TRUE(op.has_type());
+ ASSERT_EQ(InstallOperation::REPLACE_BZ, op.type());
}
// Test the simple case where all the blocks are different and no new blocks are
@@ -337,13 +343,13 @@
InitializePartitionWithUniqueBlocks(old_part_, block_size_, 5);
InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
- EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
+ ASSERT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
kSourceMinorPayloadVersion));
- EXPECT_EQ(0U, old_visited_blocks_.blocks());
- EXPECT_EQ(0U, new_visited_blocks_.blocks());
- EXPECT_EQ(0, blob_size_);
- EXPECT_TRUE(aops_.empty());
+ ASSERT_EQ(0U, old_visited_blocks_.blocks());
+ ASSERT_EQ(0U, new_visited_blocks_.blocks());
+ ASSERT_EQ(0, blob_size_);
+ ASSERT_TRUE(aops_.empty());
}
// Test that when the partitions have identical blocks in the same positions
@@ -364,21 +370,21 @@
// Override some of the old blocks with different data.
vector<Extent> different_blocks = {ExtentForRange(40, 5)};
- EXPECT_TRUE(WriteExtents(old_part_.path,
+ ASSERT_TRUE(WriteExtents(old_part_.path,
different_blocks,
kBlockSize,
brillo::Blob(5 * kBlockSize, 'a')));
- EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(10, // chunk_blocks
+ ASSERT_TRUE(RunDeltaMovedAndZeroBlocks(10, // chunk_blocks
kSourceMinorPayloadVersion));
ExtentRanges expected_ranges;
expected_ranges.AddExtent(ExtentForRange(0, 50));
expected_ranges.SubtractExtents(different_blocks);
- EXPECT_EQ(expected_ranges.extent_set(), old_visited_blocks_.extent_set());
- EXPECT_EQ(expected_ranges.extent_set(), new_visited_blocks_.extent_set());
- EXPECT_EQ(0, blob_size_);
+ ASSERT_EQ(expected_ranges.extent_set(), old_visited_blocks_.extent_set());
+ ASSERT_EQ(expected_ranges.extent_set(), new_visited_blocks_.extent_set());
+ ASSERT_EQ(0, blob_size_);
// We expect all the blocks that we didn't override with |different_blocks|
// and that we didn't mark as visited in |already_visited| to match and have a
@@ -391,15 +397,15 @@
ExtentForRange(45, 5),
};
- EXPECT_EQ(expected_op_extents.size(), aops_.size());
+ ASSERT_EQ(expected_op_extents.size(), aops_.size());
for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
const AnnotatedOperation& aop = aops_[i];
- EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
- EXPECT_EQ(1, aop.op.src_extents_size());
- EXPECT_EQ(expected_op_extents[i], aop.op.src_extents(0));
- EXPECT_EQ(1, aop.op.dst_extents_size());
- EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
+ ASSERT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+ ASSERT_EQ(1, aop.op.src_extents_size());
+ ASSERT_EQ(expected_op_extents[i], aop.op.src_extents(0));
+ ASSERT_EQ(1, aop.op.dst_extents_size());
+ ASSERT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
}
}
@@ -419,28 +425,28 @@
std::copy(
file_data.begin(), file_data.end(), partition_data.begin() + offset);
}
- EXPECT_TRUE(test_utils::WriteFileVector(old_part_.path, partition_data));
- EXPECT_TRUE(test_utils::WriteFileVector(new_part_.path, partition_data));
+ ASSERT_TRUE(test_utils::WriteFileVector(old_part_.path, partition_data));
+ ASSERT_TRUE(test_utils::WriteFileVector(new_part_.path, partition_data));
- EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
+ ASSERT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
kSourceMinorPayloadVersion));
// There should be only one SOURCE_COPY, for the whole partition and the
// source extents should cover only the first copy of the source file since
// we prefer to re-read files (maybe cached) instead of continue reading the
// rest of the partition.
- EXPECT_EQ(1U, aops_.size());
+ ASSERT_EQ(1U, aops_.size());
const AnnotatedOperation& aop = aops_[0];
- EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
- EXPECT_EQ(5, aop.op.src_extents_size());
+ ASSERT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+ ASSERT_EQ(5, aop.op.src_extents_size());
for (int i = 0; i < aop.op.src_extents_size(); ++i) {
- EXPECT_EQ(ExtentForRange(0, 10), aop.op.src_extents(i));
+ ASSERT_EQ(ExtentForRange(0, 10), aop.op.src_extents(i));
}
- EXPECT_EQ(1, aop.op.dst_extents_size());
- EXPECT_EQ(ExtentForRange(0, 50), aop.op.dst_extents(0));
+ ASSERT_EQ(1, aop.op.dst_extents_size());
+ ASSERT_EQ(ExtentForRange(0, 50), aop.op.dst_extents(0));
- EXPECT_EQ(0, blob_size_);
+ ASSERT_EQ(0, blob_size_);
}
// Test that all blocks with zeros are handled separately using REPLACE_BZ
@@ -460,21 +466,21 @@
};
brillo::Blob zeros_data(utils::BlocksInExtents(new_zeros) * block_size_,
'\0');
- EXPECT_TRUE(WriteExtents(new_part_.path, new_zeros, block_size_, zeros_data));
+ ASSERT_TRUE(WriteExtents(new_part_.path, new_zeros, block_size_, zeros_data));
vector<Extent> old_zeros = vector<Extent>{ExtentForRange(43, 7)};
- EXPECT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
+ ASSERT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
- EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(5, // chunk_blocks
+ ASSERT_TRUE(RunDeltaMovedAndZeroBlocks(5, // chunk_blocks
kSourceMinorPayloadVersion));
// Zeroed blocks from |old_visited_blocks_| were copied over.
- EXPECT_EQ(old_zeros,
+ ASSERT_EQ(old_zeros,
old_visited_blocks_.GetExtentsForBlockCount(
old_visited_blocks_.blocks()));
// All the new zeroed blocks should be used with REPLACE_BZ.
- EXPECT_EQ(new_zeros,
+ ASSERT_EQ(new_zeros,
new_visited_blocks_.GetExtentsForBlockCount(
new_visited_blocks_.blocks()));
@@ -488,16 +494,16 @@
ExtentForRange(45, 5),
};
- EXPECT_EQ(expected_op_extents.size(), aops_.size());
+ ASSERT_EQ(expected_op_extents.size(), aops_.size());
for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
const AnnotatedOperation& aop = aops_[i];
- EXPECT_EQ(InstallOperation::REPLACE_BZ, aop.op.type());
- EXPECT_EQ(0, aop.op.src_extents_size());
- EXPECT_EQ(1, aop.op.dst_extents_size());
- EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
+ ASSERT_EQ(InstallOperation::REPLACE_BZ, aop.op.type());
+ ASSERT_EQ(0, aop.op.src_extents_size());
+ ASSERT_EQ(1, aop.op.dst_extents_size());
+ ASSERT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
}
- EXPECT_NE(0, blob_size_);
+ ASSERT_NE(0, blob_size_);
}
TEST_F(DeltaDiffUtilsTest, ShuffledBlocksAreTracked) {
@@ -515,39 +521,39 @@
// |permutation| order. Block i in the old_part_ will contain the same data
// as block permutation[i] in the new_part_.
brillo::Blob new_contents;
- EXPECT_TRUE(utils::ReadFile(new_part_.path, &new_contents));
- EXPECT_TRUE(
+ ASSERT_TRUE(utils::ReadFile(new_part_.path, &new_contents));
+ ASSERT_TRUE(
WriteExtents(old_part_.path, perm_extents, block_size_, new_contents));
- EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
+ ASSERT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
kSourceMinorPayloadVersion));
- EXPECT_EQ(permutation.size(), old_visited_blocks_.blocks());
- EXPECT_EQ(permutation.size(), new_visited_blocks_.blocks());
+ ASSERT_EQ(permutation.size(), old_visited_blocks_.blocks());
+ ASSERT_EQ(permutation.size(), new_visited_blocks_.blocks());
// There should be only one SOURCE_COPY, with a complicate list of extents.
- EXPECT_EQ(1U, aops_.size());
+ ASSERT_EQ(1U, aops_.size());
const AnnotatedOperation& aop = aops_[0];
- EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+ ASSERT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
vector<Extent> aop_src_extents;
ExtentsToVector(aop.op.src_extents(), &aop_src_extents);
- EXPECT_EQ(perm_extents, aop_src_extents);
+ ASSERT_EQ(perm_extents, aop_src_extents);
- EXPECT_EQ(1, aop.op.dst_extents_size());
- EXPECT_EQ(ExtentForRange(0, permutation.size()), aop.op.dst_extents(0));
+ ASSERT_EQ(1, aop.op.dst_extents_size());
+ ASSERT_EQ(ExtentForRange(0, permutation.size()), aop.op.dst_extents(0));
- EXPECT_EQ(0, blob_size_);
+ ASSERT_EQ(0, blob_size_);
}
TEST_F(DeltaDiffUtilsTest, IsExtFilesystemTest) {
- EXPECT_TRUE(diff_utils::IsExtFilesystem(
+ ASSERT_TRUE(diff_utils::IsExtFilesystem(
test_utils::GetBuildArtifactsPath("gen/disk_ext2_1k.img")));
- EXPECT_TRUE(diff_utils::IsExtFilesystem(
+ ASSERT_TRUE(diff_utils::IsExtFilesystem(
test_utils::GetBuildArtifactsPath("gen/disk_ext2_4k.img")));
}
TEST_F(DeltaDiffUtilsTest, GetOldFileEmptyTest) {
- EXPECT_TRUE(diff_utils::GetOldFile({}, "filename").name.empty());
+ ASSERT_TRUE(diff_utils::GetOldFile({}, "filename").name.empty());
}
TEST_F(DeltaDiffUtilsTest, GetOldFileTest) {
@@ -569,24 +575,135 @@
// Always return exact match if possible.
for (const auto& name : file_list)
- EXPECT_EQ(diff_utils::GetOldFile(old_files_map, name).name, name);
+ ASSERT_EQ(diff_utils::GetOldFile(old_files_map, name).name, name);
- EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "file_name").name,
+ ASSERT_EQ(diff_utils::GetOldFile(old_files_map, "file_name").name,
"filename");
- EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "filename_new.zip").name,
+ ASSERT_EQ(diff_utils::GetOldFile(old_files_map, "filename_new.zip").name,
"filename.zip");
- EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "version1.2").name,
+ ASSERT_EQ(diff_utils::GetOldFile(old_files_map, "version1.2").name,
"version1.1");
- EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "version3.0").name,
+ ASSERT_EQ(diff_utils::GetOldFile(old_files_map, "version3.0").name,
"version2.0");
- EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "_version").name, "version");
- EXPECT_EQ(
+ ASSERT_EQ(diff_utils::GetOldFile(old_files_map, "_version").name, "version");
+ ASSERT_EQ(
diff_utils::GetOldFile(old_files_map, "update_engine_unittest").name,
"update_engine");
- EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "bin/delta_generator").name,
+ ASSERT_EQ(diff_utils::GetOldFile(old_files_map, "bin/delta_generator").name,
"delta_generator");
// Check file name with minimum size.
- EXPECT_EQ(diff_utils::GetOldFile(old_files_map, "a").name, "filename");
+ ASSERT_EQ(diff_utils::GetOldFile(old_files_map, "a").name, "filename");
+}
+
+TEST_F(DeltaDiffUtilsTest, XorOpsSourceNotAligned) {
+ ScopedTempFile patch_file;
+ bsdiff::BsdiffPatchWriter writer{patch_file.path()};
+ ASSERT_TRUE(writer.Init(kBlockSize * 10));
+ ASSERT_TRUE(writer.AddControlEntry(ControlEntry(0, 0, 123 + kBlockSize)));
+ ASSERT_TRUE(writer.AddControlEntry(ControlEntry(kBlockSize, 0, 0)));
+ ASSERT_TRUE(writer.Close());
+
+ std::string patch_data;
+ utils::ReadFile(patch_file.path(), &patch_data);
+
+ AnnotatedOperation aop;
+ *aop.op.add_src_extents() = ExtentForRange(50, 10);
+ *aop.op.add_dst_extents() = ExtentForRange(500, 10);
+
+ diff_utils::PopulateXorOps(
+ &aop,
+ reinterpret_cast<const uint8_t*>(patch_data.data()),
+ patch_data.size());
+ ASSERT_EQ(aop.xor_ops.size(), 1UL) << "Only 1 block can possibly be XORed";
+ ASSERT_EQ(aop.xor_ops[0].src_extent().num_blocks(), 1UL);
+ ASSERT_EQ(aop.xor_ops[0].src_extent().start_block(), 51UL);
+ ASSERT_EQ(aop.xor_ops[0].src_offset(), 123UL);
+
+ ASSERT_EQ(aop.xor_ops[0].dst_extent().num_blocks(), 1UL);
+ ASSERT_EQ(aop.xor_ops[0].dst_extent().start_block(), 500UL);
+}
+
+TEST_F(DeltaDiffUtilsTest, XorOpsTargetNotAligned) {
+ ScopedTempFile patch_file;
+ bsdiff::BsdiffPatchWriter writer{patch_file.path()};
+ ASSERT_TRUE(writer.Init(kBlockSize * 10));
+ ASSERT_TRUE(writer.AddControlEntry(
+ ControlEntry(0, kBlockSize - 456, 123 + kBlockSize)));
+ ASSERT_TRUE(writer.AddControlEntry(ControlEntry(kBlockSize + 456, 0, 0)));
+ ASSERT_TRUE(writer.Close());
+
+ std::string patch_data;
+ utils::ReadFile(patch_file.path(), &patch_data);
+
+ AnnotatedOperation aop;
+ *aop.op.add_src_extents() = ExtentForRange(50, 10);
+ *aop.op.add_dst_extents() = ExtentForRange(500, 10);
+
+ diff_utils::PopulateXorOps(
+ &aop,
+ reinterpret_cast<const uint8_t*>(patch_data.data()),
+ patch_data.size());
+ ASSERT_EQ(aop.xor_ops.size(), 1UL) << "Only 1 block can possibly be XORed";
+ ASSERT_EQ(aop.xor_ops[0].src_extent().num_blocks(), 1UL);
+ ASSERT_EQ(aop.xor_ops[0].src_extent().start_block(), 51UL);
+ ASSERT_EQ(aop.xor_ops[0].src_offset(), 123UL + 456UL);
+
+ ASSERT_EQ(aop.xor_ops[0].dst_extent().num_blocks(), 1UL);
+ ASSERT_EQ(aop.xor_ops[0].dst_extent().start_block(), 501UL);
+}
+
+TEST_F(DeltaDiffUtilsTest, XorOpsStrided) {
+ ScopedTempFile patch_file;
+ bsdiff::BsdiffPatchWriter writer{patch_file.path()};
+ ASSERT_TRUE(writer.Init(kBlockSize * 10));
+ ASSERT_TRUE(writer.AddControlEntry(ControlEntry(0, kBlockSize - 456, 123)));
+ ASSERT_TRUE(
+ writer.AddControlEntry(ControlEntry(kBlockSize * 10 + 456, 0, 0)));
+ ASSERT_TRUE(writer.Close());
+
+ std::string patch_data;
+ utils::ReadFile(patch_file.path(), &patch_data);
+
+ AnnotatedOperation aop;
+ *aop.op.add_src_extents() = ExtentForRange(50, 5);
+ *aop.op.add_src_extents() = ExtentForRange(60, 5);
+
+ *aop.op.add_dst_extents() = ExtentForRange(500, 2);
+ *aop.op.add_dst_extents() = ExtentForRange(600, 2);
+ *aop.op.add_dst_extents() = ExtentForRange(700, 7);
+
+ diff_utils::PopulateXorOps(
+ &aop,
+ reinterpret_cast<const uint8_t*>(patch_data.data()),
+ patch_data.size());
+ ASSERT_EQ(aop.xor_ops.size(), 4UL);
+ for (const auto& op : aop.xor_ops) {
+ ASSERT_EQ(op.src_offset(), 123UL + 456UL);
+ LOG(INFO) << op.src_extent() << ", " << op.dst_extent();
+ }
+ ASSERT_EQ(aop.xor_ops[0].src_extent().num_blocks(), 2UL);
+ ASSERT_EQ(aop.xor_ops[0].src_extent().start_block(), 50UL);
+
+ ASSERT_EQ(aop.xor_ops[0].dst_extent().num_blocks(), 1UL);
+ ASSERT_EQ(aop.xor_ops[0].dst_extent().start_block(), 501UL);
+
+ ASSERT_EQ(aop.xor_ops[1].src_extent().num_blocks(), 3UL);
+ ASSERT_EQ(aop.xor_ops[1].src_extent().start_block(), 51UL);
+
+ ASSERT_EQ(aop.xor_ops[1].dst_extent().num_blocks(), 2UL);
+ ASSERT_EQ(aop.xor_ops[1].dst_extent().start_block(), 600UL);
+
+ ASSERT_EQ(aop.xor_ops[2].src_extent().num_blocks(), 3UL);
+ ASSERT_EQ(aop.xor_ops[2].src_extent().start_block(), 53UL);
+
+ ASSERT_EQ(aop.xor_ops[2].dst_extent().num_blocks(), 2UL);
+ ASSERT_EQ(aop.xor_ops[2].dst_extent().start_block(), 700UL);
+
+ ASSERT_EQ(aop.xor_ops[3].src_extent().num_blocks(), 6UL);
+ ASSERT_EQ(aop.xor_ops[3].src_extent().start_block(), 60UL);
+
+ ASSERT_EQ(aop.xor_ops[3].dst_extent().num_blocks(), 5UL);
+ ASSERT_EQ(aop.xor_ops[3].dst_extent().start_block(), 702UL);
}
} // namespace chromeos_update_engine
diff --git a/payload_generator/extent_utils.cc b/payload_generator/extent_utils.cc
index 2efef12..f4a9ff0 100644
--- a/payload_generator/extent_utils.cc
+++ b/payload_generator/extent_utils.cc
@@ -87,7 +87,8 @@
}
}
-string ExtentsToString(const vector<Extent>& extents) {
+template <typename Container>
+string ExtentsToStringTemplate(const Container& extents) {
string ext_str;
for (const Extent& e : extents)
ext_str += base::StringPrintf("[%" PRIu64 ", %" PRIu64 "] ",
@@ -96,6 +97,15 @@
return ext_str;
}
+std::string ExtentsToString(const std::vector<Extent>& extents) {
+ return ExtentsToStringTemplate(extents);
+}
+
+std::string ExtentsToString(
+ const google::protobuf::RepeatedPtrField<Extent>& extents) {
+ return ExtentsToStringTemplate(extents);
+}
+
void NormalizeExtents(vector<Extent>* extents) {
vector<Extent> new_extents;
for (const Extent& curr_ext : *extents) {
diff --git a/payload_generator/extent_utils.h b/payload_generator/extent_utils.h
index 7aa614a..2bd6626 100644
--- a/payload_generator/extent_utils.h
+++ b/payload_generator/extent_utils.h
@@ -22,6 +22,7 @@
#include <base/logging.h>
+#include "google/protobuf/repeated_field.h"
#include "update_engine/payload_consumer/payload_constants.h"
#include "update_engine/update_metadata.pb.h"
@@ -63,6 +64,8 @@
// Returns a string representing all extents in |extents|.
std::string ExtentsToString(const std::vector<Extent>& extents);
+std::string ExtentsToString(
+ const google::protobuf::RepeatedPtrField<Extent>& extents);
// Takes a pointer to extents |extents| and extents |extents_to_add|, and
// merges them by adding |extents_to_add| to |extents| and normalizing.
@@ -124,6 +127,18 @@
std::ostream& operator<<(std::ostream& out, const Extent& extent);
+template <typename Container>
+size_t GetNthBlock(const Container& extents, const size_t n) {
+ size_t cur_block_count = 0;
+ for (const auto& extent : extents) {
+ if (n - cur_block_count < extent.num_blocks()) {
+ return extent.start_block() + (n - cur_block_count);
+ }
+ cur_block_count += extent.num_blocks();
+ }
+ return std::numeric_limits<size_t>::max();
+}
+
} // namespace chromeos_update_engine
#endif // UPDATE_ENGINE_PAYLOAD_GENERATOR_EXTENT_UTILS_H_
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index b04fec0..1919a7e 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -425,6 +425,9 @@
disable_vabc,
false,
"Whether to disable Virtual AB Compression when installing the OTA");
+ DEFINE_bool(enable_vabc_xor,
+ false,
+ "Whether to use Virtual AB Compression XOR feature");
DEFINE_string(
apex_info_file, "", "Path to META/apex_info.pb found in target build");
@@ -543,6 +546,8 @@
payload_config.apex_info_file = FLAGS_apex_info_file;
}
+ payload_config.enable_vabc_xor = FLAGS_enable_vabc_xor;
+
if (!FLAGS_new_partitions.empty()) {
LOG_IF(FATAL, !FLAGS_new_image.empty() || !FLAGS_new_kernel.empty())
<< "--new_image and --new_kernel are deprecated, please use "
diff --git a/payload_generator/merge_sequence_generator.cc b/payload_generator/merge_sequence_generator.cc
index 289e2f8..c5fd988 100644
--- a/payload_generator/merge_sequence_generator.cc
+++ b/payload_generator/merge_sequence_generator.cc
@@ -18,16 +18,22 @@
#include <algorithm>
+#include "update_engine/payload_generator/delta_diff_generator.h"
+#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 {
CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
- const Extent& dst_extent) {
+ const Extent& dst_extent,
+ CowMergeOperation::Type op_type,
+ uint32_t src_offset) {
CowMergeOperation ret;
- ret.set_type(CowMergeOperation::COW_COPY);
+ ret.set_type(op_type);
*ret.mutable_src_extent() = src_extent;
*ret.mutable_dst_extent() = dst_extent;
+ ret.set_src_offset(src_offset);
return ret;
}
@@ -36,6 +42,17 @@
os << "CowMergeOperation src extent: "
<< ExtentsToString({merge_operation.src_extent()})
<< ", dst extent: " << ExtentsToString({merge_operation.dst_extent()});
+ if (merge_operation.has_src_offset()) {
+ os << ", src offset: " << merge_operation.src_offset();
+ }
+ os << " op_type: ";
+ if (merge_operation.type() == CowMergeOperation::COW_COPY) {
+ os << "COW_COPY";
+ } else if (merge_operation.type() == CowMergeOperation::COW_XOR) {
+ os << "COW_XOR";
+ } else {
+ os << merge_operation.type();
+ }
return os;
}
@@ -56,12 +73,27 @@
return abs_diff;
}
+CowMergeOperation::Type GetCowOpType(InstallOperation::Type install_op_type) {
+ switch (install_op_type) {
+ case InstallOperation::SOURCE_COPY:
+ return CowMergeOperation::COW_COPY;
+ case InstallOperation::SOURCE_BSDIFF:
+ case InstallOperation::BROTLI_BSDIFF:
+ case InstallOperation::PUFFDIFF:
+ return CowMergeOperation::COW_XOR;
+ default:
+ CHECK(false) << "Unknown install op type: " << install_op_type;
+ return CowMergeOperation::COW_REPLACE;
+ }
+}
+
void SplitSelfOverlapping(const Extent& src_extent,
const Extent& dst_extent,
std::vector<CowMergeOperation>* sequence) {
CHECK_EQ(src_extent.num_blocks(), dst_extent.num_blocks());
if (src_extent.start_block() == dst_extent.start_block()) {
- sequence->emplace_back(CreateCowMergeOperation(src_extent, dst_extent));
+ sequence->emplace_back(CreateCowMergeOperation(
+ src_extent, dst_extent, CowMergeOperation::COW_COPY));
return;
}
@@ -71,51 +103,89 @@
auto num_blocks = std::min<size_t>(diff, src_extent.num_blocks() - i);
sequence->emplace_back(CreateCowMergeOperation(
ExtentForRange(i + src_extent.start_block(), num_blocks),
- ExtentForRange(i + dst_extent.start_block(), num_blocks)));
+ ExtentForRange(i + dst_extent.start_block(), num_blocks),
+ CowMergeOperation::COW_COPY));
}
}
+static bool ProcessXorOps(std::vector<CowMergeOperation>* sequence,
+ const AnnotatedOperation& aop) {
+ const auto size_before = sequence->size();
+ sequence->insert(sequence->end(), aop.xor_ops.begin(), aop.xor_ops.end());
+ std::for_each(
+ sequence->begin() + size_before,
+ sequence->end(),
+ [](CowMergeOperation& op) {
+ CHECK_EQ(op.type(), CowMergeOperation::COW_XOR);
+ // If |src_offset| is greater than 0, then we are reading 1
+ // extra block at the end of src_extent. This dependency must
+ // be honored during merge sequence generation, or we can end
+ // up with a corrupted device after merge.
+ if (op.src_offset() > 0) {
+ if (op.src_extent().num_blocks() == op.dst_extent().num_blocks()) {
+ op.mutable_src_extent()->set_num_blocks(
+ op.src_extent().num_blocks() + 1);
+ }
+ CHECK_EQ(op.src_extent().num_blocks(),
+ op.dst_extent().num_blocks() + 1);
+ }
+ });
+ return true;
+}
+
+static bool ProcessCopyOps(std::vector<CowMergeOperation>* sequence,
+ const AnnotatedOperation& aop) {
+ CHECK_EQ(GetCowOpType(aop.op.type()), CowMergeOperation::COW_COPY);
+ if (aop.op.dst_extents().size() != 1) {
+ std::vector<Extent> out_extents;
+ ExtentsToVector(aop.op.dst_extents(), &out_extents);
+ LOG(ERROR)
+ << "The dst extents for source_copy are expected to be contiguous,"
+ << " dst extents: " << ExtentsToString(out_extents);
+ return false;
+ }
+ // Split the source extents.
+ size_t used_blocks = 0;
+ for (const auto& src_extent : aop.op.src_extents()) {
+ // The dst_extent in the merge sequence will be a subset of
+ // InstallOperation's dst_extent. This will simplify the OTA -> COW
+ // conversion when we install the payload.
+ Extent dst_extent =
+ ExtentForRange(aop.op.dst_extents(0).start_block() + used_blocks,
+ src_extent.num_blocks());
+ // Self-overlapping operation, must split into multiple non
+ // self-overlapping ops
+ if (ExtentRanges::ExtentsOverlap(src_extent, dst_extent)) {
+ SplitSelfOverlapping(src_extent, dst_extent, sequence);
+ } else {
+ sequence->emplace_back(CreateCowMergeOperation(
+ src_extent, dst_extent, CowMergeOperation::COW_COPY));
+ }
+ used_blocks += src_extent.num_blocks();
+ }
+
+ if (used_blocks != aop.op.dst_extents(0).num_blocks()) {
+ LOG(ERROR) << "Number of blocks in src extents doesn't equal to the"
+ << " ones in the dst extents, src blocks " << used_blocks
+ << ", dst blocks " << aop.op.dst_extents(0).num_blocks();
+ return false;
+ }
+ return true;
+}
+
std::unique_ptr<MergeSequenceGenerator> MergeSequenceGenerator::Create(
const std::vector<AnnotatedOperation>& aops) {
std::vector<CowMergeOperation> sequence;
+
for (const auto& aop : aops) {
- // Only handle SOURCE_COPY now for the cow size optimization.
- if (aop.op.type() != InstallOperation::SOURCE_COPY) {
- continue;
- }
- if (aop.op.dst_extents().size() != 1) {
- std::vector<Extent> out_extents;
- ExtentsToVector(aop.op.dst_extents(), &out_extents);
- LOG(ERROR) << "The dst extents for source_copy expects to be contiguous,"
- << " dst extents: " << ExtentsToString(out_extents);
- return nullptr;
- }
-
- // Split the source extents.
- size_t used_blocks = 0;
- for (const auto& src_extent : aop.op.src_extents()) {
- // The dst_extent in the merge sequence will be a subset of
- // InstallOperation's dst_extent. This will simplify the OTA -> COW
- // conversion when we install the payload.
- Extent dst_extent =
- ExtentForRange(aop.op.dst_extents(0).start_block() + used_blocks,
- src_extent.num_blocks());
-
- // Self-overlapping SOURCE_COPY, must split into multiple non
- // self-overlapping ops
- if (ExtentRanges::ExtentsOverlap(src_extent, dst_extent)) {
- SplitSelfOverlapping(src_extent, dst_extent, &sequence);
- } else {
- sequence.emplace_back(CreateCowMergeOperation(src_extent, dst_extent));
+ if (aop.op.type() == InstallOperation::SOURCE_COPY) {
+ if (!ProcessCopyOps(&sequence, aop)) {
+ return nullptr;
}
- used_blocks += src_extent.num_blocks();
- }
-
- if (used_blocks != aop.op.dst_extents(0).num_blocks()) {
- LOG(ERROR) << "Number of blocks in src extents doesn't equal to the"
- << " ones in the dst extents, src blocks " << used_blocks
- << ", dst blocks " << aop.op.dst_extents(0).num_blocks();
- return nullptr;
+ } else if (!aop.xor_ops.empty()) {
+ if (!ProcessXorOps(&sequence, aop)) {
+ return nullptr;
+ }
}
}
@@ -167,7 +237,7 @@
}
auto ret = merge_after.emplace(op, std::move(operations));
// Check the insertion indeed happens.
- CHECK(ret.second);
+ CHECK(ret.second) << op;
}
}
@@ -221,9 +291,9 @@
for (const auto& op : free_operations) {
incoming_edges.erase(op);
- // Now that this particular operation is merged, other operations blocked
- // by this one may be free. Decrement the count of blocking operations,
- // and set up the free operations for the next iteration.
+ // Now that this particular operation is merged, other operations
+ // blocked by this one may be free. Decrement the count of blocking
+ // operations, and set up the free operations for the next iteration.
for (const auto& blocked : merge_after[op]) {
auto it = incoming_edges.find(blocked);
if (it == incoming_edges.end()) {
@@ -271,6 +341,7 @@
LOG(INFO) << "Blocks in merge sequence " << blocks_in_sequence
<< ", blocks in raw " << blocks_in_raw;
if (!ValidateSequence(merge_sequence)) {
+ LOG(ERROR) << "Invalid Sequence";
return false;
}
@@ -283,6 +354,16 @@
LOG(INFO) << "Validating merge sequence";
ExtentRanges visited;
for (const auto& op : sequence) {
+ // If |src_offset| is greater than zero, dependency should include 1 extra
+ // block at end of src_extent, as the OP actually references data past
+ // original src_extent.
+ if (op.src_offset() > 0) {
+ CHECK_EQ(op.src_extent().num_blocks(), op.dst_extent().num_blocks() + 1)
+ << op;
+ } else {
+ CHECK_EQ(op.src_extent().num_blocks(), op.dst_extent().num_blocks())
+ << op;
+ }
if (visited.OverlapsWithExtent(op.src_extent())) {
LOG(ERROR) << "Transfer violates the merge sequence " << op
<< "Visited extent ranges: ";
diff --git a/payload_generator/merge_sequence_generator.h b/payload_generator/merge_sequence_generator.h
index 385fcc3..d3b5eb9 100644
--- a/payload_generator/merge_sequence_generator.h
+++ b/payload_generator/merge_sequence_generator.h
@@ -31,7 +31,9 @@
namespace chromeos_update_engine {
// Constructs CowMergeOperation from src & dst extents
CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
- const Extent& dst_extent);
+ const Extent& dst_extent,
+ CowMergeOperation::Type op_type,
+ uint32_t src_offset = 0);
// Comparator for CowMergeOperation.
bool operator<(const CowMergeOperation& op1, const CowMergeOperation& op2);
@@ -67,7 +69,7 @@
bool FindDependency(std::map<CowMergeOperation, std::set<CowMergeOperation>>*
merge_after) const;
// The list of CowMergeOperations to sort.
- std::vector<CowMergeOperation> operations_;
+ const std::vector<CowMergeOperation> operations_;
};
void SplitSelfOverlapping(const Extent& src_extent,
diff --git a/payload_generator/merge_sequence_generator_unittest.cc b/payload_generator/merge_sequence_generator_unittest.cc
index b8507ed..86d4fdd 100644
--- a/payload_generator/merge_sequence_generator_unittest.cc
+++ b/payload_generator/merge_sequence_generator_unittest.cc
@@ -20,11 +20,18 @@
#include <gtest/gtest.h>
#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_generator/delta_diff_generator.h"
#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/extent_utils.h"
#include "update_engine/payload_generator/merge_sequence_generator.h"
+#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
+CowMergeOperation CreateCowMergeOperation(const Extent& src_extent,
+ const Extent& dst_extent) {
+ return CreateCowMergeOperation(
+ src_extent, dst_extent, CowMergeOperation::COW_COPY);
+}
class MergeSequenceGeneratorTest : public ::testing::Test {
protected:
void VerifyTransfers(MergeSequenceGenerator* generator,
@@ -51,7 +58,7 @@
};
TEST_F(MergeSequenceGeneratorTest, Create) {
- std::vector<AnnotatedOperation> aops{{"file1", {}}, {"file2", {}}};
+ std::vector<AnnotatedOperation> aops{{"file1", {}, {}}, {"file2", {}, {}}};
aops[0].op.set_type(InstallOperation::SOURCE_COPY);
*aops[0].op.add_src_extents() = ExtentForRange(10, 10);
*aops[0].op.add_dst_extents() = ExtentForRange(30, 10);
@@ -81,7 +88,7 @@
*(op.add_src_extents()) = ExtentForRange(8, 4);
*(op.add_dst_extents()) = ExtentForRange(10, 8);
- AnnotatedOperation aop{"file1", op};
+ AnnotatedOperation aop{"file1", op, {}};
auto generator = MergeSequenceGenerator::Create({aop});
ASSERT_TRUE(generator);
std::vector<CowMergeOperation> expected = {
@@ -183,8 +190,8 @@
CreateCowMergeOperation(ExtentForRange(10, 10), ExtentForRange(15, 10)),
};
- // file 1,2,3 form a cycle. And file3, whose dst ext has smallest offset, will
- // be converted to raw blocks
+ // file 1,2,3 form a cycle. And file3, whose dst ext has smallest offset,
+ // will be converted to raw blocks
std::vector<CowMergeOperation> expected{
transfers[3], transfers[1], transfers[0]};
GenerateSequence(transfers, expected);
@@ -247,4 +254,174 @@
ValidateSplitSequence(b, a);
}
+TEST_F(MergeSequenceGeneratorTest, GenerateSequenceWithXor) {
+ std::vector<CowMergeOperation> transfers = {
+ // cycle 1
+ CreateCowMergeOperation(ExtentForRange(10, 10),
+ ExtentForRange(25, 10),
+ CowMergeOperation::COW_XOR),
+ CreateCowMergeOperation(ExtentForRange(24, 5), ExtentForRange(35, 5)),
+ CreateCowMergeOperation(ExtentForRange(30, 10),
+ ExtentForRange(15, 10),
+ CowMergeOperation::COW_XOR),
+ // cycle 2
+ CreateCowMergeOperation(ExtentForRange(55, 10), ExtentForRange(60, 10)),
+ CreateCowMergeOperation(ExtentForRange(60, 10),
+ ExtentForRange(70, 10),
+ CowMergeOperation::COW_XOR),
+ CreateCowMergeOperation(ExtentForRange(70, 10), ExtentForRange(55, 10)),
+ };
+
+ // file 3, 6 will be converted to raw.
+ std::vector<CowMergeOperation> expected{
+ transfers[1], transfers[0], transfers[4], transfers[3]};
+ GenerateSequence(transfers, expected);
+}
+
+TEST_F(MergeSequenceGeneratorTest, CreateGeneratorWithXor) {
+ std::vector<AnnotatedOperation> aops;
+ auto& aop = aops.emplace_back();
+ aop.op.set_type(InstallOperation::SOURCE_BSDIFF);
+ *aop.op.mutable_src_extents()->Add() = ExtentForRange(10, 5);
+ *aop.op.mutable_dst_extents()->Add() = ExtentForRange(20, 5);
+ auto& xor_map = aop.xor_ops;
+ {
+ // xor_map[i] = i * kBlockSize + 123;
+ auto& op = xor_map.emplace_back();
+ *op.mutable_src_extent() = ExtentForRange(10, 5);
+ *op.mutable_dst_extent() = ExtentForRange(20, 5);
+ op.set_src_offset(123);
+ op.set_type(CowMergeOperation::COW_XOR);
+ }
+ auto generator = MergeSequenceGenerator::Create(aops);
+ ASSERT_NE(generator, nullptr);
+ std::vector<CowMergeOperation> sequence;
+ ASSERT_TRUE(generator->Generate(&sequence));
+ ASSERT_EQ(sequence.size(), 1UL);
+ ASSERT_EQ(sequence[0].src_extent().start_block(), 10UL);
+ ASSERT_EQ(sequence[0].dst_extent().start_block(), 20UL);
+ ASSERT_EQ(sequence[0].src_extent().num_blocks(), 6UL);
+ ASSERT_EQ(sequence[0].dst_extent().num_blocks(), 5UL);
+ ASSERT_EQ(sequence[0].type(), CowMergeOperation::COW_XOR);
+ ASSERT_EQ(sequence[0].src_offset(), 123UL);
+
+ ASSERT_TRUE(generator->ValidateSequence(sequence));
+}
+
+TEST_F(MergeSequenceGeneratorTest, CreateGeneratorWithXorMultipleExtents) {
+ std::vector<AnnotatedOperation> aops;
+ auto& aop = aops.emplace_back();
+ aop.op.set_type(InstallOperation::SOURCE_BSDIFF);
+ *aop.op.mutable_src_extents()->Add() = ExtentForRange(10, 10);
+ *aop.op.mutable_dst_extents()->Add() = ExtentForRange(30, 5);
+ *aop.op.mutable_dst_extents()->Add() = ExtentForRange(45, 5);
+ auto& xor_map = aop.xor_ops;
+ {
+ // xor_map[i] = i * kBlockSize + 123;
+ auto& op = xor_map.emplace_back();
+ *op.mutable_src_extent() = ExtentForRange(10, 5);
+ *op.mutable_dst_extent() = ExtentForRange(30, 5);
+ op.set_src_offset(123);
+ op.set_type(CowMergeOperation::COW_XOR);
+ }
+ {
+ // xor_map[i] = i * kBlockSize + 123;
+ auto& op = xor_map.emplace_back();
+ *op.mutable_src_extent() = ExtentForRange(15, 5);
+ *op.mutable_dst_extent() = ExtentForRange(45, 5);
+ op.set_src_offset(123);
+ op.set_type(CowMergeOperation::COW_XOR);
+ }
+ auto generator = MergeSequenceGenerator::Create(aops);
+ ASSERT_NE(generator, nullptr);
+ std::vector<CowMergeOperation> sequence;
+ ASSERT_TRUE(generator->Generate(&sequence));
+ ASSERT_EQ(sequence.size(), 2UL);
+ ASSERT_EQ(sequence[0].src_extent().start_block(), 10UL);
+ ASSERT_EQ(sequence[0].dst_extent().start_block(), 30UL);
+ ASSERT_EQ(sequence[0].src_extent().num_blocks(), 6UL);
+ ASSERT_EQ(sequence[0].dst_extent().num_blocks(), 5UL);
+ ASSERT_EQ(sequence[0].type(), CowMergeOperation::COW_XOR);
+ ASSERT_EQ(sequence[0].src_offset(), 123UL);
+
+ ASSERT_EQ(sequence[1].src_extent().start_block(), 15UL);
+ ASSERT_EQ(sequence[1].dst_extent().start_block(), 45UL);
+ ASSERT_EQ(sequence[1].src_extent().num_blocks(), 6UL);
+ ASSERT_EQ(sequence[1].dst_extent().num_blocks(), 5UL);
+ ASSERT_EQ(sequence[1].type(), CowMergeOperation::COW_XOR);
+ ASSERT_EQ(sequence[1].src_offset(), 123UL);
+
+ ASSERT_TRUE(generator->ValidateSequence(sequence));
+}
+
+TEST_F(MergeSequenceGeneratorTest, CreateGeneratorXorAppendBlock) {
+ std::vector<AnnotatedOperation> aops;
+ auto& aop = aops.emplace_back();
+ aop.op.set_type(InstallOperation::SOURCE_BSDIFF);
+ *aop.op.mutable_src_extents()->Add() = ExtentForRange(10, 10);
+ *aop.op.mutable_dst_extents()->Add() = ExtentForRange(20, 10);
+ auto& xor_map = aop.xor_ops;
+ {
+ auto& op = xor_map.emplace_back();
+ *op.mutable_src_extent() = ExtentForRange(10, 5);
+ *op.mutable_dst_extent() = ExtentForRange(20, 5);
+ op.set_type(CowMergeOperation::COW_XOR);
+ }
+ {
+ auto& op = xor_map.emplace_back();
+ *op.mutable_src_extent() = ExtentForRange(15, 5);
+ *op.mutable_dst_extent() = ExtentForRange(25, 5);
+ op.set_src_offset(123);
+ op.set_type(CowMergeOperation::COW_XOR);
+ }
+ auto generator = MergeSequenceGenerator::Create(aops);
+ ASSERT_NE(generator, nullptr);
+ std::vector<CowMergeOperation> sequence;
+ ASSERT_TRUE(generator->Generate(&sequence));
+ ASSERT_EQ(sequence.size(), 2UL);
+ ASSERT_EQ(sequence[0].src_extent().start_block(), 15UL);
+ ASSERT_EQ(sequence[0].dst_extent().start_block(), 25UL);
+ ASSERT_EQ(sequence[0].src_extent().num_blocks(), 6UL);
+ ASSERT_EQ(sequence[0].dst_extent().num_blocks(), 5UL);
+ ASSERT_EQ(sequence[0].type(), CowMergeOperation::COW_XOR);
+ ASSERT_EQ(sequence[0].src_offset(), 123UL);
+
+ ASSERT_EQ(sequence[1].src_extent().start_block(), 10UL);
+ ASSERT_EQ(sequence[1].dst_extent().start_block(), 20UL);
+ ASSERT_EQ(sequence[1].src_extent().num_blocks(), 5UL);
+ ASSERT_EQ(sequence[1].dst_extent().num_blocks(), 5UL);
+ ASSERT_EQ(sequence[1].type(), CowMergeOperation::COW_XOR);
+
+ ASSERT_TRUE(generator->ValidateSequence(sequence));
+}
+
+TEST_F(MergeSequenceGeneratorTest, CreateGeneratorXorAlreadyPlusOne) {
+ std::vector<AnnotatedOperation> aops;
+ auto& aop = aops.emplace_back();
+ aop.op.set_type(InstallOperation::SOURCE_BSDIFF);
+ *aop.op.mutable_src_extents()->Add() = ExtentForRange(10, 10);
+ *aop.op.mutable_dst_extents()->Add() = ExtentForRange(20, 10);
+ auto& xor_map = aop.xor_ops;
+ {
+ auto& op = xor_map.emplace_back();
+ *op.mutable_src_extent() = ExtentForRange(15, 6);
+ *op.mutable_dst_extent() = ExtentForRange(25, 5);
+ op.set_src_offset(123);
+ op.set_type(CowMergeOperation::COW_XOR);
+ }
+ auto generator = MergeSequenceGenerator::Create(aops);
+ ASSERT_NE(generator, nullptr);
+ std::vector<CowMergeOperation> sequence;
+ ASSERT_TRUE(generator->Generate(&sequence));
+ ASSERT_EQ(sequence.size(), 1UL);
+ ASSERT_EQ(sequence[0].src_extent().start_block(), 15UL);
+ ASSERT_EQ(sequence[0].dst_extent().start_block(), 25UL);
+ ASSERT_EQ(sequence[0].src_extent().num_blocks(), 6UL);
+ ASSERT_EQ(sequence[0].dst_extent().num_blocks(), 5UL);
+ ASSERT_EQ(sequence[0].type(), CowMergeOperation::COW_XOR);
+ ASSERT_EQ(sequence[0].src_offset(), 123UL);
+
+ ASSERT_TRUE(generator->ValidateSequence(sequence));
+}
+
} // namespace chromeos_update_engine
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 9c8c59f..c15ac78 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -233,6 +233,9 @@
// Path to apex_info.pb, extracted from target_file.zip
std::string apex_info_file;
+
+ // Whether to enable VABC xor op
+ bool enable_vabc_xor = false;
};
} // namespace chromeos_update_engine
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 746cefb..f36e995 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -204,6 +204,8 @@
DEFINE_string full_boot "" "Will include full boot image"
DEFINE_string disable_vabc "" \
"Optional: Disables Virtual AB Compression when installing the OTA"
+ DEFINE_string enable_vabc_xor "" \
+ "Optional: Enable the use of Virtual AB Compression XOR feature"
fi
if [[ "${COMMAND}" == "hash" || "${COMMAND}" == "sign" ]]; then
DEFINE_string unsigned_payload "" "Path to the input unsigned payload."
@@ -710,6 +712,11 @@
fi
fi
+ if [[ -n "${FLAGS_enable_vabc_xor}" ]]; then
+ GENERATOR_ARGS+=(
+ --enable_vabc_xor="${FLAGS_enable_vabc_xor}" )
+ fi
+
if [[ -n "${FLAGS_disable_vabc}" ]]; then
GENERATOR_ARGS+=(
--disable_vabc="${FLAGS_disable_vabc}" )
diff --git a/scripts/update_device.py b/scripts/update_device.py
index f672cda..165bc97 100755
--- a/scripts/update_device.py
+++ b/scripts/update_device.py
@@ -30,6 +30,7 @@
import sys
import struct
import tempfile
+import time
import threading
import xml.etree.ElementTree
import zipfile
@@ -441,6 +442,8 @@
logging.basicConfig(
level=logging.WARNING if args.no_verbose else logging.INFO)
+ start_time = time.perf_counter()
+
dut = AdbHost(args.s)
server_thread = None
@@ -548,6 +551,7 @@
for cmd in finalize_cmds:
dut.adb(cmd, 5)
+ logging.info('Update took %.3f seconds', (time.perf_counter() - start_time))
return 0
diff --git a/scripts/update_payload/payload.py b/scripts/update_payload/payload.py
index 998703a..2de8694 100644
--- a/scripts/update_payload/payload.py
+++ b/scripts/update_payload/payload.py
@@ -127,7 +127,8 @@
self.payload_file = zfp.open("payload.bin", "r")
elif isinstance(payload_file, str):
payload_fp = open(payload_file, "rb")
- payload_bytes = mmap.mmap(payload_fp.fileno(), 0, access=mmap.ACCESS_READ)
+ payload_bytes = mmap.mmap(
+ payload_fp.fileno(), 0, access=mmap.ACCESS_READ)
self.payload_file = io.BytesIO(payload_bytes)
else:
self.payload_file = payload_file
@@ -138,6 +139,7 @@
self.manifest = None
self.data_offset = None
self.metadata_signature = None
+ self.payload_signature = None
self.metadata_size = None
def _ReadHeader(self):
@@ -235,6 +237,13 @@
self.metadata_size = self.header.size + self.header.manifest_len
self.data_offset = self.metadata_size + self.header.metadata_signature_len
+ if self.manifest.signatures_offset and self.manifest.signatures_size:
+ payload_signature_blob = self.ReadDataBlob(
+ self.manifest.signatures_offset, self.manifest.signatures_size)
+ payload_signature = update_metadata_pb2.Signatures()
+ payload_signature.ParseFromString(payload_signature_blob)
+ self.payload_signature = payload_signature
+
self.is_init = True
def _AssertInit(self):
diff --git a/scripts/update_payload/update_metadata_pb2.py b/scripts/update_payload/update_metadata_pb2.py
index 9aef9f2..a8f1bff 100644
--- a/scripts/update_payload/update_metadata_pb2.py
+++ b/scripts/update_payload/update_metadata_pb2.py
@@ -20,7 +20,7 @@
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\"\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')
+ 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\"\x81\x02\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\x12\x12\n\nsrc_offset\x18\x04 \x01(\x04\"2\n\x04Type\x12\x0c\n\x08\x43OW_COPY\x10\x00\x12\x0b\n\x07\x43OW_XOR\x10\x01\x12\x0f\n\x0b\x43OW_REPLACE\x10\x02\"\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\"\xd5\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\x12\x13\n\x0b\x63ow_version\x18\x05 \x01(\r\x12\x15\n\rvabc_optional\x18\x06 \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')
)
@@ -93,11 +93,19 @@
name='COW_COPY', index=0, number=0,
serialized_options=None,
type=None),
+ _descriptor.EnumValueDescriptor(
+ name='COW_XOR', index=1, number=1,
+ serialized_options=None,
+ type=None),
+ _descriptor.EnumValueDescriptor(
+ name='COW_REPLACE', index=2, number=2,
+ serialized_options=None,
+ type=None),
],
containing_type=None,
serialized_options=None,
- serialized_start=1138,
- serialized_end=1158,
+ serialized_start=1158,
+ serialized_end=1208,
)
_sym_db.RegisterEnumDescriptor(_COWMERGEOPERATION_TYPE)
@@ -435,6 +443,13 @@
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='src_offset', full_name='chromeos_update_engine.CowMergeOperation.src_offset', index=3,
+ number=4, type=4, cpp_type=4, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
@@ -449,7 +464,7 @@
oneofs=[
],
serialized_start=951,
- serialized_end=1158,
+ serialized_end=1208,
)
@@ -605,8 +620,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=1161,
- serialized_end=2001,
+ serialized_start=1211,
+ serialized_end=2051,
)
@@ -650,8 +665,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=2003,
- serialized_end=2079,
+ serialized_start=2053,
+ serialized_end=2129,
)
@@ -690,6 +705,20 @@
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='cow_version', full_name='chromeos_update_engine.DynamicPartitionMetadata.cow_version', index=4,
+ number=5, type=13, cpp_type=3, label=1,
+ has_default_value=False, default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
+ _descriptor.FieldDescriptor(
+ name='vabc_optional', full_name='chromeos_update_engine.DynamicPartitionMetadata.vabc_optional', index=5,
+ number=6, type=8, cpp_type=7, label=1,
+ has_default_value=False, default_value=False,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
@@ -702,8 +731,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=2082,
- serialized_end=2251,
+ serialized_start=2132,
+ serialized_end=2345,
)
@@ -754,8 +783,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=2253,
- serialized_end=2352,
+ serialized_start=2347,
+ serialized_end=2446,
)
@@ -785,8 +814,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=2354,
- serialized_end=2421,
+ serialized_start=2448,
+ serialized_end=2515,
)
@@ -928,8 +957,8 @@
extension_ranges=[],
oneofs=[
],
- serialized_start=2424,
- serialized_end=3350,
+ serialized_start=2518,
+ serialized_end=3444,
)
_SIGNATURES_SIGNATURE.containing_type = _SIGNATURES
diff --git a/update_metadata.proto b/update_metadata.proto
index 93e4e2e..533f6f0 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -233,12 +233,22 @@
// read after write, similar to the inplace update schema.
message CowMergeOperation {
enum Type {
- COW_COPY = 0; // identical blocks
+ COW_COPY = 0; // identical blocks
+ COW_XOR = 1; // used when src/dst blocks are highly similar
+ COW_REPLACE = 2; // Raw replace operation
}
optional Type type = 1;
optional Extent src_extent = 2;
optional Extent dst_extent = 3;
+ // For COW_XOR, source location might be unaligned, so this field is in range
+ // [0, block_size), representing how much should the src_extent shift toward
+ // larger block number. If this field is non-zero, then src_extent will
+ // include 1 extra block in the end, as the merge op actually references the
+ // first |src_offset| bytes of that extra block. For example, if |dst_extent|
+ // is [10, 15], |src_offset| is 500, then src_extent might look like [25, 31].
+ // Note that |src_extent| contains 1 extra block than the |dst_extent|.
+ optional uint32 src_offset = 4;
}
// Describes the update to apply to a single partition.