OLD | NEW |
| (Empty) |
1 // Copyright 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_operation.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/file_util.h" | |
11 #include "base/files/memory_mapped_file.h" | |
12 #include "base/location.h" | |
13 #include "base/strings/string_number_conversions.h" | |
14 #include "chrome/browser/component_updater/component_patcher.h" | |
15 #include "chrome/browser/component_updater/component_updater_service.h" | |
16 #include "courgette/courgette.h" | |
17 #include "courgette/third_party/bsdiff.h" | |
18 #include "crypto/secure_hash.h" | |
19 #include "crypto/sha2.h" | |
20 #include "crypto/signature_verifier.h" | |
21 | |
22 using crypto::SecureHash; | |
23 | |
24 namespace component_updater { | |
25 | |
26 namespace { | |
27 | |
28 const char kOutput[] = "output"; | |
29 const char kSha256[] = "sha256"; | |
30 | |
31 // The integer offset disambiguates between overlapping error ranges. | |
32 const int kCourgetteErrorOffset = 300; | |
33 const int kBsdiffErrorOffset = 600; | |
34 | |
35 } // namespace | |
36 | |
37 const char kOp[] = "op"; | |
38 const char kBsdiff[] = "bsdiff"; | |
39 const char kCourgette[] = "courgette"; | |
40 const char kInput[] = "input"; | |
41 const char kPatch[] = "patch"; | |
42 | |
43 DeltaUpdateOp* CreateDeltaUpdateOp( | |
44 const std::string& operation, | |
45 scoped_refptr<OutOfProcessPatcher> out_of_process_patcher) { | |
46 if (operation == "copy") { | |
47 return new DeltaUpdateOpCopy(); | |
48 } else if (operation == "create") { | |
49 return new DeltaUpdateOpCreate(); | |
50 } else if (operation == "bsdiff" || operation == "courgette") { | |
51 return new DeltaUpdateOpPatch(operation, out_of_process_patcher); | |
52 } | |
53 return NULL; | |
54 } | |
55 | |
56 DeltaUpdateOp::DeltaUpdateOp() { | |
57 } | |
58 | |
59 DeltaUpdateOp::~DeltaUpdateOp() { | |
60 } | |
61 | |
62 void DeltaUpdateOp::Run(const base::DictionaryValue* command_args, | |
63 const base::FilePath& input_dir, | |
64 const base::FilePath& unpack_dir, | |
65 ComponentInstaller* installer, | |
66 const ComponentUnpacker::Callback& callback, | |
67 scoped_refptr<base::SequencedTaskRunner> task_runner) { | |
68 callback_ = callback; | |
69 task_runner_ = task_runner; | |
70 std::string output_rel_path; | |
71 if (!command_args->GetString(kOutput, &output_rel_path) || | |
72 !command_args->GetString(kSha256, &output_sha256_)) { | |
73 DoneRunning(ComponentUnpacker::kDeltaBadCommands, 0); | |
74 return; | |
75 } | |
76 | |
77 output_abs_path_ = | |
78 unpack_dir.Append(base::FilePath::FromUTF8Unsafe(output_rel_path)); | |
79 ComponentUnpacker::Error parse_result = | |
80 DoParseArguments(command_args, input_dir, installer); | |
81 if (parse_result != ComponentUnpacker::kNone) { | |
82 DoneRunning(parse_result, 0); | |
83 return; | |
84 } | |
85 | |
86 const base::FilePath parent = output_abs_path_.DirName(); | |
87 if (!base::DirectoryExists(parent)) { | |
88 if (!base::CreateDirectory(parent)) { | |
89 DoneRunning(ComponentUnpacker::kIoError, 0); | |
90 return; | |
91 } | |
92 } | |
93 | |
94 DoRun(base::Bind(&DeltaUpdateOp::DoneRunning, | |
95 scoped_refptr<DeltaUpdateOp>(this))); | |
96 } | |
97 | |
98 void DeltaUpdateOp::DoneRunning(ComponentUnpacker::Error error, | |
99 int extended_error) { | |
100 if (error == ComponentUnpacker::kNone) | |
101 error = CheckHash(); | |
102 task_runner_->PostTask(FROM_HERE, | |
103 base::Bind(callback_, error, extended_error)); | |
104 callback_.Reset(); | |
105 } | |
106 | |
107 // Uses the hash as a checksum to confirm that the file now residing in the | |
108 // output directory probably has the contents it should. | |
109 ComponentUnpacker::Error DeltaUpdateOp::CheckHash() { | |
110 std::vector<uint8> expected_hash; | |
111 if (!base::HexStringToBytes(output_sha256_, &expected_hash) || | |
112 expected_hash.size() != crypto::kSHA256Length) | |
113 return ComponentUnpacker::kDeltaVerificationFailure; | |
114 | |
115 base::MemoryMappedFile output_file_mmapped; | |
116 if (!output_file_mmapped.Initialize(output_abs_path_)) | |
117 return ComponentUnpacker::kDeltaVerificationFailure; | |
118 | |
119 uint8 actual_hash[crypto::kSHA256Length] = {0}; | |
120 const scoped_ptr<SecureHash> hasher(SecureHash::Create(SecureHash::SHA256)); | |
121 hasher->Update(output_file_mmapped.data(), output_file_mmapped.length()); | |
122 hasher->Finish(actual_hash, sizeof(actual_hash)); | |
123 if (memcmp(actual_hash, &expected_hash[0], sizeof(actual_hash))) | |
124 return ComponentUnpacker::kDeltaVerificationFailure; | |
125 | |
126 return ComponentUnpacker::kNone; | |
127 } | |
128 | |
129 scoped_refptr<base::SequencedTaskRunner> DeltaUpdateOp::GetTaskRunner() { | |
130 return task_runner_; | |
131 } | |
132 | |
133 DeltaUpdateOpCopy::DeltaUpdateOpCopy() { | |
134 } | |
135 | |
136 DeltaUpdateOpCopy::~DeltaUpdateOpCopy() { | |
137 } | |
138 | |
139 ComponentUnpacker::Error DeltaUpdateOpCopy::DoParseArguments( | |
140 const base::DictionaryValue* command_args, | |
141 const base::FilePath& input_dir, | |
142 ComponentInstaller* installer) { | |
143 std::string input_rel_path; | |
144 if (!command_args->GetString(kInput, &input_rel_path)) | |
145 return ComponentUnpacker::kDeltaBadCommands; | |
146 | |
147 if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) | |
148 return ComponentUnpacker::kDeltaMissingExistingFile; | |
149 | |
150 return ComponentUnpacker::kNone; | |
151 } | |
152 | |
153 void DeltaUpdateOpCopy::DoRun(const ComponentUnpacker::Callback& callback) { | |
154 if (!base::CopyFile(input_abs_path_, output_abs_path_)) | |
155 callback.Run(ComponentUnpacker::kDeltaOperationFailure, 0); | |
156 else | |
157 callback.Run(ComponentUnpacker::kNone, 0); | |
158 } | |
159 | |
160 DeltaUpdateOpCreate::DeltaUpdateOpCreate() { | |
161 } | |
162 | |
163 DeltaUpdateOpCreate::~DeltaUpdateOpCreate() { | |
164 } | |
165 | |
166 ComponentUnpacker::Error DeltaUpdateOpCreate::DoParseArguments( | |
167 const base::DictionaryValue* command_args, | |
168 const base::FilePath& input_dir, | |
169 ComponentInstaller* installer) { | |
170 std::string patch_rel_path; | |
171 if (!command_args->GetString(kPatch, &patch_rel_path)) | |
172 return ComponentUnpacker::kDeltaBadCommands; | |
173 | |
174 patch_abs_path_ = | |
175 input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path)); | |
176 | |
177 return ComponentUnpacker::kNone; | |
178 } | |
179 | |
180 void DeltaUpdateOpCreate::DoRun(const ComponentUnpacker::Callback& callback) { | |
181 if (!base::Move(patch_abs_path_, output_abs_path_)) | |
182 callback.Run(ComponentUnpacker::kDeltaOperationFailure, 0); | |
183 else | |
184 callback.Run(ComponentUnpacker::kNone, 0); | |
185 } | |
186 | |
187 DeltaUpdateOpPatch::DeltaUpdateOpPatch( | |
188 const std::string& operation, | |
189 scoped_refptr<OutOfProcessPatcher> out_of_process_patcher) | |
190 : operation_(operation), out_of_process_patcher_(out_of_process_patcher) { | |
191 DCHECK(operation == kBsdiff || operation == kCourgette); | |
192 } | |
193 | |
194 DeltaUpdateOpPatch::~DeltaUpdateOpPatch() { | |
195 } | |
196 | |
197 ComponentUnpacker::Error DeltaUpdateOpPatch::DoParseArguments( | |
198 const base::DictionaryValue* command_args, | |
199 const base::FilePath& input_dir, | |
200 ComponentInstaller* installer) { | |
201 std::string patch_rel_path; | |
202 std::string input_rel_path; | |
203 if (!command_args->GetString(kPatch, &patch_rel_path) || | |
204 !command_args->GetString(kInput, &input_rel_path)) | |
205 return ComponentUnpacker::kDeltaBadCommands; | |
206 | |
207 if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_)) | |
208 return ComponentUnpacker::kDeltaMissingExistingFile; | |
209 | |
210 patch_abs_path_ = | |
211 input_dir.Append(base::FilePath::FromUTF8Unsafe(patch_rel_path)); | |
212 | |
213 return ComponentUnpacker::kNone; | |
214 } | |
215 | |
216 void DeltaUpdateOpPatch::DoRun(const ComponentUnpacker::Callback& callback) { | |
217 if (out_of_process_patcher_.get()) { | |
218 out_of_process_patcher_->Patch( | |
219 operation_, | |
220 GetTaskRunner(), | |
221 input_abs_path_, | |
222 patch_abs_path_, | |
223 output_abs_path_, | |
224 base::Bind(&DeltaUpdateOpPatch::DonePatching, this, callback)); | |
225 return; | |
226 } | |
227 | |
228 if (operation_ == kBsdiff) { | |
229 DonePatching(callback, | |
230 courgette::ApplyBinaryPatch( | |
231 input_abs_path_, patch_abs_path_, output_abs_path_)); | |
232 } else if (operation_ == kCourgette) { | |
233 DonePatching( | |
234 callback, | |
235 courgette::ApplyEnsemblePatch(input_abs_path_.value().c_str(), | |
236 patch_abs_path_.value().c_str(), | |
237 output_abs_path_.value().c_str())); | |
238 } else { | |
239 NOTREACHED(); | |
240 } | |
241 } | |
242 | |
243 void DeltaUpdateOpPatch::DonePatching( | |
244 const ComponentUnpacker::Callback& callback, | |
245 int result) { | |
246 if (operation_ == kBsdiff) { | |
247 if (result == courgette::OK) { | |
248 callback.Run(ComponentUnpacker::kNone, 0); | |
249 } else { | |
250 callback.Run(ComponentUnpacker::kDeltaOperationFailure, | |
251 result + kBsdiffErrorOffset); | |
252 } | |
253 } else if (operation_ == kCourgette) { | |
254 if (result == courgette::C_OK) { | |
255 callback.Run(ComponentUnpacker::kNone, 0); | |
256 } else { | |
257 callback.Run(ComponentUnpacker::kDeltaOperationFailure, | |
258 result + kCourgetteErrorOffset); | |
259 } | |
260 } else { | |
261 NOTREACHED(); | |
262 } | |
263 } | |
264 | |
265 } // namespace component_updater | |
OLD | NEW |