[automerger skipped] Add ramdisk_available to init_first_stage's deps am: 7729c21a47 -s ours
am skip reason: Merged-In I0a9ba90f0ae6f0b551e73d0a4361b8cf5d2c1e85 with SHA-1 3cce62a6b4 is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/update_engine/+/15067039
Change-Id: Ib43aad1e226d0842c11192e3229101070356027a
diff --git a/Android.bp b/Android.bp
index d89255d..c519280 100644
--- a/Android.bp
+++ b/Android.bp
@@ -233,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",
@@ -243,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",
@@ -806,6 +808,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/cleanup_previous_update_action.cc b/aosp/cleanup_previous_update_action.cc
index dde6b89..51bb083 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..bcae3aa 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -152,6 +152,17 @@
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;
+}
+
void UpdateAttempterAndroid::Init() {
// In case of update_engine restart without a reboot we need to restore the
// reboot needed state.
@@ -159,7 +170,10 @@
SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
} else {
SetStatusAndNotify(UpdateStatus::IDLE);
- UpdatePrefsAndReportUpdateMetricsOnReboot();
+ if (DidSystemReboot(prefs_)) {
+ UpdateStateAfterReboot();
+ }
+
#ifdef _UE_SIDELOAD
LOG(INFO) << "Skip ScheduleCleanupPreviousUpdate in sideload because "
<< "ApplyPayload will call it later.";
@@ -883,22 +897,24 @@
}
}
-void UpdateAttempterAndroid::UpdatePrefsAndReportUpdateMetricsOnReboot() {
- string current_boot_id;
- TEST_AND_RETURN(utils::GetBootId(¤t_boot_id));
+void UpdateAttempterAndroid::UpdateStateAfterReboot() {
// 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();
+ // |InitAfterReboot()| 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;
}
@@ -909,14 +925,9 @@
// 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);
// 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_);
@@ -926,10 +937,8 @@
// 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.
diff --git a/aosp/update_attempter_android.h b/aosp/update_attempter_android.h
index 70938bc..7a5a635 100644
--- a/aosp/update_attempter_android.h
+++ b/aosp/update_attempter_android.h
@@ -162,12 +162,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();
// Prefs to update:
// |kPrefsPayloadAttemptNumber|, |kPrefsUpdateTimestampStart|,
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_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..7c07f1d 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())
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index c54316b..1d95e1e 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.
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..27f846e 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) {
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..c90b28d
--- /dev/null
+++ b/payload_consumer/install_operation_executor_unittest.cc
@@ -0,0 +1,221 @@
+//
+// 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 {
+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 (cur_block_count + extent.num_blocks() >= n) {
+ return extent.start_block() + (n - cur_block_count);
+ }
+ cur_block_count += extent.num_blocks();
+ }
+ return std::numeric_limits<size_t>::max();
+}
+
+} // 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..9db7ae0 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,12 @@
#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_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 +85,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 +112,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 +121,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 +138,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 +194,84 @@
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;
}
+std::ostream& operator<<(std::ostream& out,
+ const RepeatedPtrField<Extent>& extents) {
+ if (extents.size() == 0) {
+ out << "[]";
+ return out;
+ }
+ out << "[";
+ auto begin = extents.begin();
+ out << *begin;
+ for (int i = 1; i < extents.size(); i++) {
+ ++begin;
+ out << ", " << *begin;
+ }
+ out << "]";
+ return out;
+}
+
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: " << 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 +282,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 +296,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 +321,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 +332,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..14bd18a 100644
--- a/payload_consumer/partition_writer.h
+++ b/payload_consumer/partition_writer.h
@@ -27,10 +27,13 @@
#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/verified_source_fd.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
+
class PartitionWriter {
public:
PartitionWriter(const PartitionUpdate& partition_update,
@@ -92,7 +95,8 @@
friend class PartitionWriterTest;
FRIEND_TEST(PartitionWriterTest, ChooseSourceFDTest);
- bool OpenSourcePartition(uint32_t source_slot, bool source_may_exist);
+ [[nodiscard]] 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
@@ -108,27 +112,17 @@
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 {
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/vabc_partition_writer.cc b/payload_consumer/vabc_partition_writer.cc
index 0843fff..a27847c 100644
--- a/payload_consumer/vabc_partition_writer.cc
+++ b/payload_consumer/vabc_partition_writer.cc
@@ -90,7 +90,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;
}
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/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..e9afa98 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.