aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Zheng <zhengdaniel@google.com>2024-01-30 13:40:02 -0800
committerDaniel Zheng <zhengdaniel@google.com>2024-02-01 11:15:06 -0800
commit40354f8c566311753d2c3318b2f0164856919683 (patch)
tree05a4fab04c9eaeac79e0b3952a5bef59ad378c33
parent0dc25a67793c18f53bcd2e7787162be4e17898db (diff)
downloadupdate_engine-40354f8c566311753d2c3318b2f0164856919683.tar.gz
update_engine: refactor manifest parsing
DeltaPerformer write is quite long and complex. Let's move manifest parsing to it's own method for readability. Test: th Change-Id: Id8ce50c3bd62959b1b70a4f05eef4718f3d30867
-rw-r--r--payload_consumer/delta_performer.cc311
-rw-r--r--payload_consumer/delta_performer.h5
2 files changed, 167 insertions, 149 deletions
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index fe263c2a..fe581b6a 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -451,158 +451,14 @@ bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode* error) {
UpdateOverallProgress(false, "Completed ");
while (!manifest_valid_) {
- // Read data up to the needed limit; this is either maximium payload header
- // size, or the full metadata size (once it becomes known).
- const bool do_read_header = !IsHeaderParsed();
- CopyDataToBuffer(
- &c_bytes,
- &count,
- (do_read_header ? kMaxPayloadHeaderSize
- : metadata_size_ + metadata_signature_size_));
-
- MetadataParseResult result = ParsePayloadMetadata(buffer_, error);
- if (result == MetadataParseResult::kError)
+ bool insufficient_bytes = false;
+ if (!ParseManifest(&c_bytes, &count, error, &insufficient_bytes)) {
+ LOG(ERROR) << "Failed to parse manifest";
return false;
- if (result == MetadataParseResult::kInsufficientData) {
- // If we just processed the header, make an attempt on the manifest.
- if (do_read_header && IsHeaderParsed())
- continue;
-
- return true;
- }
-
- // Checks the integrity of the payload manifest.
- if ((*error = ValidateManifest()) != ErrorCode::kSuccess)
- return false;
- manifest_valid_ = true;
- if (!install_plan_->is_resume) {
- auto begin = reinterpret_cast<const char*>(buffer_.data());
- prefs_->SetString(kPrefsManifestBytes, {begin, buffer_.size()});
- }
-
- // Clear the download buffer.
- DiscardBuffer(false, metadata_size_);
-
- block_size_ = manifest_.block_size();
-
- if (!install_plan_->spl_downgrade && !CheckSPLDowngrade()) {
- *error = ErrorCode::kPayloadTimestampError;
- return false;
- }
-
- // update estimate_cow_size if VABC is disabled
- // new_cow_size per partition = partition_size - (#blocks in Copy
- // operations part of the partition)
- if (install_plan_->vabc_none) {
- LOG(INFO) << "Setting Virtual AB Compression algorithm to none. This "
- "would also disable VABC XOR as XOR only saves space if "
- "compression is enabled.";
- manifest_.mutable_dynamic_partition_metadata()
- ->set_vabc_compression_param("none");
- for (auto& partition : *manifest_.mutable_partitions()) {
- if (!partition.has_estimate_cow_size()) {
- continue;
- }
- auto new_cow_size = partition.new_partition_info().size();
- for (const auto& operation : partition.merge_operations()) {
- if (operation.type() == CowMergeOperation::COW_COPY) {
- new_cow_size -=
- operation.dst_extent().num_blocks() * manifest_.block_size();
- }
- }
- // Remove all COW_XOR merge ops, as XOR without compression is useless.
- // It increases CPU usage but does not reduce space usage at all.
- auto&& merge_ops = *partition.mutable_merge_operations();
- merge_ops.erase(std::remove_if(merge_ops.begin(),
- merge_ops.end(),
- [](const auto& op) {
- return op.type() ==
- CowMergeOperation::COW_XOR;
- }),
- merge_ops.end());
-
- // Every block written to COW device will come with a header which
- // stores src/dst block info along with other data.
- const auto cow_metadata_size = partition.new_partition_info().size() /
- manifest_.block_size() *
- sizeof(android::snapshot::CowOperation);
- // update_engine will emit a label op every op or every two seconds,
- // whichever one is longer. In the worst case, we add 1 label per
- // InstallOp. So take size of label ops into account.
- const auto label_ops_size = partition.operations_size() *
- sizeof(android::snapshot::CowOperation);
- // Adding extra 2MB headroom just for any unexpected space usage.
- // If we overrun reserved COW size, entire OTA will fail
- // and no way for user to retry OTA
- partition.set_estimate_cow_size(new_cow_size + (1024 * 1024 * 2) +
- cow_metadata_size + label_ops_size);
- // Setting op count max to 0 will defer to num_blocks as the op buffer
- // size.
- partition.set_estimate_op_count_max(0);
- LOG(INFO) << "New COW size for partition " << partition.partition_name()
- << " is " << partition.estimate_cow_size();
- }
- }
- if (install_plan_->disable_vabc) {
- manifest_.mutable_dynamic_partition_metadata()->set_vabc_enabled(false);
- }
- if (install_plan_->enable_threading) {
- manifest_.mutable_dynamic_partition_metadata()
- ->mutable_vabc_feature_set()
- ->set_threaded(install_plan_->enable_threading.value());
- LOG(INFO) << "Attempting to "
- << (install_plan_->enable_threading.value() ? "enable"
- : "disable")
- << " multi-threaded compression for VABC";
}
- if (install_plan_->batched_writes) {
- manifest_.mutable_dynamic_partition_metadata()
- ->mutable_vabc_feature_set()
- ->set_batch_writes(true);
- LOG(INFO) << "Attempting to enable batched writes for VABC";
- }
-
- // This populates |partitions_| and the |install_plan.partitions| with the
- // list of partitions from the manifest.
- if (!ParseManifestPartitions(error))
- return false;
-
- // |install_plan.partitions| was filled in, nothing need to be done here if
- // the payload was already applied, returns false to terminate http fetcher,
- // but keep |error| as ErrorCode::kSuccess.
- if (payload_->already_applied)
- return false;
-
- num_total_operations_ = 0;
- for (const auto& partition : partitions_) {
- num_total_operations_ += partition.operations_size();
- acc_num_operations_.push_back(num_total_operations_);
- }
-
- LOG_IF(WARNING,
- !prefs_->SetInt64(kPrefsManifestMetadataSize, metadata_size_))
- << "Unable to save the manifest metadata size.";
- LOG_IF(WARNING,
- !prefs_->SetInt64(kPrefsManifestSignatureSize,
- metadata_signature_size_))
- << "Unable to save the manifest signature size.";
-
- if (!PrimeUpdateState()) {
- *error = ErrorCode::kDownloadStateInitializationError;
- LOG(ERROR) << "Unable to prime the update state.";
- return false;
- }
-
- if (next_operation_num_ < acc_num_operations_[current_partition_]) {
- if (!OpenCurrentPartition()) {
- *error = ErrorCode::kInstallDeviceOpenError;
- return false;
- }
+ if (insufficient_bytes) {
+ return true;
}
-
- if (next_operation_num_ > 0)
- UpdateOverallProgress(true, "Resuming after ");
- LOG(INFO) << "Starting to apply update payload operations";
}
while (next_operation_num_ < num_total_operations_) {
@@ -693,6 +549,163 @@ bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode* error) {
return true;
}
+bool DeltaPerformer::ParseManifest(const char** c_bytes,
+ size_t* count,
+ ErrorCode* error,
+ bool* should_return) {
+ // Read data up to the needed limit; this is either maximium payload header
+ // size, or the full metadata size (once it becomes known).
+ const bool do_read_header = !IsHeaderParsed();
+ CopyDataToBuffer(
+ c_bytes,
+ count,
+ (do_read_header ? kMaxPayloadHeaderSize
+ : metadata_size_ + metadata_signature_size_));
+ MetadataParseResult result = ParsePayloadMetadata(buffer_, error);
+ if (result == MetadataParseResult::kError)
+ return false;
+ if (result == MetadataParseResult::kInsufficientData) {
+ // If we just processed the header, make an attempt on the manifest.
+ if (do_read_header && IsHeaderParsed()) {
+ return true;
+ }
+ *should_return = true;
+ return true;
+ }
+
+ // Checks the integrity of the payload manifest.
+ if ((*error = ValidateManifest()) != ErrorCode::kSuccess)
+ return false;
+ manifest_valid_ = true;
+ if (!install_plan_->is_resume) {
+ auto begin = reinterpret_cast<const char*>(buffer_.data());
+ prefs_->SetString(kPrefsManifestBytes, {begin, buffer_.size()});
+ }
+
+ // Clear the download buffer.
+ DiscardBuffer(false, metadata_size_);
+
+ block_size_ = manifest_.block_size();
+
+ if (!install_plan_->spl_downgrade && !CheckSPLDowngrade()) {
+ *error = ErrorCode::kPayloadTimestampError;
+ return false;
+ }
+
+ // update estimate_cow_size if VABC is disabled
+ // new_cow_size per partition = partition_size - (#blocks in Copy
+ // operations part of the partition)
+ if (install_plan_->vabc_none) {
+ LOG(INFO) << "Setting Virtual AB Compression algorithm to none. This "
+ "would also disable VABC XOR as XOR only saves space if "
+ "compression is enabled.";
+ manifest_.mutable_dynamic_partition_metadata()->set_vabc_compression_param(
+ "none");
+ for (auto& partition : *manifest_.mutable_partitions()) {
+ if (!partition.has_estimate_cow_size()) {
+ continue;
+ }
+ auto new_cow_size = partition.new_partition_info().size();
+ for (const auto& operation : partition.merge_operations()) {
+ if (operation.type() == CowMergeOperation::COW_COPY) {
+ new_cow_size -=
+ operation.dst_extent().num_blocks() * manifest_.block_size();
+ }
+ }
+ // Remove all COW_XOR merge ops, as XOR without compression is useless.
+ // It increases CPU usage but does not reduce space usage at all.
+ auto&& merge_ops = *partition.mutable_merge_operations();
+ merge_ops.erase(std::remove_if(merge_ops.begin(),
+ merge_ops.end(),
+ [](const auto& op) {
+ return op.type() ==
+ CowMergeOperation::COW_XOR;
+ }),
+ merge_ops.end());
+
+ // Every block written to COW device will come with a header which
+ // stores src/dst block info along with other data.
+ const auto cow_metadata_size = partition.new_partition_info().size() /
+ manifest_.block_size() *
+ sizeof(android::snapshot::CowOperation);
+ // update_engine will emit a label op every op or every two seconds,
+ // whichever one is longer. In the worst case, we add 1 label per
+ // InstallOp. So take size of label ops into account.
+ const auto label_ops_size =
+ partition.operations_size() * sizeof(android::snapshot::CowOperation);
+ // Adding extra 2MB headroom just for any unexpected space usage.
+ // If we overrun reserved COW size, entire OTA will fail
+ // and no way for user to retry OTA
+ partition.set_estimate_cow_size(new_cow_size + (1024 * 1024 * 2) +
+ cow_metadata_size + label_ops_size);
+ // Setting op count max to 0 will defer to num_blocks as the op buffer
+ // size.
+ partition.set_estimate_op_count_max(0);
+ LOG(INFO) << "New COW size for partition " << partition.partition_name()
+ << " is " << partition.estimate_cow_size();
+ }
+ }
+ if (install_plan_->disable_vabc) {
+ manifest_.mutable_dynamic_partition_metadata()->set_vabc_enabled(false);
+ }
+ if (install_plan_->enable_threading) {
+ manifest_.mutable_dynamic_partition_metadata()
+ ->mutable_vabc_feature_set()
+ ->set_threaded(install_plan_->enable_threading.value());
+ LOG(INFO) << "Attempting to "
+ << (install_plan_->enable_threading.value() ? "enable"
+ : "disable")
+ << " multi-threaded compression for VABC";
+ }
+ if (install_plan_->batched_writes) {
+ manifest_.mutable_dynamic_partition_metadata()
+ ->mutable_vabc_feature_set()
+ ->set_batch_writes(true);
+ LOG(INFO) << "Attempting to enable batched writes for VABC";
+ }
+
+ // This populates |partitions_| and the |install_plan.partitions| with the
+ // list of partitions from the manifest.
+ if (!ParseManifestPartitions(error))
+ return false;
+
+ // |install_plan.partitions| was filled in, nothing need to be done here if
+ // the payload was already applied, returns false to terminate http fetcher,
+ // but keep |error| as ErrorCode::kSuccess.
+ if (payload_->already_applied)
+ return false;
+
+ num_total_operations_ = 0;
+ for (const auto& partition : partitions_) {
+ num_total_operations_ += partition.operations_size();
+ acc_num_operations_.push_back(num_total_operations_);
+ }
+
+ LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestMetadataSize, metadata_size_))
+ << "Unable to save the manifest metadata size.";
+ LOG_IF(
+ WARNING,
+ !prefs_->SetInt64(kPrefsManifestSignatureSize, metadata_signature_size_))
+ << "Unable to save the manifest signature size.";
+
+ if (!PrimeUpdateState()) {
+ *error = ErrorCode::kDownloadStateInitializationError;
+ LOG(ERROR) << "Unable to prime the update state.";
+ return false;
+ }
+
+ if (next_operation_num_ < acc_num_operations_[current_partition_]) {
+ if (!OpenCurrentPartition()) {
+ *error = ErrorCode::kInstallDeviceOpenError;
+ return false;
+ }
+ }
+
+ if (next_operation_num_ > 0)
+ UpdateOverallProgress(true, "Resuming after ");
+ LOG(INFO) << "Starting to apply update payload operations";
+ return true;
+}
bool DeltaPerformer::ProcessOperation(const InstallOperation* op,
ErrorCode* error) {
// Validate the operation unconditionally. This helps prevent the
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 1584dc63..76d64c56 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -246,6 +246,11 @@ class DeltaPerformer : public FileWriter {
// to be able to perform a given install operation.
bool CanPerformInstallOperation(const InstallOperation& operation);
+ bool ParseManifest(const char** c_bytes,
+ size_t* count,
+ ErrorCode* error,
+ bool* should_return);
+
// Process one InstallOperation
bool ProcessOperation(const InstallOperation* op, ErrorCode* error);
// Checks the integrity of the payload manifest. Returns true upon success,