OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/component_updater/component_patcher.h" |
| 6 |
| 7 #include <string> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/file_util.h" |
| 11 #include "base/files/memory_mapped_file.h" |
| 12 #include "base/json/json_file_value_serializer.h" |
| 13 #include "base/memory/scoped_handle.h" |
| 14 #include "base/path_service.h" |
| 15 #include "base/strings/string_number_conversions.h" |
| 16 #include "chrome/browser/component_updater/component_patcher_internal.h" |
| 17 #include "chrome/browser/component_updater/component_updater_service.h" |
| 18 #include "chrome/common/extensions/extension_constants.h" |
| 19 #include "crypto/secure_hash.h" |
| 20 #include "crypto/sha2.h" |
| 21 #include "crypto/signature_verifier.h" |
| 22 #include "extensions/common/crx_file.h" |
| 23 #include "third_party/zlib/google/zip.h" |
| 24 |
| 25 using crypto::SecureHash; |
| 26 |
| 27 namespace { |
| 28 |
| 29 const char kInput[] = "input"; |
| 30 const char kOp[] = "op"; |
| 31 const char kOutput[] = "output"; |
| 32 const char kPatch[] = "patch"; |
| 33 const char kSha256[] = "sha256"; |
| 34 |
| 35 DeltaUpdateOp* CreateDeltaUpdateOp(base::DictionaryValue* command) { |
| 36 std::string operation; |
| 37 if (!command->GetString(kOp, &operation)) |
| 38 return NULL; |
| 39 if (operation == "copy") |
| 40 return new DeltaUpdateOpCopy(); |
| 41 else if (operation == "create") |
| 42 return new DeltaUpdateOpCreate(); |
| 43 else if (operation == "bsdiff") |
| 44 return new DeltaUpdateOpPatchBsdiff(); |
| 45 else if (operation == "courgette") |
| 46 return new DeltaUpdateOpPatchCourgette(); |
| 47 return NULL; |
| 48 } |
| 49 |
| 50 // Deserialize the commands file (present in delta update packages). The top |
| 51 // level must be a list. |
| 52 base::ListValue* ReadCommands(const base::FilePath& unpack_path) { |
| 53 const base::FilePath commands = |
| 54 unpack_path.Append(FILE_PATH_LITERAL("commands.json")); |
| 55 if (!file_util::PathExists(commands)) |
| 56 return NULL; |
| 57 |
| 58 JSONFileValueSerializer serializer(commands); |
| 59 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, NULL)); |
| 60 if (!root.get()) |
| 61 return NULL; |
| 62 |
| 63 if (!root->IsType(base::Value::TYPE_LIST)) |
| 64 return NULL; |
| 65 |
| 66 return static_cast<base::ListValue*>(root.release()); |
| 67 } |
| 68 |
| 69 } // namespace. |
| 70 |
| 71 |
| 72 // The patching support is not cross-platform at the moment. |
| 73 ComponentPatcherCrossPlatform::ComponentPatcherCrossPlatform() {} |
| 74 |
| 75 ComponentUnpacker::Error ComponentPatcherCrossPlatform::Patch( |
| 76 PatchType patch_type, |
| 77 const base::FilePath& input_file, |
| 78 const base::FilePath& patch_file, |
| 79 const base::FilePath& output_file, |
| 80 int* error) { |
| 81 return ComponentUnpacker::kDeltaUnsupportedCommand; |
| 82 } |
| 83 |
| 84 DeltaUpdateOp::~DeltaUpdateOp() {} |
| 85 |
| 86 DeltaUpdateOp::DeltaUpdateOp() {} |
| 87 |
| 88 ComponentUnpacker::Error DeltaUpdateOp::Run(base::DictionaryValue* command_args, |
| 89 const base::FilePath& input_dir, |
| 90 const base::FilePath& unpack_dir, |
| 91 ComponentPatcher* patcher, |
| 92 ComponentInstaller* installer, |
| 93 int* error) { |
| 94 std::string output_rel_path; |
| 95 if (!command_args->GetString(kOutput, &output_rel_path) || |
| 96 !command_args->GetString(kSha256, &output_sha256_)) |
| 97 return ComponentUnpacker::kDeltaBadCommands; |
| 98 |
| 99 output_abs_path_ = unpack_dir.Append( |
| 100 base::FilePath::FromUTF8Unsafe(output_rel_path)); |
| 101 ComponentUnpacker::Error parse_result = DoParseArguments( |
| 102 command_args, input_dir, installer); |
| 103 if (parse_result != ComponentUnpacker::kNone) |
| 104 return parse_result; |
| 105 |
| 106 const base::FilePath parent = output_abs_path_.DirName(); |
| 107 if (!file_util::DirectoryExists(parent)) { |
| 108 if (!file_util::CreateDirectory(parent)) |
| 109 return ComponentUnpacker::kIoError; |
| 110 } |
| 111 |
| 112 ComponentUnpacker::Error run_result = DoRun(patcher, error); |
| 113 if (run_result != ComponentUnpacker::kNone) |
| 114 return run_result; |
| 115 |
| 116 return CheckHash(); |
| 117 } |
| 118 |
| 119 // Uses the hash as a checksum to confirm that the file now residing in the |
| 120 // output directory probably has the contents it should. |
| 121 ComponentUnpacker::Error DeltaUpdateOp::CheckHash() { |
| 122 std::vector<uint8> expected_hash; |
| 123 if (!base::HexStringToBytes(output_sha256_, &expected_hash) || |
| 124 expected_hash.size() != crypto::kSHA256Length) |
| 125 return ComponentUnpacker::kDeltaVerificationFailure; |
| 126 |
| 127 base::MemoryMappedFile output_file_mmapped; |
| 128 if (!output_file_mmapped.Initialize(output_abs_path_)) |
| 129 return ComponentUnpacker::kDeltaVerificationFailure; |
| 130 |
| 131 uint8 actual_hash[crypto::kSHA256Length] = {0}; |
| 132 const scoped_ptr<SecureHash> hasher(SecureHash::Create(SecureHash::SHA256)); |
| 133 hasher->Update(output_file_mmapped.data(), output_file_mmapped.length()); |
| 134 hasher->Finish(actual_hash, sizeof(actual_hash)); |
| 135 if (memcmp(actual_hash, &expected_hash[0], sizeof(actual_hash))) |
| 136 return ComponentUnpacker::kDeltaVerificationFailure; |
| 137 |
| 138 return ComponentUnpacker::kNone; |
| 139 } |
| 140 |
| 141 DeltaUpdateOpCopy::DeltaUpdateOpCopy() {} |
| 142 |
| 143 ComponentUnpacker::Error DeltaUpdateOpCopy::DoParseArguments( |
| 144 base::DictionaryValue* command_args, |
| 145 const base::FilePath& input_dir, |
| 146 ComponentInstaller* installer) { |
| 147 std::string input_rel_path; |
| 148 if (!command_args->GetString(kInput, &input_rel_path)) |
| 149 return ComponentUnpacker::kDeltaBadCommands; |
| 150 |
| 151 if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) |
| 152 return ComponentUnpacker::kDeltaMissingExistingFile; |
| 153 |
| 154 return ComponentUnpacker::kNone; |
| 155 } |
| 156 |
| 157 ComponentUnpacker::Error DeltaUpdateOpCopy::DoRun(ComponentPatcher*, |
| 158 int* error) { |
| 159 *error = 0; |
| 160 if (!file_util::CopyFile(input_abs_path_, output_abs_path_)) |
| 161 return ComponentUnpacker::kDeltaOperationFailure; |
| 162 |
| 163 return ComponentUnpacker::kNone; |
| 164 } |
| 165 |
| 166 DeltaUpdateOpCreate::DeltaUpdateOpCreate() {} |
| 167 |
| 168 ComponentUnpacker::Error DeltaUpdateOpCreate::DoParseArguments( |
| 169 base::DictionaryValue* command_args, |
| 170 const base::FilePath& input_dir, |
| 171 ComponentInstaller* installer) { |
| 172 std::string patch_rel_path; |
| 173 if (!command_args->GetString(kPatch, &patch_rel_path)) |
| 174 return ComponentUnpacker::kDeltaBadCommands; |
| 175 |
| 176 patch_abs_path_ = input_dir.Append( |
| 177 base::FilePath::FromUTF8Unsafe(patch_rel_path)); |
| 178 |
| 179 return ComponentUnpacker::kNone; |
| 180 } |
| 181 |
| 182 ComponentUnpacker::Error DeltaUpdateOpCreate::DoRun(ComponentPatcher*, |
| 183 int* error) { |
| 184 *error = 0; |
| 185 if (!file_util::Move(patch_abs_path_, output_abs_path_)) |
| 186 return ComponentUnpacker::kDeltaOperationFailure; |
| 187 |
| 188 return ComponentUnpacker::kNone; |
| 189 } |
| 190 |
| 191 DeltaUpdateOpPatchBsdiff::DeltaUpdateOpPatchBsdiff() {} |
| 192 |
| 193 ComponentUnpacker::Error DeltaUpdateOpPatchBsdiff::DoParseArguments( |
| 194 base::DictionaryValue* command_args, |
| 195 const base::FilePath& input_dir, |
| 196 ComponentInstaller* installer) { |
| 197 std::string patch_rel_path; |
| 198 std::string input_rel_path; |
| 199 if (!command_args->GetString(kPatch, &patch_rel_path) || |
| 200 !command_args->GetString(kInput, &input_rel_path)) |
| 201 return ComponentUnpacker::kDeltaBadCommands; |
| 202 |
| 203 if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) |
| 204 return ComponentUnpacker::kDeltaMissingExistingFile; |
| 205 |
| 206 patch_abs_path_ = input_dir.Append( |
| 207 base::FilePath::FromUTF8Unsafe(patch_rel_path)); |
| 208 |
| 209 return ComponentUnpacker::kNone; |
| 210 } |
| 211 |
| 212 ComponentUnpacker::Error DeltaUpdateOpPatchBsdiff::DoRun( |
| 213 ComponentPatcher* patcher, |
| 214 int* error) { |
| 215 *error = 0; |
| 216 return patcher->Patch(ComponentPatcher::kPatchTypeBsdiff, |
| 217 input_abs_path_, |
| 218 patch_abs_path_, |
| 219 output_abs_path_, |
| 220 error); |
| 221 } |
| 222 |
| 223 DeltaUpdateOpPatchCourgette::DeltaUpdateOpPatchCourgette() {} |
| 224 |
| 225 ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoParseArguments( |
| 226 base::DictionaryValue* command_args, |
| 227 const base::FilePath& input_dir, |
| 228 ComponentInstaller* installer) { |
| 229 std::string patch_rel_path; |
| 230 std::string input_rel_path; |
| 231 if (!command_args->GetString(kPatch, &patch_rel_path) || |
| 232 !command_args->GetString(kInput, &input_rel_path)) |
| 233 return ComponentUnpacker::kDeltaBadCommands; |
| 234 |
| 235 if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) |
| 236 return ComponentUnpacker::kDeltaMissingExistingFile; |
| 237 |
| 238 patch_abs_path_ = input_dir.Append( |
| 239 base::FilePath::FromUTF8Unsafe(patch_rel_path)); |
| 240 |
| 241 return ComponentUnpacker::kNone; |
| 242 } |
| 243 |
| 244 ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoRun( |
| 245 ComponentPatcher* patcher, |
| 246 int* error) { |
| 247 *error = 0; |
| 248 return patcher->Patch(ComponentPatcher::kPatchTypeCourgette, |
| 249 input_abs_path_, |
| 250 patch_abs_path_, |
| 251 output_abs_path_, |
| 252 error); |
| 253 } |
| 254 |
| 255 // Takes the contents of a differential component update in input_dir |
| 256 // and produces the contents of a full component update in unpack_dir |
| 257 // using input_abs_path_ files that the installer knows about. |
| 258 ComponentUnpacker::Error DifferentialUpdatePatch( |
| 259 const base::FilePath& input_dir, |
| 260 const base::FilePath& unpack_dir, |
| 261 ComponentPatcher* patcher, |
| 262 ComponentInstaller* installer, |
| 263 int* error) { |
| 264 *error = 0; |
| 265 scoped_ptr<base::ListValue> commands(ReadCommands(input_dir)); |
| 266 if (!commands.get()) |
| 267 return ComponentUnpacker::kDeltaBadCommands; |
| 268 |
| 269 for (base::ValueVector::const_iterator command = commands->begin(), |
| 270 end = commands->end(); command != end; command++) { |
| 271 if (!(*command)->IsType(base::Value::TYPE_DICTIONARY)) |
| 272 return ComponentUnpacker::kDeltaBadCommands; |
| 273 base::DictionaryValue* command_args = |
| 274 static_cast<base::DictionaryValue*>(*command); |
| 275 scoped_ptr<DeltaUpdateOp> operation(CreateDeltaUpdateOp(command_args)); |
| 276 if (!operation) |
| 277 return ComponentUnpacker::kDeltaUnsupportedCommand; |
| 278 |
| 279 ComponentUnpacker::Error result = operation->Run( |
| 280 command_args, input_dir, unpack_dir, patcher, installer, error); |
| 281 if (result != ComponentUnpacker::kNone) |
| 282 return result; |
| 283 } |
| 284 |
| 285 return ComponentUnpacker::kNone; |
| 286 } |
| 287 |
OLD | NEW |