diff options
author | Daniel Zheng <zhengdaniel@google.com> | 2024-01-30 13:40:02 -0800 |
---|---|---|
committer | Daniel Zheng <zhengdaniel@google.com> | 2024-02-01 11:15:06 -0800 |
commit | 40354f8c566311753d2c3318b2f0164856919683 (patch) | |
tree | 05a4fab04c9eaeac79e0b3952a5bef59ad378c33 | |
parent | 0dc25a67793c18f53bcd2e7787162be4e17898db (diff) | |
download | update_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.cc | 311 | ||||
-rw-r--r-- | payload_consumer/delta_performer.h | 5 |
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, |