Chromium Code Reviews| 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 JSONFileValueSerializer serializer(commands); | |
| 58 std::string error; | |
|
robertshield
2013/06/17 17:47:00
error isn't used, pass NULL to Deserialize instead
Sorin Jianu
2013/06/17 22:21:09
Done.
| |
| 59 scoped_ptr<base::Value> root(serializer.Deserialize(NULL, &error)); | |
| 60 if (!root.get()) | |
| 61 return NULL; | |
| 62 if (!root->IsType(base::Value::TYPE_LIST)) | |
| 63 return NULL; | |
| 64 return static_cast<base::ListValue*>(root.release()); | |
|
robertshield
2013/06/17 17:47:00
Could shorten the last five lines to:
return (roo
Sorin Jianu
2013/06/17 22:21:09
I prefer to keep it as is instead of the terse exp
robertshield
2013/06/18 13:27:07
How about a compromise:
if (!root.get()) || !root
Sorin Jianu
2013/06/18 17:00:11
Thank you Robert. Given the choice, I decided to f
| |
| 65 } | |
| 66 | |
| 67 } // namespace. | |
| 68 | |
| 69 | |
| 70 // The patching support is not cross-platform at the moment. | |
| 71 ComponentPatcherCrossPlatform::ComponentPatcherCrossPlatform() {} | |
| 72 | |
| 73 ComponentUnpacker::Error ComponentPatcherCrossPlatform::Patch( | |
| 74 PatchType patch_type, | |
| 75 const base::FilePath& input_file, | |
| 76 const base::FilePath& patch_file, | |
| 77 const base::FilePath& output_file, | |
| 78 int* error) { | |
| 79 return ComponentUnpacker::kDeltaOperationFailure; | |
|
cpu_(ooo_6.6-7.5)
2013/06/14 23:25:28
return kDeltaOperationUnsuported ?
waffles
2013/06/15 00:01:37
Done; will be in the next patch set.
| |
| 80 } | |
| 81 | |
| 82 DeltaUpdateOp::~DeltaUpdateOp() {} | |
| 83 | |
| 84 DeltaUpdateOp::DeltaUpdateOp() {} | |
|
robertshield
2013/06/17 17:47:00
This is a relatively unconventional file structure
Sorin Jianu
2013/06/18 03:05:14
Done. Introduced component_patcher_operation.[h|cc
| |
| 85 | |
| 86 ComponentUnpacker::Error DeltaUpdateOp::Run(base::DictionaryValue* command_args, | |
| 87 const base::FilePath& input_dir, | |
| 88 const base::FilePath& unpack_dir, | |
| 89 ComponentPatcher* patcher, | |
| 90 ComponentInstaller* installer, | |
| 91 int* error) { | |
|
cpu_(ooo_6.6-7.5)
2013/06/17 20:27:01
After trying to like the int* error (and its frien
Sorin Jianu
2013/06/17 22:21:09
The component patcher can give us valuable error i
| |
| 92 std::string output_rel_path; | |
| 93 if (!command_args->GetString(kOutput, &output_rel_path) || | |
| 94 !command_args->GetString(kSha256, &output_sha256_)) | |
| 95 return ComponentUnpacker::kDeltaBadCommands; | |
| 96 output_abs_path_ = unpack_dir.Append( | |
| 97 base::FilePath::FromUTF8Unsafe(output_rel_path)); | |
| 98 ComponentUnpacker::Error parse_result = DoParseArguments( | |
| 99 command_args, input_dir, installer); | |
| 100 if (parse_result != ComponentUnpacker::kNone) | |
| 101 return parse_result; | |
| 102 | |
| 103 const base::FilePath parent = output_abs_path_.DirName(); | |
| 104 if (!file_util::DirectoryExists(parent)) { | |
| 105 if (!file_util::CreateDirectory(parent)) | |
| 106 return ComponentUnpacker::kIoError; | |
| 107 } | |
| 108 | |
| 109 ComponentUnpacker::Error run_result = DoRun(patcher, error); | |
| 110 if (run_result != ComponentUnpacker::kNone) | |
| 111 return run_result; | |
| 112 return CheckHash(); | |
| 113 } | |
| 114 | |
| 115 // Uses the hash as a checksum to confirm that the file now residing in the | |
| 116 // output directory probably has the contents it should. | |
| 117 ComponentUnpacker::Error DeltaUpdateOp::CheckHash() { | |
| 118 std::vector<uint8> expected_hash(0); | |
|
robertshield
2013/06/17 17:47:00
remove the (0), it is redundant
Sorin Jianu
2013/06/17 22:21:09
Done.
| |
| 119 if (!base::HexStringToBytes(output_sha256_, &expected_hash) || | |
| 120 expected_hash.size() != crypto::kSHA256Length) | |
| 121 return ComponentUnpacker::kDeltaVerificationFailure; | |
|
robertshield
2013/06/17 17:47:00
nit: I find this code a little too dense, and woul
Sorin Jianu
2013/06/17 22:21:09
Done.
| |
| 122 uint8 actual_hash[crypto::kSHA256Length] = {0}; | |
| 123 base::MemoryMappedFile output_file_mmapped; | |
| 124 if (!output_file_mmapped.Initialize(output_abs_path_)) | |
| 125 return ComponentUnpacker::kDeltaVerificationFailure; | |
| 126 const scoped_ptr<SecureHash> hasher(SecureHash::Create(SecureHash::SHA256)); | |
| 127 hasher->Update(output_file_mmapped.data(), output_file_mmapped.length()); | |
| 128 hasher->Finish(actual_hash, sizeof(actual_hash)); | |
| 129 if (memcmp(actual_hash, &expected_hash[0], crypto::kSHA256Length)) | |
|
robertshield
2013/06/17 17:47:00
crypto::kSHA256Length -> arraysize(actual_hash)
Sorin Jianu
2013/06/17 22:21:09
replaced it with sizeof since we are doing a byte-
| |
| 130 return ComponentUnpacker::kDeltaVerificationFailure; | |
| 131 return ComponentUnpacker::kNone; | |
| 132 } | |
| 133 | |
| 134 DeltaUpdateOpCopy::DeltaUpdateOpCopy() {} | |
| 135 | |
| 136 ComponentUnpacker::Error DeltaUpdateOpCopy::DoParseArguments( | |
| 137 base::DictionaryValue* command_args, | |
| 138 const base::FilePath& input_dir, | |
| 139 ComponentInstaller* installer) { | |
| 140 std::string input_rel_path; | |
| 141 if (!command_args->GetString(kInput, &input_rel_path)) | |
| 142 return ComponentUnpacker::kDeltaBadCommands; | |
| 143 if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) | |
| 144 return ComponentUnpacker::kDeltaMissingExistingFile; | |
| 145 return ComponentUnpacker::kNone; | |
| 146 } | |
| 147 | |
| 148 ComponentUnpacker::Error DeltaUpdateOpCopy::DoRun(ComponentPatcher*, | |
| 149 int* error) { | |
| 150 *error = 0; | |
| 151 if (!file_util::CopyFile(input_abs_path_, output_abs_path_)) | |
| 152 return ComponentUnpacker::kDeltaOperationFailure; | |
| 153 return ComponentUnpacker::kNone; | |
| 154 } | |
| 155 | |
| 156 DeltaUpdateOpCreate::DeltaUpdateOpCreate() {} | |
| 157 | |
| 158 ComponentUnpacker::Error DeltaUpdateOpCreate::DoParseArguments( | |
| 159 base::DictionaryValue* command_args, | |
| 160 const base::FilePath& input_dir, | |
| 161 ComponentInstaller* installer) { | |
| 162 std::string patch_rel_path; | |
| 163 if (!command_args->GetString(kPatch, &patch_rel_path)) | |
| 164 return ComponentUnpacker::kDeltaBadCommands; | |
| 165 patch_abs_path_ = input_dir.Append( | |
| 166 base::FilePath::FromUTF8Unsafe(patch_rel_path)); | |
| 167 return ComponentUnpacker::kNone; | |
| 168 } | |
| 169 | |
| 170 ComponentUnpacker::Error DeltaUpdateOpCreate::DoRun(ComponentPatcher*, | |
| 171 int* error) { | |
| 172 *error = 0; | |
| 173 if (!file_util::Move(patch_abs_path_, output_abs_path_)) | |
| 174 return ComponentUnpacker::kDeltaOperationFailure; | |
| 175 return ComponentUnpacker::kNone; | |
| 176 } | |
| 177 | |
| 178 DeltaUpdateOpPatchBsdiff::DeltaUpdateOpPatchBsdiff() {} | |
| 179 | |
| 180 ComponentUnpacker::Error DeltaUpdateOpPatchBsdiff::DoParseArguments( | |
| 181 base::DictionaryValue* command_args, | |
| 182 const base::FilePath& input_dir, | |
| 183 ComponentInstaller* installer) { | |
| 184 std::string patch_rel_path; | |
| 185 std::string input_rel_path; | |
| 186 if (!command_args->GetString(kPatch, &patch_rel_path) || | |
| 187 !command_args->GetString(kInput, &input_rel_path)) | |
| 188 return ComponentUnpacker::kDeltaBadCommands; | |
| 189 if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) | |
| 190 return ComponentUnpacker::kDeltaMissingExistingFile; | |
| 191 patch_abs_path_ = input_dir.Append( | |
| 192 base::FilePath::FromUTF8Unsafe(patch_rel_path)); | |
| 193 return ComponentUnpacker::kNone; | |
| 194 } | |
| 195 | |
| 196 ComponentUnpacker::Error DeltaUpdateOpPatchBsdiff::DoRun( | |
| 197 ComponentPatcher* patcher, | |
| 198 int* error) { | |
| 199 *error = 0; | |
| 200 return patcher->Patch(kPatchTypeBsdiff, | |
| 201 input_abs_path_, | |
| 202 patch_abs_path_, | |
| 203 output_abs_path_, | |
| 204 error); | |
| 205 } | |
| 206 | |
| 207 DeltaUpdateOpPatchCourgette::DeltaUpdateOpPatchCourgette() {} | |
| 208 | |
| 209 ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoParseArguments( | |
| 210 base::DictionaryValue* command_args, | |
| 211 const base::FilePath& input_dir, | |
| 212 ComponentInstaller* installer) { | |
| 213 std::string patch_rel_path; | |
| 214 std::string input_rel_path; | |
| 215 if (!command_args->GetString(kPatch, &patch_rel_path) || | |
| 216 !command_args->GetString(kInput, &input_rel_path)) | |
| 217 return ComponentUnpacker::kDeltaBadCommands; | |
| 218 if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) | |
| 219 return ComponentUnpacker::kDeltaMissingExistingFile; | |
| 220 patch_abs_path_ = input_dir.Append( | |
| 221 base::FilePath::FromUTF8Unsafe(patch_rel_path)); | |
| 222 return ComponentUnpacker::kNone; | |
| 223 } | |
| 224 | |
| 225 ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoRun( | |
| 226 ComponentPatcher* patcher, | |
| 227 int* error) { | |
| 228 *error = 0; | |
| 229 return patcher->Patch(kPatchTypeCourgette, | |
| 230 input_abs_path_, | |
| 231 patch_abs_path_, | |
| 232 output_abs_path_, | |
| 233 error); | |
| 234 } | |
| 235 | |
| 236 // Takes the contents of a differential component update in input_dir | |
| 237 // and produces the contents of a full component update in unpack_dir | |
| 238 // using input_abs_path_ files that the installer knows about. | |
| 239 ComponentUnpacker::Error DifferentialUpdatePatch( | |
| 240 const base::FilePath& input_dir, | |
| 241 const base::FilePath& unpack_dir, | |
| 242 ComponentPatcher* patcher, | |
| 243 ComponentInstaller* installer, | |
| 244 int* error) { | |
| 245 *error = 0; | |
| 246 scoped_ptr<base::ListValue> commands(ReadCommands(input_dir)); | |
| 247 if (!commands.get()) | |
| 248 return ComponentUnpacker::kDeltaBadCommands; | |
| 249 for (base::ValueVector::const_iterator command = commands->begin(), | |
| 250 end = commands->end(); command != end; command++) { | |
| 251 if (!(*command)->IsType(base::Value::TYPE_DICTIONARY)) | |
| 252 return ComponentUnpacker::kDeltaBadCommands; | |
| 253 base::DictionaryValue* command_args = | |
| 254 static_cast<base::DictionaryValue*>(*command); | |
| 255 scoped_ptr<DeltaUpdateOp> operation(CreateDeltaUpdateOp(command_args)); | |
| 256 if (!operation) | |
| 257 return ComponentUnpacker::kDeltaUnsupportedCommand; | |
| 258 ComponentUnpacker::Error result = operation->Run( | |
| 259 command_args, input_dir, unpack_dir, patcher, installer, error); | |
| 260 if (result != ComponentUnpacker::kNone) | |
| 261 return result; | |
| 262 } | |
| 263 return ComponentUnpacker::kNone; | |
| 264 } | |
| 265 | |
| OLD | NEW |