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