diff options
| author | Amin Hassani <ahassani@google.com> | 2017-07-26 11:26:05 -0700 |
|---|---|---|
| committer | chrome-bot <chrome-bot@chromium.org> | 2017-08-01 13:01:42 -0700 |
| commit | 4b9775a26cd5ff26db61b7762667d607458082f0 (patch) | |
| tree | da1b37456dd35c2558ba846b762bd3aeb7eff966 | |
| parent | 73b18b83ebae881cefa6fb2b0563a0b94c17bcde (diff) | |
update_engine: Add "verify" support to brillo_update_payload
This change adds a new command "verify" to brillo_update_payload to verify the
process of update by applying a delta or full payload to temporary target
partitions and comparing them with the original target partitions. This is
specially usefull when manually debuggin/testing delta performer operations.
BUG=none
TEST=brillo_update_payload verify --payload=payload.delta --source_image=link_8872.49.bin --target_image=link_9000.82.bin
Change-Id: I4b30bc8a1088f4f72b681c6095cca6863a715078
Reviewed-on: https://chromium-review.googlesource.com/585565
Commit-Ready: Amin Hassani <ahassani@chromium.org>
Tested-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
Reviewed-by: Sen Jiang <senj@chromium.org>
| -rw-r--r-- | payload_generator/generate_delta_main.cc | 102 | ||||
| -rwxr-xr-x | scripts/brillo_update_payload | 127 |
2 files changed, 180 insertions, 49 deletions
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc index d7d4f350..d93313f7 100644 --- a/payload_generator/generate_delta_main.cc +++ b/payload_generator/generate_delta_main.cc @@ -29,6 +29,8 @@ #include <brillo/flag_helper.h> #include <brillo/key_value_store.h> +#include "update_engine/common/fake_boot_control.h" +#include "update_engine/common/fake_hardware.h" #include "update_engine/common/prefs.h" #include "update_engine/common/terminator.h" #include "update_engine/common/utils.h" @@ -179,43 +181,58 @@ void VerifySignedPayload(const string& in_file, // TODO(deymo): This function is likely broken for deltas minor version 2 or // newer. Move this function to a new file and make the delta_performer // integration tests use this instead. -void ApplyDelta(const string& in_file, - const string& old_kernel, - const string& old_rootfs, - const string& prefs_dir) { +bool ApplyPayload(const string& payload_file, + // Simply reuses the payload config used for payload + // generation. + const PayloadGenerationConfig& config) { LOG(INFO) << "Applying delta."; - LOG_IF(FATAL, old_rootfs.empty()) - << "Must pass --old_image to apply delta."; - Prefs prefs; + FakeBootControl fake_boot_control; + FakeHardware fake_hardware; + MemoryPrefs prefs; InstallPlan install_plan; - LOG(INFO) << "Setting up preferences under: " << prefs_dir; - LOG_IF(ERROR, !prefs.Init(base::FilePath(prefs_dir))) - << "Failed to initialize preferences."; - // Get original checksums - LOG(INFO) << "Calculating original checksums"; - ImageConfig old_image; - old_image.partitions.emplace_back(kLegacyPartitionNameRoot); - old_image.partitions.back().path = old_rootfs; - old_image.partitions.emplace_back(kLegacyPartitionNameKernel); - old_image.partitions.back().path = old_kernel; - CHECK(old_image.LoadImageSize()); - for (const auto& old_part : old_image.partitions) { - PartitionInfo part_info; - CHECK(diff_utils::InitializePartitionInfo(old_part, &part_info)); + install_plan.source_slot = + config.is_delta ? 0 : BootControlInterface::kInvalidSlot; + install_plan.target_slot = 1; + install_plan.payload_type = + config.is_delta ? InstallPayloadType::kDelta : InstallPayloadType::kFull; + + for (size_t i = 0; i < config.target.partitions.size(); i++) { InstallPlan::Partition part; - part.name = old_part.name; - part.source_hash.assign(part_info.hash().begin(), - part_info.hash().end()); - part.source_path = old_part.path; - // Apply the delta in-place to the old_part. - part.target_path = old_part.path; + part.name = config.target.partitions[i].name; + part.target_path = config.target.partitions[i].path; + fake_boot_control.SetPartitionDevice( + part.name, install_plan.target_slot, part.target_path); + + if (config.is_delta) { + TEST_AND_RETURN_FALSE(config.target.partitions.size() == + config.source.partitions.size()); + PartitionInfo part_info; + TEST_AND_RETURN_FALSE(diff_utils::InitializePartitionInfo( + config.source.partitions[i], &part_info)); + part.source_hash.assign(part_info.hash().begin(), part_info.hash().end()); + part.source_path = config.source.partitions[i].path; + + fake_boot_control.SetPartitionDevice( + part.name, install_plan.source_slot, part.source_path); + } + install_plan.partitions.push_back(part); + + LOG(INFO) << "Install partition:" + << " source: " << part.source_path + << " target: " << part.target_path; + } - DeltaPerformer performer( - &prefs, nullptr, nullptr, nullptr, &install_plan, true); + DeltaPerformer performer(&prefs, + &fake_boot_control, + &fake_hardware, + nullptr, + &install_plan, + true); // is_interactive + brillo::Blob buf(1024 * 1024); - int fd = open(in_file.c_str(), O_RDONLY, 0); + int fd = open(payload_file.c_str(), O_RDONLY, 0); CHECK_GE(fd, 0); ScopedFdCloser fd_closer(&fd); for (off_t offset = 0;; offset += buf.size()) { @@ -223,11 +240,13 @@ void ApplyDelta(const string& in_file, CHECK(utils::PReadAll(fd, buf.data(), buf.size(), offset, &bytes_read)); if (bytes_read == 0) break; - CHECK_EQ(performer.Write(buf.data(), bytes_read), bytes_read); + TEST_AND_RETURN_FALSE(performer.Write(buf.data(), bytes_read)); } CHECK_EQ(performer.Close(), 0); DeltaPerformer::ResetUpdateProgress(&prefs, false); - LOG(INFO) << "Done applying delta."; + LOG(INFO) << "Completed applying " << (config.is_delta ? "delta" : "full") + << " payload."; + return true; } int ExtractProperties(const string& payload_path, const string& props_file) { @@ -289,8 +308,6 @@ int Main(int argc, char** argv) { DEFINE_string(public_key, "", "Path to public key in .pem format"); DEFINE_int32(public_key_version, -1, "DEPRECATED. Key-check version # of client"); - DEFINE_string(prefs_dir, "/tmp/update_engine_prefs", - "Preferences directory, used with apply_delta"); DEFINE_string(signature_size, "", "Raw signature size used for hash calculation. " "You may pass in multiple sizes by colon separating them. E.g. " @@ -401,11 +418,6 @@ int Main(int argc, char** argv) { if (!FLAGS_properties_file.empty()) { return ExtractProperties(FLAGS_in_file, FLAGS_properties_file) ? 0 : 1; } - if (!FLAGS_in_file.empty()) { - ApplyDelta(FLAGS_in_file, FLAGS_old_kernel, FLAGS_old_image, - FLAGS_prefs_dir); - return 0; - } // A payload generation was requested. Convert the flags to a // PayloadGenerationConfig. @@ -488,6 +500,11 @@ int Main(int argc, char** argv) { } } + if (!FLAGS_in_file.empty()) { + ApplyPayload(FLAGS_in_file, payload_config); + return 0; + } + if (!FLAGS_new_postinstall_config_file.empty()) { LOG_IF(FATAL, FLAGS_major_version == kChromeOSMajorPayloadVersion) << "Postinstall config is only allowed in major version 2 or newer."; @@ -563,11 +580,8 @@ int Main(int argc, char** argv) { LOG(INFO) << "Using provided minor_version=" << FLAGS_minor_version; } - if (payload_config.is_delta) { - LOG(INFO) << "Generating delta update"; - } else { - LOG(INFO) << "Generating full update"; - } + LOG(INFO) << "Generating " << (payload_config.is_delta ? "delta" : "full") + << " update"; // From this point, all the options have been parsed. if (!payload_config.Validate()) { diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload index b1394912..911d6229 100755 --- a/scripts/brillo_update_payload +++ b/scripts/brillo_update_payload @@ -1,8 +1,20 @@ #!/bin/bash -# Copyright 2015 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. +# +# Copyright (C) 2015 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. +# # Script to generate a Brillo update for use by the update engine. # @@ -12,14 +24,15 @@ # hash generate a payload or metadata hash # sign generate a signed payload # properties generate a properties file from a payload +# verify verify a payload by recreating a target image. # # Generate command arguments: # --payload generated unsigned payload output file # --source_image if defined, generate a delta payload from the specified # image to the target_image # --target_image the target image that should be sent to clients -# --metadata_size_file if defined, generate a file containing the size of the payload -# metadata in bytes to the specified file +# --metadata_size_file if defined, generate a file containing the size of the +# payload metadata in bytes to the specified file # # Hash command arguments: # --unsigned_payload the input unsigned payload to generate the hash from @@ -50,6 +63,10 @@ # --payload the input signed or unsigned payload # --properties_file the output path where to write the properties, or # '-' for stdout. +# Verify command arguments: +# --payload payload input file +# --source_image verify payload to the specified source image. +# --target_image the target image to verify upon. # Exit codes: @@ -85,6 +102,7 @@ HELP_HASH="hash: Generate the hashes of the unsigned payload and metadata used \ for signing." HELP_SIGN="sign: Insert the signatures into the unsigned payload." HELP_PROPERTIES="properties: Extract payload properties to a file." +HELP_VERIFY="verify: Verify a (signed) update payload." usage() { echo "Supported commands:" @@ -93,6 +111,7 @@ usage() { echo "${HELP_HASH}" echo "${HELP_SIGN}" echo "${HELP_PROPERTIES}" + echo "${HELP_VERIFY}" echo echo "Use: \"$0 <command> --help\" for more options." } @@ -123,6 +142,11 @@ case "${COMMAND}" in properties) FLAGS_HELP="${HELP_PROPERTIES}" ;; + + verify) + FLAGS_HELP="${HELP_VERIFY}" + ;; + *) echo "Unrecognized command: \"${COMMAND}\"" >&2 usage >&2 @@ -174,6 +198,15 @@ if [[ "${COMMAND}" == "properties" ]]; then "Path to output the extracted property files. If '-' is passed stdout will \ be used." fi +if [[ "${COMMAND}" == "verify" ]]; then + DEFINE_string payload "" \ + "Path to the input payload file." + DEFINE_string target_image "" \ + "Path to the target image to verify upon." + DEFINE_string source_image "" \ + "Optional: Path to a source image. If specified, the delta update is \ +applied to this." +fi DEFINE_string work_dir "${TMPDIR:-/tmp}" "Where to dump temporary files." @@ -621,6 +654,87 @@ cmd_properties() { -properties_file="${FLAGS_properties_file}" } +validate_verify() { + [[ -n "${FLAGS_payload}" ]] || + die "Error: you must specify an input filename with --payload FILENAME" + + [[ -n "${FLAGS_target_image}" ]] || + die "Error: you must specify a target image with --target_image FILENAME" +} + +cmd_verify() { + local payload_type="delta" + if [[ -z "${FLAGS_source_image}" ]]; then + payload_type="full" + fi + + echo "Extracting images for ${payload_type} update." + + if [[ "${payload_type}" == "delta" ]]; then + extract_image "${FLAGS_source_image}" SRC_PARTITIONS + fi + extract_image "${FLAGS_target_image}" DST_PARTITIONS PARTITIONS_ORDER + + declare -A TMP_PARTITIONS + for part in "${PARTITIONS_ORDER[@]}"; do + local tmp_part=$(create_tempfile "tmp_part.bin.XXXXXX") + echo "Creating temporary target partition ${tmp_part} for ${part}" + CLEANUP_FILES+=("${tmp_part}") + TMP_PARTITIONS[${part}]=${tmp_part} + local FILESIZE=$(stat -c%s "${DST_PARTITIONS[${part}]}") + echo "Truncating ${TMP_PARTITIONS[${part}]} to ${FILESIZE}" + truncate_file "${TMP_PARTITIONS[${part}]}" "${FILESIZE}" + done + + echo "Verifying ${payload_type} update." + # Common payload args: + GENERATOR_ARGS=( -in_file="${FLAGS_payload}" ) + + local part old_partitions="" new_partitions="" partition_names="" + for part in "${PARTITIONS_ORDER[@]}"; do + if [[ -n "${partition_names}" ]]; then + partition_names+=":" + new_partitions+=":" + old_partitions+=":" + fi + partition_names+="${part}" + new_partitions+="${TMP_PARTITIONS[${part}]}" + old_partitions+="${SRC_PARTITIONS[${part}]:-}" + done + + # Target image args: + GENERATOR_ARGS+=( + -partition_names="${partition_names}" + -new_partitions="${new_partitions}" + ) + + if [[ "${payload_type}" == "delta" ]]; then + # Source image args: + GENERATOR_ARGS+=( + -old_partitions="${old_partitions}" + ) + fi + + echo "Running delta_generator to verify ${payload_type} payload with args: \ +${GENERATOR_ARGS[@]}" + "${GENERATOR}" "${GENERATOR_ARGS[@]}" + + if [[ $? -eq 0 ]]; then + echo "Done applying ${payload_type} update." + echo "Checking the newly generated partitions against the target partitions" + for part in "${PARTITIONS_ORDER[@]}"; do + cmp "${TMP_PARTITIONS[${part}]}" "${DST_PARTITIONS[${part}]}" + local not_str="" + if [[ $? -ne 0 ]]; then + not_str="in" + fi + echo "The new partition (${part}) is ${not_str}valid." + done + else + echo "Failed to apply ${payload_type} update." + fi +} + # Sanity check that the real generator exists: GENERATOR="$(which delta_generator || true)" [[ -x "${GENERATOR}" ]] || die "can't find delta_generator" @@ -638,4 +752,7 @@ case "$COMMAND" in properties) validate_properties cmd_properties ;; + verify) validate_verify + cmd_verify + ;; esac |
