| // Copyright 2014 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/update_client/component_patcher_operation.h" |
| |
| #include <stdint.h> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/files/file_util.h" |
| #include "base/files/memory_mapped_file.h" |
| #include "base/location.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "components/courgette/courgette.h" |
| #include "components/courgette/third_party/bsdiff/bsdiff.h" |
| #include "components/update_client/patcher.h" |
| #include "components/update_client/update_client.h" |
| #include "components/update_client/update_client_errors.h" |
| #include "components/update_client/utils.h" |
| |
| namespace update_client { |
| |
| namespace { |
| |
| const char kOutput[] = "output"; |
| const char kSha256[] = "sha256"; |
| |
| // The integer offset disambiguates between overlapping error ranges. |
| const int kCourgetteErrorOffset = 300; |
| const int kBsdiffErrorOffset = 600; |
| |
| } // namespace |
| |
| const char kOp[] = "op"; |
| const char kBsdiff[] = "bsdiff"; |
| const char kCourgette[] = "courgette"; |
| const char kInput[] = "input"; |
| const char kPatch[] = "patch"; |
| |
| DeltaUpdateOp* CreateDeltaUpdateOp(const std::string& operation, |
| scoped_refptr<Patcher> patcher) { |
| if (operation == "copy") { |
| return new DeltaUpdateOpCopy(); |
| } else if (operation == "create") { |
| return new DeltaUpdateOpCreate(); |
| } else if (operation == "bsdiff" || operation == "courgette") { |
| return new DeltaUpdateOpPatch(operation, patcher); |
| } |
| return nullptr; |
| } |
| |
| DeltaUpdateOp::DeltaUpdateOp() {} |
| |
| DeltaUpdateOp::~DeltaUpdateOp() {} |
| |
| void DeltaUpdateOp::Run(const base::DictionaryValue* command_args, |
| const base::FilePath& input_dir, |
| const base::FilePath& unpack_dir, |
| scoped_refptr<CrxInstaller> installer, |
| ComponentPatcher::Callback callback) { |
| callback_ = std::move(callback); |
| std::string output_rel_path; |
| if (!command_args->GetString(kOutput, &output_rel_path) || |
| !command_args->GetString(kSha256, &output_sha256_)) { |
| DoneRunning(UnpackerError::kDeltaBadCommands, 0); |
| return; |
| } |
| |
| output_abs_path_ = |
| unpack_dir.Append(base::FilePath::FromUTF8Unsafe(output_rel_path)); |
| UnpackerError parse_result = |
| DoParseArguments(command_args, input_dir, installer); |
| if (parse_result != UnpackerError::kNone) { |
| DoneRunning(parse_result, 0); |
| return; |
| } |
| |
| const base::FilePath parent = output_abs_path_.DirName(); |
| if (!base::DirectoryExists(parent)) { |
| if (!base::CreateDirectory(parent)) { |
| DoneRunning(UnpackerError::kIoError, 0); |
| return; |
| } |
| } |
| |
| DoRun(base::BindOnce(&DeltaUpdateOp::DoneRunning, |
| scoped_refptr<DeltaUpdateOp>(this))); |
| } |
| |
| void DeltaUpdateOp::DoneRunning(UnpackerError error, int extended_error) { |
| if (error == UnpackerError::kNone) |
| error = CheckHash(); |
| base::SequencedTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback_), error, extended_error)); |
| } |
| |
| // Uses the hash as a checksum to confirm that the file now residing in the |
| // output directory probably has the contents it should. |
| UnpackerError DeltaUpdateOp::CheckHash() { |
| return VerifyFileHash256(output_abs_path_, output_sha256_) |
| ? UnpackerError::kNone |
| : UnpackerError::kDeltaVerificationFailure; |
| } |
| |
| DeltaUpdateOpCopy::DeltaUpdateOpCopy() {} |
| |
| DeltaUpdateOpCopy::~DeltaUpdateOpCopy() {} |
| |
| UnpackerError DeltaUpdateOpCopy::DoParseArguments( |
| const base::DictionaryValue* command_args, |
| const base::FilePath& input_dir, |
| scoped_refptr<CrxInstaller> installer) { |
| std::string input_rel_path; |
| if (!command_args->GetString(kInput, &input_rel_path)) |
| return UnpackerError::kDeltaBadCommands; |
| |
| if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) |
| return UnpackerError::kDeltaMissingExistingFile; |
| |
| return UnpackerError::kNone; |
| } |
| |
| void DeltaUpdateOpCopy::DoRun(ComponentPatcher::Callback callback) { |
| if (!base::CopyFile(input_abs_path_, output_abs_path_)) |
| std::move(callback).Run(UnpackerError::kDeltaOperationFailure, 0); |
| else |
| std::move(callback).Run(UnpackerError::kNone, 0); |
| } |
| |
| DeltaUpdateOpCreate::DeltaUpdateOpCreate() {} |
| |
| DeltaUpdateOpCreate::~DeltaUpdateOpCreate() {} |
| |
| UnpackerError DeltaUpdateOpCreate::DoParseArguments( |
| const base::DictionaryValue* command_args, |
| const base::FilePath& input_dir, |
| scoped_refptr<CrxInstaller> installer) { |
| std::string patch_rel_path; |
| if (!command_args->GetString(kPatch, &patch_rel_path)) |
| return UnpackerError::kDeltaBadCommands; |
| |
| patch_abs_path_ = |
| input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path)); |
| |
| return UnpackerError::kNone; |
| } |
| |
| void DeltaUpdateOpCreate::DoRun(ComponentPatcher::Callback callback) { |
| #if !defined(OS_STARBOARD) |
| if (!base::Move(patch_abs_path_, output_abs_path_)) |
| std::move(callback).Run(UnpackerError::kDeltaOperationFailure, 0); |
| else |
| std::move(callback).Run(UnpackerError::kNone, 0); |
| #else |
| std::move(callback).Run(UnpackerError::kDeltaOperationFailure, 0); |
| #endif |
| } |
| |
| DeltaUpdateOpPatch::DeltaUpdateOpPatch(const std::string& operation, |
| scoped_refptr<Patcher> patcher) |
| : operation_(operation), patcher_(patcher) { |
| DCHECK(operation == kBsdiff || operation == kCourgette); |
| } |
| |
| DeltaUpdateOpPatch::~DeltaUpdateOpPatch() {} |
| |
| UnpackerError DeltaUpdateOpPatch::DoParseArguments( |
| const base::DictionaryValue* command_args, |
| const base::FilePath& input_dir, |
| scoped_refptr<CrxInstaller> installer) { |
| std::string patch_rel_path; |
| std::string input_rel_path; |
| if (!command_args->GetString(kPatch, &patch_rel_path) || |
| !command_args->GetString(kInput, &input_rel_path)) |
| return UnpackerError::kDeltaBadCommands; |
| |
| if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) |
| return UnpackerError::kDeltaMissingExistingFile; |
| |
| patch_abs_path_ = |
| input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path)); |
| |
| return UnpackerError::kNone; |
| } |
| |
| void DeltaUpdateOpPatch::DoRun(ComponentPatcher::Callback callback) { |
| if (operation_ == kBsdiff) { |
| patcher_->PatchBsdiff(input_abs_path_, patch_abs_path_, output_abs_path_, |
| base::BindOnce(&DeltaUpdateOpPatch::DonePatching, |
| this, std::move(callback))); |
| } else { |
| patcher_->PatchCourgette(input_abs_path_, patch_abs_path_, output_abs_path_, |
| base::BindOnce(&DeltaUpdateOpPatch::DonePatching, |
| this, std::move(callback))); |
| } |
| } |
| |
| void DeltaUpdateOpPatch::DonePatching(ComponentPatcher::Callback callback, |
| int result) { |
| if (operation_ == kBsdiff) { |
| if (result == bsdiff::OK) { |
| std::move(callback).Run(UnpackerError::kNone, 0); |
| } else { |
| std::move(callback).Run(UnpackerError::kDeltaOperationFailure, |
| result + kBsdiffErrorOffset); |
| } |
| } else if (operation_ == kCourgette) { |
| if (result == courgette::C_OK) { |
| std::move(callback).Run(UnpackerError::kNone, 0); |
| } else { |
| std::move(callback).Run(UnpackerError::kDeltaOperationFailure, |
| result + kCourgetteErrorOffset); |
| } |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| } // namespace update_client |