| // Copyright (c) 2011 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. |
| |
| // This file contains the code to apply a Courgette patch. |
| |
| #include "courgette/ensemble.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/files/file.h" |
| #include "base/files/file_util.h" |
| #include "base/files/memory_mapped_file.h" |
| #include "base/macros.h" |
| #include "courgette/crc.h" |
| #include "courgette/patcher_x86_32.h" |
| #include "courgette/region.h" |
| #include "courgette/simple_delta.h" |
| #include "courgette/streams.h" |
| |
| namespace courgette { |
| |
| // EnsemblePatchApplication is all the logic and data required to apply the |
| // multi-stage patch. |
| class EnsemblePatchApplication { |
| public: |
| EnsemblePatchApplication(); |
| ~EnsemblePatchApplication() = default; |
| |
| Status ReadHeader(SourceStream* header_stream); |
| |
| Status InitBase(const Region& region); |
| |
| Status ValidateBase(); |
| |
| Status ReadInitialParameters(SourceStream* initial_parameters); |
| |
| Status PredictTransformParameters(SinkStreamSet* predicted_parameters); |
| |
| Status SubpatchTransformParameters(SinkStreamSet* prediction, |
| SourceStream* correction, |
| SourceStreamSet* corrected_parameters); |
| |
| Status TransformUp(SourceStreamSet* parameters, |
| SinkStreamSet* transformed_elements); |
| |
| Status SubpatchTransformedElements(SinkStreamSet* elements, |
| SourceStream* correction, |
| SourceStreamSet* corrected_elements); |
| |
| Status TransformDown(SourceStreamSet* transformed_elements, |
| SinkStream* basic_elements); |
| |
| Status SubpatchFinalOutput(SourceStream* original, |
| SourceStream* correction, |
| SinkStream* corrected_ensemble); |
| |
| private: |
| Status SubpatchStreamSets(SinkStreamSet* predicted_items, |
| SourceStream* correction, |
| SourceStreamSet* corrected_items, |
| SinkStream* corrected_items_storage); |
| |
| Region base_region_; // Location of in-memory copy of 'old' version. |
| |
| uint32_t source_checksum_; |
| uint32_t target_checksum_; |
| uint32_t final_patch_input_size_prediction_; |
| |
| std::vector<std::unique_ptr<TransformationPatcher>> patchers_; |
| |
| SinkStream corrected_parameters_storage_; |
| SinkStream corrected_elements_storage_; |
| |
| DISALLOW_COPY_AND_ASSIGN(EnsemblePatchApplication); |
| }; |
| |
| EnsemblePatchApplication::EnsemblePatchApplication() |
| : source_checksum_(0), target_checksum_(0), |
| final_patch_input_size_prediction_(0) { |
| } |
| |
| Status EnsemblePatchApplication::ReadHeader(SourceStream* header_stream) { |
| uint32_t magic; |
| if (!header_stream->ReadVarint32(&magic)) |
| return C_BAD_ENSEMBLE_MAGIC; |
| |
| if (magic != CourgettePatchFile::kMagic) |
| return C_BAD_ENSEMBLE_MAGIC; |
| |
| uint32_t version; |
| if (!header_stream->ReadVarint32(&version)) |
| return C_BAD_ENSEMBLE_VERSION; |
| |
| if (version != CourgettePatchFile::kVersion) |
| return C_BAD_ENSEMBLE_VERSION; |
| |
| if (!header_stream->ReadVarint32(&source_checksum_)) |
| return C_BAD_ENSEMBLE_HEADER; |
| |
| if (!header_stream->ReadVarint32(&target_checksum_)) |
| return C_BAD_ENSEMBLE_HEADER; |
| |
| if (!header_stream->ReadVarint32(&final_patch_input_size_prediction_)) |
| return C_BAD_ENSEMBLE_HEADER; |
| |
| return C_OK; |
| } |
| |
| Status EnsemblePatchApplication::InitBase(const Region& region) { |
| base_region_.assign(region); |
| return C_OK; |
| } |
| |
| Status EnsemblePatchApplication::ValidateBase() { |
| uint32_t checksum = CalculateCrc(base_region_.start(), base_region_.length()); |
| if (source_checksum_ != checksum) |
| return C_BAD_ENSEMBLE_CRC; |
| |
| return C_OK; |
| } |
| |
| Status EnsemblePatchApplication::ReadInitialParameters( |
| SourceStream* transformation_parameters) { |
| uint32_t number_of_transformations = 0; |
| if (!transformation_parameters->ReadVarint32(&number_of_transformations)) |
| return C_BAD_ENSEMBLE_HEADER; |
| |
| for (size_t i = 0; i < number_of_transformations; ++i) { |
| uint32_t kind; |
| if (!transformation_parameters->ReadVarint32(&kind)) |
| return C_BAD_ENSEMBLE_HEADER; |
| |
| std::unique_ptr<TransformationPatcher> patcher; |
| |
| switch (kind) { |
| case EXE_WIN_32_X86: // Fall through. |
| case EXE_ELF_32_X86: |
| case EXE_WIN_32_X64: |
| patcher.reset(new PatcherX86_32(base_region_)); |
| break; |
| default: |
| return C_BAD_ENSEMBLE_HEADER; |
| } |
| |
| DCHECK(patcher); |
| patchers_.push_back(std::move(patcher)); |
| } |
| |
| for (size_t i = 0; i < patchers_.size(); ++i) { |
| Status status = patchers_[i]->Init(transformation_parameters); |
| if (status != C_OK) |
| return status; |
| } |
| |
| // All transformation_parameters should have been consumed by the above loop. |
| if (!transformation_parameters->Empty()) |
| return C_BAD_ENSEMBLE_HEADER; |
| |
| return C_OK; |
| } |
| |
| Status EnsemblePatchApplication::PredictTransformParameters( |
| SinkStreamSet* all_predicted_parameters) { |
| for (size_t i = 0; i < patchers_.size(); ++i) { |
| SinkStreamSet single_predicted_parameters; |
| Status status = |
| patchers_[i]->PredictTransformParameters(&single_predicted_parameters); |
| if (status != C_OK) |
| return status; |
| if (!all_predicted_parameters->WriteSet(&single_predicted_parameters)) |
| return C_STREAM_ERROR; |
| } |
| return C_OK; |
| } |
| |
| Status EnsemblePatchApplication::SubpatchTransformParameters( |
| SinkStreamSet* predicted_parameters, |
| SourceStream* correction, |
| SourceStreamSet* corrected_parameters) { |
| return SubpatchStreamSets(predicted_parameters, |
| correction, |
| corrected_parameters, |
| &corrected_parameters_storage_); |
| } |
| |
| Status EnsemblePatchApplication::TransformUp( |
| SourceStreamSet* parameters, |
| SinkStreamSet* transformed_elements) { |
| for (size_t i = 0; i < patchers_.size(); ++i) { |
| SourceStreamSet single_parameters; |
| if (!parameters->ReadSet(&single_parameters)) |
| return C_STREAM_ERROR; |
| SinkStreamSet single_transformed_element; |
| Status status = patchers_[i]->Transform(&single_parameters, |
| &single_transformed_element); |
| if (status != C_OK) |
| return status; |
| if (!single_parameters.Empty()) |
| return C_STREAM_NOT_CONSUMED; |
| if (!transformed_elements->WriteSet(&single_transformed_element)) |
| return C_STREAM_ERROR; |
| } |
| |
| if (!parameters->Empty()) |
| return C_STREAM_NOT_CONSUMED; |
| return C_OK; |
| } |
| |
| Status EnsemblePatchApplication::SubpatchTransformedElements( |
| SinkStreamSet* predicted_elements, |
| SourceStream* correction, |
| SourceStreamSet* corrected_elements) { |
| return SubpatchStreamSets(predicted_elements, |
| correction, |
| corrected_elements, |
| &corrected_elements_storage_); |
| } |
| |
| Status EnsemblePatchApplication::TransformDown( |
| SourceStreamSet* transformed_elements, |
| SinkStream* basic_elements) { |
| // Construct blob of original input followed by reformed elements. |
| |
| if (!basic_elements->Reserve(final_patch_input_size_prediction_)) { |
| return C_STREAM_ERROR; |
| } |
| |
| // The original input: |
| if (!basic_elements->Write(base_region_.start(), base_region_.length())) |
| return C_STREAM_ERROR; |
| |
| for (size_t i = 0; i < patchers_.size(); ++i) { |
| SourceStreamSet single_corrected_element; |
| if (!transformed_elements->ReadSet(&single_corrected_element)) |
| return C_STREAM_ERROR; |
| Status status = patchers_[i]->Reform(&single_corrected_element, |
| basic_elements); |
| if (status != C_OK) |
| return status; |
| if (!single_corrected_element.Empty()) |
| return C_STREAM_NOT_CONSUMED; |
| } |
| |
| if (!transformed_elements->Empty()) |
| return C_STREAM_NOT_CONSUMED; |
| // We have totally consumed transformed_elements, so can free the |
| // storage to which it referred. |
| corrected_elements_storage_.Retire(); |
| |
| return C_OK; |
| } |
| |
| Status EnsemblePatchApplication::SubpatchFinalOutput( |
| SourceStream* original, |
| SourceStream* correction, |
| SinkStream* corrected_ensemble) { |
| Status delta_status = ApplySimpleDelta(original, correction, |
| corrected_ensemble); |
| if (delta_status != C_OK) |
| return delta_status; |
| |
| if (CalculateCrc(corrected_ensemble->Buffer(), |
| corrected_ensemble->Length()) != target_checksum_) |
| return C_BAD_ENSEMBLE_CRC; |
| |
| return C_OK; |
| } |
| |
| Status EnsemblePatchApplication::SubpatchStreamSets( |
| SinkStreamSet* predicted_items, |
| SourceStream* correction, |
| SourceStreamSet* corrected_items, |
| SinkStream* corrected_items_storage) { |
| SinkStream linearized_predicted_items; |
| if (!predicted_items->CopyTo(&linearized_predicted_items)) |
| return C_STREAM_ERROR; |
| |
| SourceStream prediction; |
| prediction.Init(linearized_predicted_items); |
| |
| Status status = ApplySimpleDelta(&prediction, |
| correction, |
| corrected_items_storage); |
| if (status != C_OK) |
| return status; |
| |
| if (!corrected_items->Init(corrected_items_storage->Buffer(), |
| corrected_items_storage->Length())) |
| return C_STREAM_ERROR; |
| |
| return C_OK; |
| } |
| |
| Status ApplyEnsemblePatch(SourceStream* base, |
| SourceStream* patch, |
| SinkStream* output) { |
| Status status; |
| EnsemblePatchApplication patch_process; |
| |
| status = patch_process.ReadHeader(patch); |
| if (status != C_OK) |
| return status; |
| |
| status = patch_process.InitBase(Region(base->Buffer(), base->Remaining())); |
| if (status != C_OK) |
| return status; |
| |
| status = patch_process.ValidateBase(); |
| if (status != C_OK) |
| return status; |
| |
| // The rest of the patch stream is a StreamSet. |
| SourceStreamSet patch_streams; |
| patch_streams.Init(patch); |
| |
| SourceStream* transformation_descriptions = patch_streams.stream(0); |
| SourceStream* parameter_correction = patch_streams.stream(1); |
| SourceStream* transformed_elements_correction = patch_streams.stream(2); |
| SourceStream* ensemble_correction = patch_streams.stream(3); |
| |
| status = patch_process.ReadInitialParameters(transformation_descriptions); |
| if (status != C_OK) |
| return status; |
| |
| SinkStreamSet predicted_parameters; |
| status = patch_process.PredictTransformParameters(&predicted_parameters); |
| if (status != C_OK) |
| return status; |
| |
| SourceStreamSet corrected_parameters; |
| status = patch_process.SubpatchTransformParameters(&predicted_parameters, |
| parameter_correction, |
| &corrected_parameters); |
| if (status != C_OK) |
| return status; |
| |
| SinkStreamSet transformed_elements; |
| status = patch_process.TransformUp(&corrected_parameters, |
| &transformed_elements); |
| if (status != C_OK) |
| return status; |
| |
| SourceStreamSet corrected_transformed_elements; |
| status = patch_process.SubpatchTransformedElements( |
| &transformed_elements, |
| transformed_elements_correction, |
| &corrected_transformed_elements); |
| if (status != C_OK) |
| return status; |
| |
| SinkStream original_ensemble_and_corrected_base_elements; |
| status = patch_process.TransformDown( |
| &corrected_transformed_elements, |
| &original_ensemble_and_corrected_base_elements); |
| if (status != C_OK) |
| return status; |
| |
| SourceStream final_patch_prediction; |
| final_patch_prediction.Init(original_ensemble_and_corrected_base_elements); |
| status = patch_process.SubpatchFinalOutput(&final_patch_prediction, |
| ensemble_correction, output); |
| if (status != C_OK) |
| return status; |
| |
| return C_OK; |
| } |
| |
| Status ApplyEnsemblePatch(base::File old_file, |
| base::File patch_file, |
| base::File new_file) { |
| base::MemoryMappedFile patch_file_mem; |
| if (!patch_file_mem.Initialize(std::move(patch_file))) |
| return C_READ_OPEN_ERROR; |
| |
| // 'Dry-run' the first step of the patch process to validate format of header. |
| SourceStream patch_header_stream; |
| patch_header_stream.Init(patch_file_mem.data(), patch_file_mem.length()); |
| EnsemblePatchApplication patch_process; |
| Status status = patch_process.ReadHeader(&patch_header_stream); |
| if (status != C_OK) |
| return status; |
| |
| // Read the old_file. |
| base::MemoryMappedFile old_file_mem; |
| if (!old_file_mem.Initialize(std::move(old_file))) |
| return C_READ_ERROR; |
| |
| // Apply patch on streams. |
| SourceStream old_source_stream; |
| SourceStream patch_source_stream; |
| old_source_stream.Init(old_file_mem.data(), old_file_mem.length()); |
| patch_source_stream.Init(patch_file_mem.data(), patch_file_mem.length()); |
| SinkStream new_sink_stream; |
| status = ApplyEnsemblePatch(&old_source_stream, &patch_source_stream, |
| &new_sink_stream); |
| if (status != C_OK) |
| return status; |
| |
| // Write the patched data to |new_file_name|. |
| int written = new_file.Write( |
| 0, |
| reinterpret_cast<const char*>(new_sink_stream.Buffer()), |
| static_cast<int>(new_sink_stream.Length())); |
| if (written == -1) |
| return C_WRITE_OPEN_ERROR; |
| if (static_cast<size_t>(written) != new_sink_stream.Length()) |
| return C_WRITE_ERROR; |
| |
| return C_OK; |
| } |
| |
| Status ApplyEnsemblePatch(const base::FilePath::CharType* old_file_name, |
| const base::FilePath::CharType* patch_file_name, |
| const base::FilePath::CharType* new_file_name) { |
| Status result = ApplyEnsemblePatch( |
| base::File(base::FilePath(old_file_name), |
| base::File::FLAG_OPEN | base::File::FLAG_READ | |
| base::File::FLAG_SHARE_DELETE), |
| base::File(base::FilePath(patch_file_name), |
| base::File::FLAG_OPEN | base::File::FLAG_READ | |
| base::File::FLAG_SHARE_DELETE), |
| base::File(base::FilePath(new_file_name), |
| base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE | |
| base::File::FLAG_EXCLUSIVE_WRITE | |
| base::File::FLAG_SHARE_DELETE)); |
| if (result != C_OK) |
| base::DeleteFile(base::FilePath(new_file_name)); |
| return result; |
| } |
| |
| } // namespace courgette |