Parse multiple packages from Omaha response.
The multi-payload info are stored in OmahaResponse and InstallPlan, but
we still can only apply the first payload for now.
Bug: 36252799
Test: mma -j
Test: update_engine_unittests
Change-Id: I5ca63944ae9082670d0e67888409374f140d4245
(cherry picked from commit 2aba8a87d4fac245a2e2d238b3159f8eabce630f)
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 21299d7..b14a54f 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -187,7 +187,7 @@
}
// Format download total count and percentage.
- size_t payload_size = install_plan_->payload_size;
+ size_t payload_size = payload_->size;
string payload_size_str("?");
string downloaded_percentage_str("");
if (payload_size) {
@@ -222,7 +222,7 @@
// eliminated once we ensure that the payload_size in the install plan is
// always given and is non-zero. This currently isn't the case during unit
// tests (see chromium-os:37969).
- size_t payload_size = install_plan_->payload_size;
+ size_t payload_size = payload_->size;
unsigned actual_operations_weight = kProgressOperationsWeight;
if (payload_size)
new_overall_progress += min(
@@ -518,9 +518,9 @@
// beyond the expected metadata size.
metadata_size_ = manifest_offset + manifest_size_;
if (install_plan_->hash_checks_mandatory) {
- if (install_plan_->metadata_size != metadata_size_) {
+ if (payload_->metadata_size != metadata_size_) {
LOG(ERROR) << "Mandatory metadata size in Omaha response ("
- << install_plan_->metadata_size
+ << payload_->metadata_size
<< ") is missing/incorrect, actual = " << metadata_size_;
*error = ErrorCode::kDownloadInvalidMetadataSize;
return kMetadataParseError;
@@ -537,13 +537,13 @@
// here. This is logged here (after we received the full metadata data) so
// that we just log once (instead of logging n times) if it takes n
// DeltaPerformer::Write calls to download the full manifest.
- if (install_plan_->metadata_size == metadata_size_) {
+ if (payload_->metadata_size == metadata_size_) {
LOG(INFO) << "Manifest size in payload matches expected value from Omaha";
} else {
// For mandatory-cases, we'd have already returned a kMetadataParseError
// above. We'll be here only for non-mandatory cases. Just send a UMA stat.
LOG(WARNING) << "Ignoring missing/incorrect metadata size ("
- << install_plan_->metadata_size
+ << payload_->metadata_size
<< ") in Omaha response as validation is not mandatory. "
<< "Trusting metadata size in payload = " << metadata_size_;
}
@@ -687,7 +687,7 @@
// NOTE: If hash checks are mandatory and if metadata_signature is empty,
// we would have already failed in ParsePayloadMetadata method and thus not
// even be here. So no need to handle that case again here.
- if (!install_plan_->metadata_signature.empty()) {
+ if (!payload_->metadata_signature.empty()) {
// Note: Validate must be called only if CanPerformInstallOperation is
// called. Otherwise, we might be failing operations before even if there
// isn't sufficient data to compute the proper hash.
@@ -1323,18 +1323,18 @@
return ErrorCode::kDownloadMetadataSignatureError;
brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
- if (!install_plan_->metadata_signature.empty()) {
+ if (!payload_->metadata_signature.empty()) {
// Convert base64-encoded signature to raw bytes.
- if (!brillo::data_encoding::Base64Decode(
- install_plan_->metadata_signature, &metadata_signature_blob)) {
+ if (!brillo::data_encoding::Base64Decode(payload_->metadata_signature,
+ &metadata_signature_blob)) {
LOG(ERROR) << "Unable to decode base64 metadata signature: "
- << install_plan_->metadata_signature;
+ << payload_->metadata_signature;
return ErrorCode::kDownloadMetadataSignatureError;
}
} else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
- metadata_signature_protobuf_blob.assign(payload.begin() + metadata_size_,
- payload.begin() + metadata_size_ +
- metadata_signature_size_);
+ metadata_signature_protobuf_blob.assign(
+ payload.begin() + metadata_size_,
+ payload.begin() + metadata_size_ + metadata_signature_size_);
}
if (metadata_signature_blob.empty() &&
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 7fe2cd2..f363a4c 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -78,12 +78,14 @@
BootControlInterface* boot_control,
HardwareInterface* hardware,
DownloadActionDelegate* download_delegate,
- InstallPlan* install_plan)
+ InstallPlan* install_plan,
+ InstallPlan::Payload* payload)
: prefs_(prefs),
boot_control_(boot_control),
hardware_(hardware),
download_delegate_(download_delegate),
- install_plan_(install_plan) {}
+ install_plan_(install_plan),
+ payload_(payload) {}
// FileWriter's Write implementation where caller doesn't care about
// error codes.
@@ -303,6 +305,9 @@
// Install Plan based on Omaha Response.
InstallPlan* install_plan_;
+ // Pointer to the current payload in install_plan_.payloads.
+ InstallPlan::Payload* payload_{nullptr};
+
// File descriptor of the source partition. Only set while updating a
// partition when using a delta payload.
FileDescriptorPtr source_fd_{nullptr};
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index e87a907..80d4dc3 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -719,8 +719,9 @@
// Update the A image in place.
InstallPlan* install_plan = &state->install_plan;
+ install_plan->payloads.resize(1);
install_plan->hash_checks_mandatory = hash_checks_mandatory;
- install_plan->metadata_size = state->metadata_size;
+ install_plan->payloads[0].metadata_size = state->metadata_size;
install_plan->payload_type = (full_kernel && full_rootfs)
? InstallPayloadType::kFull
: InstallPayloadType::kDelta;
@@ -739,14 +740,15 @@
state->delta.data(),
state->metadata_size,
GetBuildArtifactsPath(kUnittestPrivateKeyPath),
- &install_plan->metadata_signature));
- EXPECT_FALSE(install_plan->metadata_signature.empty());
+ &install_plan->payloads[0].metadata_signature));
+ EXPECT_FALSE(install_plan->payloads[0].metadata_signature.empty());
*performer = new DeltaPerformer(&prefs,
&state->fake_boot_control_,
&state->fake_hardware_,
&state->mock_delegate_,
- install_plan);
+ install_plan,
+ &install_plan->payloads[0]);
string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
(*performer)->set_public_key_path(public_key_path);
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index 18481a7..3af13ec 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -164,7 +164,7 @@
string private_key =
sign_payload ? GetBuildArtifactsPath(kUnittestPrivateKeyPath) : "";
EXPECT_TRUE(payload.WritePayload(
- payload_path, blob_path, private_key, &install_plan_.metadata_size));
+ payload_path, blob_path, private_key, &payload_.metadata_size));
brillo::Blob payload_data;
EXPECT_TRUE(utils::ReadFile(payload_path, &payload_data));
@@ -231,7 +231,7 @@
uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
EXPECT_TRUE(performer_.Write(&version, 8));
- install_plan_.metadata_size = expected_metadata_size;
+ payload_.metadata_size = expected_metadata_size;
ErrorCode error_code;
// When filling in size in manifest, exclude the size of the 20-byte header.
uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
@@ -268,13 +268,13 @@
// Fill up the metadata signature in install plan according to the test.
switch (metadata_signature_test) {
case kEmptyMetadataSignature:
- install_plan_.metadata_signature.clear();
+ payload_.metadata_signature.clear();
expected_result = DeltaPerformer::kMetadataParseError;
expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
break;
case kInvalidMetadataSignature:
- install_plan_.metadata_signature = kBogusMetadataSignature1;
+ payload_.metadata_signature = kBogusMetadataSignature1;
expected_result = DeltaPerformer::kMetadataParseError;
expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
break;
@@ -286,10 +286,10 @@
// then we can get to manifest signature checks.
ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
payload.data(),
- install_plan_.metadata_size,
+ payload_.metadata_size,
GetBuildArtifactsPath(kUnittestPrivateKeyPath),
- &install_plan_.metadata_signature));
- EXPECT_FALSE(install_plan_.metadata_signature.empty());
+ &payload_.metadata_signature));
+ EXPECT_FALSE(payload_.metadata_signature.empty());
expected_result = DeltaPerformer::kMetadataParseSuccess;
expected_error = ErrorCode::kSuccess;
break;
@@ -317,7 +317,7 @@
// Check that the parsed metadata size is what's expected. This test
// implicitly confirms that the metadata signature is valid, if required.
- EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize());
+ EXPECT_EQ(payload_.metadata_size, performer_.GetMetadataSize());
}
void SetSupportedMajorVersion(uint64_t major_version) {
@@ -325,11 +325,16 @@
}
FakePrefs prefs_;
InstallPlan install_plan_;
+ InstallPlan::Payload payload_;
FakeBootControl fake_boot_control_;
FakeHardware fake_hardware_;
MockDownloadActionDelegate mock_delegate_;
- DeltaPerformer performer_{
- &prefs_, &fake_boot_control_, &fake_hardware_, &mock_delegate_, &install_plan_};
+ DeltaPerformer performer_{&prefs_,
+ &fake_boot_control_,
+ &fake_hardware_,
+ &mock_delegate_,
+ &install_plan_,
+ &payload_};
};
TEST_F(DeltaPerformerTest, FullPayloadWriteTest) {
@@ -666,7 +671,7 @@
install_plan_.hash_checks_mandatory = true;
// Just set these value so that we can use ValidateMetadataSignature directly.
performer_.major_payload_version_ = kBrilloMajorPayloadVersion;
- performer_.metadata_size_ = install_plan_.metadata_size;
+ performer_.metadata_size_ = payload_.metadata_size;
uint64_t signature_length;
EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
{GetBuildArtifactsPath(kUnittestPrivateKeyPath)}, &signature_length));
diff --git a/payload_consumer/download_action.cc b/payload_consumer/download_action.cc
index 65ae1ab..a8c987e 100644
--- a/payload_consumer/download_action.cc
+++ b/payload_consumer/download_action.cc
@@ -83,7 +83,7 @@
bool DownloadAction::SetupP2PSharingFd() {
P2PManager *p2p_manager = system_state_->p2p_manager();
- if (!p2p_manager->FileShare(p2p_file_id_, install_plan_.payload_size)) {
+ if (!p2p_manager->FileShare(p2p_file_id_, payload_->size)) {
LOG(ERROR) << "Unable to share file via p2p";
CloseP2PSharingFd(true); // delete p2p file
return false;
@@ -174,6 +174,9 @@
bytes_received_ = 0;
install_plan_.Dump();
+ // TODO(senj): check that install plan has at least one payload.
+ if (!payload_)
+ payload_ = &install_plan_.payloads[0];
LOG(INFO) << "Marking new slot as unbootable";
if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) {
@@ -186,15 +189,14 @@
LOG(INFO) << "Using writer for test.";
} else {
delta_performer_.reset(new DeltaPerformer(
- prefs_, boot_control_, hardware_, delegate_, &install_plan_));
+ prefs_, boot_control_, hardware_, delegate_, &install_plan_, payload_));
writer_ = delta_performer_.get();
}
download_active_ = true;
if (system_state_ != nullptr) {
const PayloadStateInterface* payload_state = system_state_->payload_state();
- string file_id = utils::CalculateP2PFileId(install_plan_.payload_hash,
- install_plan_.payload_size);
+ string file_id = utils::CalculateP2PFileId(payload_->hash, payload_->size);
if (payload_state->GetUsingP2PForSharing()) {
// If we're sharing the update, store the file_id to convey
// that we should write to the file.
@@ -266,8 +268,7 @@
bytes_received_ += length;
if (delegate_ && download_active_) {
- delegate_->BytesReceived(
- length, bytes_received_, install_plan_.payload_size);
+ delegate_->BytesReceived(length, bytes_received_, payload_->size);
}
if (writer_ && !writer_->Write(bytes, length, &code_)) {
LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
@@ -302,8 +303,7 @@
ErrorCode code =
successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError;
if (code == ErrorCode::kSuccess && delta_performer_.get()) {
- code = delta_performer_->VerifyPayload(install_plan_.payload_hash,
- install_plan_.payload_size);
+ code = delta_performer_->VerifyPayload(payload_->hash, payload_->size);
if (code != ErrorCode::kSuccess) {
LOG(ERROR) << "Download of " << install_plan_.download_url
<< " failed due to payload verification error.";
diff --git a/payload_consumer/download_action.h b/payload_consumer/download_action.h
index 285930a..0bd0d88 100644
--- a/payload_consumer/download_action.h
+++ b/payload_consumer/download_action.h
@@ -134,6 +134,9 @@
// The InstallPlan passed in
InstallPlan install_plan_;
+ // Pointer to the current payload in install_plan_.payloads.
+ InstallPlan::Payload* payload_{nullptr};
+
// SystemState required pointers.
PrefsInterface* prefs_;
BootControlInterface* boot_control_;
diff --git a/payload_consumer/download_action_unittest.cc b/payload_consumer/download_action_unittest.cc
index 4392b74..57910cc 100644
--- a/payload_consumer/download_action_unittest.cc
+++ b/payload_consumer/download_action_unittest.cc
@@ -142,10 +142,10 @@
uint64_t size = data.size();
InstallPlan install_plan;
install_plan.payload_type = InstallPayloadType::kDelta;
- install_plan.payload_size = size;
+ install_plan.payloads.push_back({.size = size});
// We pull off the first byte from data and seek past it.
EXPECT_TRUE(HashCalculator::RawHashOfBytes(
- &data[1], data.size() - 1, &install_plan.payload_hash));
+ &data[1], data.size() - 1, &install_plan.payloads[0].hash));
install_plan.source_slot = 0;
install_plan.target_slot = 1;
// We mark both slots as bootable. Only the target slot should be unbootable
@@ -272,6 +272,7 @@
// takes ownership of passed in HttpFetcher
ObjectFeederAction<InstallPlan> feeder_action;
InstallPlan install_plan;
+ install_plan.payloads.resize(1);
feeder_action.set_obj(install_plan);
FakeSystemState fake_system_state_;
MockPrefs prefs;
@@ -370,8 +371,9 @@
// takes ownership of passed in HttpFetcher
InstallPlan install_plan;
- install_plan.payload_size = 1;
- EXPECT_TRUE(HashCalculator::RawHashOfData({'x'}, &install_plan.payload_hash));
+ install_plan.payloads.push_back({.size = 1});
+ EXPECT_TRUE(
+ HashCalculator::RawHashOfData({'x'}, &install_plan.payloads[0].hash));
ObjectFeederAction<InstallPlan> feeder_action;
feeder_action.set_obj(install_plan);
MockPrefs prefs;
@@ -455,8 +457,9 @@
EXPECT_EQ(
0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
InstallPlan install_plan;
- install_plan.payload_size = data_.length();
- install_plan.payload_hash = {'1', '2', '3', '4', 'h', 'a', 's', 'h'};
+ install_plan.payloads.push_back(
+ {.size = data_.length(),
+ .hash = {'1', '2', '3', '4', 'h', 'a', 's', 'h'}});
ObjectFeederAction<InstallPlan> feeder_action;
feeder_action.set_obj(install_plan);
MockPrefs prefs;
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index fff0ac2..5f004bf 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -43,14 +43,9 @@
bool InstallPlan::operator==(const InstallPlan& that) const {
return ((is_resume == that.is_resume) &&
(payload_type == that.payload_type) &&
- (download_url == that.download_url) &&
- (payload_size == that.payload_size) &&
- (payload_hash == that.payload_hash) &&
- (metadata_size == that.metadata_size) &&
- (metadata_signature == that.metadata_signature) &&
+ (download_url == that.download_url) && (payloads == that.payloads) &&
(source_slot == that.source_slot) &&
- (target_slot == that.target_slot) &&
- (partitions == that.partitions));
+ (target_slot == that.target_slot) && (partitions == that.partitions));
}
bool InstallPlan::operator!=(const InstallPlan& that) const {
@@ -68,16 +63,22 @@
partition.target_size,
utils::ToString(partition.run_postinstall).c_str());
}
+ string payloads_str;
+ for (const auto& payload : payloads) {
+ payloads_str += base::StringPrintf(
+ ", payload: (size: %" PRIu64 ", metadata_size: %" PRIu64
+ ", metadata signature: %s, hash: %s)",
+ payload.size,
+ payload.metadata_size,
+ payload.metadata_signature.c_str(),
+ base::HexEncode(payload.hash.data(), payload.hash.size()).c_str());
+ }
LOG(INFO) << "InstallPlan: " << (is_resume ? "resume" : "new_update")
<< ", payload type: " << InstallPayloadTypeToString(payload_type)
<< ", source_slot: " << BootControlInterface::SlotName(source_slot)
<< ", target_slot: " << BootControlInterface::SlotName(target_slot)
- << ", url: " << download_url << ", payload size: " << payload_size
- << ", payload hash: "
- << base::HexEncode(payload_hash.data(), payload_hash.size())
- << ", metadata size: " << metadata_size
- << ", metadata signature: " << metadata_signature << partitions_str
+ << ", url: " << download_url << payloads_str << partitions_str
<< ", hash_checks_mandatory: "
<< utils::ToString(hash_checks_mandatory)
<< ", powerwash_required: " << utils::ToString(powerwash_required);
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 0e25cc3..db471da 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -56,10 +56,18 @@
std::string download_url; // url to download from
std::string version; // version we are installing.
- uint64_t payload_size{0}; // size of the payload
- brillo::Blob payload_hash; // SHA256 hash of the payload
- uint64_t metadata_size{0}; // size of the metadata
- std::string metadata_signature; // signature of the metadata
+ struct Payload {
+ uint64_t size = 0; // size of the payload
+ uint64_t metadata_size = 0; // size of the metadata
+ std::string metadata_signature; // signature of the metadata in base64
+ brillo::Blob hash; // SHA256 hash of the payload
+
+ bool operator==(const Payload& that) const {
+ return size == that.size && metadata_size == that.metadata_size &&
+ metadata_signature == that.metadata_signature && hash == that.hash;
+ }
+ };
+ std::vector<Payload> payloads;
// The partition slots used for the update.
BootControlInterface::Slot source_slot{BootControlInterface::kInvalidSlot};