diff options
author | Samuel Huang <huangs@chromium.org> | 2018-03-13 18:19:34 +0000 |
---|---|---|
committer | Edward Lesmes <ehmaldonado@google.com> | 2021-07-23 21:50:59 +0000 |
commit | 06f1ae9aaca969ee95ef840f22b6b461c304542d (patch) | |
tree | f1e5c6624e70628e81fbf38d6cd14b974abe5d93 /patch_reader.cc | |
download | zucchini-06f1ae9aaca969ee95ef840f22b6b461c304542d.tar.gz |
[Zucchini] Move Zucchini from /chrome/installer/ to /components/.
(Use "git log --follow" to see older revisions of files).
/components/ is the most logical place to put Zucchini, which only
depends on /base and /testing/gtest. This move also enables Zucchini to
be used by the Component Updater. Details:
- Move all files; run the following to change deps and guards:
sed 's/chrome\/installer/components/' *.cc *.h -i
sed 's/CHROME_INSTALLER/COMPONENTS/' *.cc *.h -i
- Sorting works out pretty well!
- Change all 'chrome/installer/zucchini' to 'components/zucchini'
throughout other parts of the repo; sort if necessary.
- Fix 6 'git cl lint' errors.
- Change 1 Bind() usage to BindRepeated().
- Update OWNER.
Bug: 729154
Change-Id: I50c5a7d411ea85f707b5994ab319dfb2a1acccf7
Reviewed-on: https://chromium-review.googlesource.com/954923
Reviewed-by: Greg Thompson <grt@chromium.org>
Reviewed-by: Jochen Eisinger <jochen@chromium.org>
Reviewed-by: Samuel Huang <huangs@chromium.org>
Commit-Queue: Samuel Huang <huangs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542857}
NOKEYCHECK=True
GitOrigin-RevId: 577ef6c435e8d43be6e3e60ccbcbd1881780f4ec
Diffstat (limited to 'patch_reader.cc')
-rw-r--r-- | patch_reader.cc | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/patch_reader.cc b/patch_reader.cc new file mode 100644 index 0000000..eceb969 --- /dev/null +++ b/patch_reader.cc @@ -0,0 +1,345 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/zucchini/patch_reader.h" + +#include <type_traits> +#include <utility> + +#include "base/numerics/safe_conversions.h" +#include "components/zucchini/crc32.h" + +namespace zucchini { + +namespace patch { + +bool ParseElementMatch(BufferSource* source, ElementMatch* element_match) { + PatchElementHeader element_header; + if (!source->GetValue(&element_header)) { + LOG(ERROR) << "Impossible to read ElementMatch from source."; + LOG(ERROR) << base::debug::StackTrace().ToString(); + return false; + } + ExecutableType exe_type = + static_cast<ExecutableType>(element_header.exe_type); + if (exe_type >= kNumExeType) { + LOG(ERROR) << "Invalid ExecutableType encountered."; + LOG(ERROR) << base::debug::StackTrace().ToString(); + return false; + } + element_match->old_element.offset = element_header.old_offset; + element_match->new_element.offset = element_header.new_offset; + element_match->old_element.size = element_header.old_length; + element_match->new_element.size = element_header.new_length; + element_match->old_element.exe_type = exe_type; + element_match->new_element.exe_type = exe_type; + return true; +} + +bool ParseBuffer(BufferSource* source, BufferSource* buffer) { + uint32_t size = 0; + if (!source->GetValue(&size)) { + LOG(ERROR) << "Impossible to read buffer size from source."; + LOG(ERROR) << base::debug::StackTrace().ToString(); + return false; + } + if (!source->GetRegion(base::checked_cast<size_t>(size), buffer)) { + LOG(ERROR) << "Impossible to read buffer content from source."; + LOG(ERROR) << base::debug::StackTrace().ToString(); + return false; + } + return true; +} + +} // namespace patch + +/******** EquivalenceSource ********/ + +EquivalenceSource::EquivalenceSource() = default; +EquivalenceSource::EquivalenceSource(const EquivalenceSource&) = default; +EquivalenceSource::~EquivalenceSource() = default; + +bool EquivalenceSource::Initialize(BufferSource* source) { + return patch::ParseBuffer(source, &src_skip_) && + patch::ParseBuffer(source, &dst_skip_) && + patch::ParseBuffer(source, ©_count_); +} + +base::Optional<Equivalence> EquivalenceSource::GetNext() { + if (src_skip_.empty() || dst_skip_.empty() || copy_count_.empty()) + return base::nullopt; + + Equivalence equivalence = {}; + + uint32_t length = 0; + if (!patch::ParseVarUInt<uint32_t>(©_count_, &length)) + return base::nullopt; + equivalence.length = base::strict_cast<offset_t>(length); + + int32_t src_offset_diff = 0; // Intentionally signed. + if (!patch::ParseVarInt<int32_t>(&src_skip_, &src_offset_diff)) + return base::nullopt; + base::CheckedNumeric<offset_t> src_offset = + previous_src_offset_ + src_offset_diff; + if (!src_offset.IsValid()) + return base::nullopt; + + equivalence.src_offset = src_offset.ValueOrDie(); + previous_src_offset_ = src_offset + equivalence.length; + if (!previous_src_offset_.IsValid()) + return base::nullopt; + + uint32_t dst_offset_diff = 0; // Intentionally unsigned. + if (!patch::ParseVarUInt<uint32_t>(&dst_skip_, &dst_offset_diff)) + return base::nullopt; + base::CheckedNumeric<offset_t> dst_offset = + previous_dst_offset_ + dst_offset_diff; + if (!dst_offset.IsValid()) + return base::nullopt; + + equivalence.dst_offset = dst_offset.ValueOrDie(); + previous_dst_offset_ = equivalence.dst_offset + equivalence.length; + if (!previous_dst_offset_.IsValid()) + return base::nullopt; + + return equivalence; +} + +/******** ExtraDataSource ********/ + +ExtraDataSource::ExtraDataSource() = default; +ExtraDataSource::ExtraDataSource(const ExtraDataSource&) = default; +ExtraDataSource::~ExtraDataSource() = default; + +bool ExtraDataSource::Initialize(BufferSource* source) { + return patch::ParseBuffer(source, &extra_data_); +} + +base::Optional<ConstBufferView> ExtraDataSource::GetNext(offset_t size) { + ConstBufferView buffer; + if (!extra_data_.GetRegion(size, &buffer)) + return base::nullopt; + return buffer; +} + +/******** RawDeltaSource ********/ + +RawDeltaSource::RawDeltaSource() = default; +RawDeltaSource::RawDeltaSource(const RawDeltaSource&) = default; +RawDeltaSource::~RawDeltaSource() = default; + +bool RawDeltaSource::Initialize(BufferSource* source) { + return patch::ParseBuffer(source, &raw_delta_skip_) && + patch::ParseBuffer(source, &raw_delta_diff_); +} + +base::Optional<RawDeltaUnit> RawDeltaSource::GetNext() { + if (raw_delta_skip_.empty() || raw_delta_diff_.empty()) + return base::nullopt; + + RawDeltaUnit delta = {}; + uint32_t copy_offset_diff = 0; + if (!patch::ParseVarUInt<uint32_t>(&raw_delta_skip_, ©_offset_diff)) + return base::nullopt; + base::CheckedNumeric<offset_t> copy_offset = + copy_offset_diff + copy_offset_compensation_; + if (!copy_offset.IsValid()) + return base::nullopt; + delta.copy_offset = copy_offset.ValueOrDie(); + + if (!raw_delta_diff_.GetValue<int8_t>(&delta.diff)) + return base::nullopt; + + // We keep track of the compensation needed for next offset, taking into + // accound delta encoding and bias of -1. + copy_offset_compensation_ = copy_offset + 1; + if (!copy_offset_compensation_.IsValid()) + return base::nullopt; + return delta; +} + +/******** ReferenceDeltaSource ********/ + +ReferenceDeltaSource::ReferenceDeltaSource() = default; +ReferenceDeltaSource::ReferenceDeltaSource(const ReferenceDeltaSource&) = + default; +ReferenceDeltaSource::~ReferenceDeltaSource() = default; + +bool ReferenceDeltaSource::Initialize(BufferSource* source) { + return patch::ParseBuffer(source, &reference_delta_); +} + +base::Optional<int32_t> ReferenceDeltaSource::GetNext() { + if (reference_delta_.empty()) + return base::nullopt; + int32_t delta = 0; + if (!patch::ParseVarInt<int32_t>(&reference_delta_, &delta)) + return base::nullopt; + return delta; +} + +/******** TargetSource ********/ + +TargetSource::TargetSource() = default; +TargetSource::TargetSource(const TargetSource&) = default; +TargetSource::~TargetSource() = default; + +bool TargetSource::Initialize(BufferSource* source) { + return patch::ParseBuffer(source, &extra_targets_); +} + +base::Optional<offset_t> TargetSource::GetNext() { + if (extra_targets_.empty()) + return base::nullopt; + + uint32_t target_diff = 0; + if (!patch::ParseVarUInt<uint32_t>(&extra_targets_, &target_diff)) + return base::nullopt; + base::CheckedNumeric<offset_t> target = target_diff + target_compensation_; + if (!target.IsValid()) + return base::nullopt; + + // We keep track of the compensation needed for next target, taking into + // accound delta encoding and bias of -1. + target_compensation_ = target + 1; + if (!target_compensation_.IsValid()) + return base::nullopt; + return offset_t(target.ValueOrDie()); +} + +/******** PatchElementReader ********/ + +PatchElementReader::PatchElementReader() = default; +PatchElementReader::PatchElementReader(PatchElementReader&&) = default; +PatchElementReader::~PatchElementReader() = default; + +bool PatchElementReader::Initialize(BufferSource* source) { + bool ok = patch::ParseElementMatch(source, &element_match_) && + equivalences_.Initialize(source) && + extra_data_.Initialize(source) && raw_delta_.Initialize(source) && + reference_delta_.Initialize(source); + if (!ok) + return false; + uint32_t pool_count = 0; + if (!source->GetValue(&pool_count)) { + LOG(ERROR) << "Impossible to read pool_count from source."; + return false; + } + for (uint32_t i = 0; i < pool_count; ++i) { + uint8_t pool_tag_value = 0; + if (!source->GetValue(&pool_tag_value)) { + LOG(ERROR) << "Impossible to read pool_tag from source."; + return false; + } + PoolTag pool_tag(pool_tag_value); + if (pool_tag == kNoPoolTag) { + LOG(ERROR) << "Invalid pool_tag encountered in ExtraTargetList."; + return false; + } + auto insert_result = extra_targets_.insert({pool_tag, {}}); + if (!insert_result.second) { // Element already present. + LOG(ERROR) << "Multiple ExtraTargetList found for the same pool_tag"; + return false; + } + if (!insert_result.first->second.Initialize(source)) + return false; + } + return true; +} + +/******** EnsemblePatchReader ********/ + +base::Optional<EnsemblePatchReader> EnsemblePatchReader::Create( + ConstBufferView buffer) { + BufferSource source(buffer); + EnsemblePatchReader patch; + if (!patch.Initialize(&source)) + return base::nullopt; + return patch; +} + +EnsemblePatchReader::EnsemblePatchReader() = default; +EnsemblePatchReader::EnsemblePatchReader(EnsemblePatchReader&&) = default; +EnsemblePatchReader::~EnsemblePatchReader() = default; + +bool EnsemblePatchReader::Initialize(BufferSource* source) { + if (!source->GetValue(&header_)) { + LOG(ERROR) << "Impossible to read header from source."; + return false; + } + if (header_.magic != PatchHeader::kMagic) { + LOG(ERROR) << "Patch contains invalid magic."; + return false; + } + uint32_t patch_type_int = + static_cast<uint32_t>(PatchType::kUnrecognisedPatch); + if (!source->GetValue(&patch_type_int)) { + LOG(ERROR) << "Impossible to read patch_type from source."; + return false; + } + patch_type_ = static_cast<PatchType>(patch_type_int); + if (patch_type_ != PatchType::kRawPatch && + patch_type_ != PatchType::kSinglePatch && + patch_type_ != PatchType::kEnsemblePatch) { + LOG(ERROR) << "Invalid patch_type encountered."; + return false; + } + + uint32_t element_count = 0; + if (!source->GetValue(&element_count)) { + LOG(ERROR) << "Impossible to read element_count from source."; + return false; + } + if (patch_type_ == PatchType::kRawPatch || + patch_type_ == PatchType::kSinglePatch) { + if (element_count != 1) { + LOG(ERROR) << "Unexpected number of elements in patch."; + return false; // Only one element expected. + } + } + + offset_t current_dst_offset = 0; + for (uint32_t i = 0; i < element_count; ++i) { + PatchElementReader element_patch; + if (!element_patch.Initialize(source)) + return false; + + if (!element_patch.old_element().FitsIn(header_.old_size) || + !element_patch.new_element().FitsIn(header_.new_size)) { + LOG(ERROR) << "Invalid element encountered."; + return false; + } + + if (element_patch.new_element().offset != current_dst_offset) { + LOG(ERROR) << "Invalid element encountered."; + return false; + } + current_dst_offset = element_patch.new_element().EndOffset(); + + elements_.push_back(std::move(element_patch)); + } + if (current_dst_offset != header_.new_size) { + LOG(ERROR) << "Patch elements don't fully cover new image file."; + return false; + } + + if (!source->empty()) { + LOG(ERROR) << "Patch was not fully consumed."; + return false; + } + + return true; +} + +bool EnsemblePatchReader::CheckOldFile(ConstBufferView old_image) const { + return old_image.size() == header_.old_size && + CalculateCrc32(old_image.begin(), old_image.end()) == header_.old_crc; +} + +bool EnsemblePatchReader::CheckNewFile(ConstBufferView new_image) const { + return new_image.size() == header_.new_size && + CalculateCrc32(new_image.begin(), new_image.end()) == header_.new_crc; +} + +} // namespace zucchini |