OLD | NEW |
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "update_engine/delta_performer.h" | 5 #include "update_engine/delta_performer.h" |
| 6 |
6 #include <endian.h> | 7 #include <endian.h> |
7 #include <errno.h> | 8 #include <errno.h> |
| 9 |
8 #include <algorithm> | 10 #include <algorithm> |
9 #include <cstring> | 11 #include <cstring> |
10 #include <string> | 12 #include <string> |
11 #include <vector> | 13 #include <vector> |
12 | 14 |
| 15 #include <base/scoped_ptr.h> |
| 16 #include <base/string_util.h> |
13 #include <google/protobuf/repeated_field.h> | 17 #include <google/protobuf/repeated_field.h> |
14 | 18 |
15 #include "base/scoped_ptr.h" | |
16 #include "base/string_util.h" | |
17 #include "update_engine/bzip_extent_writer.h" | 19 #include "update_engine/bzip_extent_writer.h" |
18 #include "update_engine/delta_diff_generator.h" | 20 #include "update_engine/delta_diff_generator.h" |
19 #include "update_engine/extent_writer.h" | 21 #include "update_engine/extent_writer.h" |
20 #include "update_engine/graph_types.h" | 22 #include "update_engine/graph_types.h" |
| 23 #include "update_engine/payload_signer.h" |
21 #include "update_engine/subprocess.h" | 24 #include "update_engine/subprocess.h" |
22 | 25 |
23 using std::min; | 26 using std::min; |
24 using std::string; | 27 using std::string; |
25 using std::vector; | 28 using std::vector; |
26 using google::protobuf::RepeatedPtrField; | 29 using google::protobuf::RepeatedPtrField; |
27 | 30 |
28 namespace chromeos_update_engine { | 31 namespace chromeos_update_engine { |
29 | 32 |
30 namespace { | 33 namespace { |
31 | 34 |
32 const int kDeltaVersionLength = 8; | 35 const int kDeltaVersionLength = 8; |
33 const int kDeltaProtobufLengthLength = 8; | 36 const int kDeltaProtobufLengthLength = 8; |
34 | 37 const char kUpdatePayloadPublicKeyPath[] = |
35 // Remove count bytes from the beginning of *buffer. | 38 "/usr/share/update_engine/update-payload-key.pub.pem"; |
36 void RemoveBufferHeadBytes(vector<char>* buffer, size_t count) { | |
37 buffer->erase(buffer->begin(), buffer->begin() + count); | |
38 } | |
39 | 39 |
40 // Converts extents to a human-readable string, for use by DumpUpdateProto(). | 40 // Converts extents to a human-readable string, for use by DumpUpdateProto(). |
41 string ExtentsToString(const RepeatedPtrField<Extent>& extents) { | 41 string ExtentsToString(const RepeatedPtrField<Extent>& extents) { |
42 string ret; | 42 string ret; |
43 for (int i = 0; i < extents.size(); i++) { | 43 for (int i = 0; i < extents.size(); i++) { |
44 const Extent& extent = extents.Get(i); | 44 const Extent& extent = extents.Get(i); |
45 if (extent.start_block() == kSparseHole) { | 45 if (extent.start_block() == kSparseHole) { |
46 ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks()); | 46 ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks()); |
47 } else { | 47 } else { |
48 ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ", | 48 ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ", |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 } | 129 } |
130 int err = 0; | 130 int err = 0; |
131 if (close(kernel_fd_) == -1) { | 131 if (close(kernel_fd_) == -1) { |
132 err = errno; | 132 err = errno; |
133 PLOG(ERROR) << "Unable to close kernel fd:"; | 133 PLOG(ERROR) << "Unable to close kernel fd:"; |
134 } | 134 } |
135 if (close(fd_) == -1) { | 135 if (close(fd_) == -1) { |
136 err = errno; | 136 err = errno; |
137 PLOG(ERROR) << "Unable to close rootfs fd:"; | 137 PLOG(ERROR) << "Unable to close rootfs fd:"; |
138 } | 138 } |
| 139 LOG_IF(ERROR, !hash_calculator_.Finalize()) << "Unable to finalize the hash."; |
139 fd_ = -2; // Set so that isn't not valid AND calls to Open() will fail. | 140 fd_ = -2; // Set so that isn't not valid AND calls to Open() will fail. |
140 path_ = ""; | 141 path_ = ""; |
141 return -err; | 142 return -err; |
142 } | 143 } |
143 | 144 |
144 // Wrapper around write. Returns bytes written on success or | 145 // Wrapper around write. Returns bytes written on success or |
145 // -errno on error. | 146 // -errno on error. |
146 // This function performs as many actions as it can, given the amount of | 147 // This function performs as many actions as it can, given the amount of |
147 // data received thus far. | 148 // data received thus far. |
148 ssize_t DeltaPerformer::Write(const void* bytes, size_t count) { | 149 ssize_t DeltaPerformer::Write(const void* bytes, size_t count) { |
(...skipping 20 matching lines...) Expand all Loading... |
169 } | 170 } |
170 // We have the full proto buffer in buffer_. Parse it. | 171 // We have the full proto buffer in buffer_. Parse it. |
171 const int offset = strlen(kDeltaMagic) + kDeltaVersionLength + | 172 const int offset = strlen(kDeltaMagic) + kDeltaVersionLength + |
172 kDeltaProtobufLengthLength; | 173 kDeltaProtobufLengthLength; |
173 if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) { | 174 if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) { |
174 LOG(ERROR) << "Unable to parse manifest in update file."; | 175 LOG(ERROR) << "Unable to parse manifest in update file."; |
175 return -EINVAL; | 176 return -EINVAL; |
176 } | 177 } |
177 // Remove protobuf and header info from buffer_, so buffer_ contains | 178 // Remove protobuf and header info from buffer_, so buffer_ contains |
178 // just data blobs | 179 // just data blobs |
179 RemoveBufferHeadBytes(&buffer_, | 180 DiscardBufferHeadBytes(strlen(kDeltaMagic) + |
180 strlen(kDeltaMagic) + | 181 kDeltaVersionLength + |
181 kDeltaVersionLength + | 182 kDeltaProtobufLengthLength + protobuf_length, |
182 kDeltaProtobufLengthLength + protobuf_length); | 183 true); // do_hash |
183 manifest_valid_ = true; | 184 manifest_valid_ = true; |
184 block_size_ = manifest_.block_size(); | 185 block_size_ = manifest_.block_size(); |
185 } | 186 } |
186 ssize_t total_operations = manifest_.install_operations_size() + | 187 ssize_t total_operations = manifest_.install_operations_size() + |
187 manifest_.kernel_install_operations_size(); | 188 manifest_.kernel_install_operations_size(); |
188 while (next_operation_num_ < total_operations) { | 189 while (next_operation_num_ < total_operations) { |
189 const DeltaArchiveManifest_InstallOperation &op = | 190 const DeltaArchiveManifest_InstallOperation &op = |
190 next_operation_num_ < manifest_.install_operations_size() ? | 191 next_operation_num_ < manifest_.install_operations_size() ? |
191 manifest_.install_operations(next_operation_num_) : | 192 manifest_.install_operations(next_operation_num_) : |
192 manifest_.kernel_install_operations( | 193 manifest_.kernel_install_operations( |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 // Move operations don't require any data blob, so they can always | 233 // Move operations don't require any data blob, so they can always |
233 // be performed | 234 // be performed |
234 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) | 235 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) |
235 return true; | 236 return true; |
236 | 237 |
237 // See if we have the entire data blob in the buffer | 238 // See if we have the entire data blob in the buffer |
238 if (operation.data_offset() < buffer_offset_) { | 239 if (operation.data_offset() < buffer_offset_) { |
239 LOG(ERROR) << "we threw away data it seems?"; | 240 LOG(ERROR) << "we threw away data it seems?"; |
240 return false; | 241 return false; |
241 } | 242 } |
242 | 243 |
243 return (operation.data_offset() + operation.data_length()) <= | 244 return (operation.data_offset() + operation.data_length()) <= |
244 (buffer_offset_ + buffer_.size()); | 245 (buffer_offset_ + buffer_.size()); |
245 } | 246 } |
246 | 247 |
247 bool DeltaPerformer::PerformReplaceOperation( | 248 bool DeltaPerformer::PerformReplaceOperation( |
248 const DeltaArchiveManifest_InstallOperation& operation, | 249 const DeltaArchiveManifest_InstallOperation& operation, |
249 bool is_kernel_partition) { | 250 bool is_kernel_partition) { |
250 CHECK(operation.type() == \ | 251 CHECK(operation.type() == \ |
251 DeltaArchiveManifest_InstallOperation_Type_REPLACE || \ | 252 DeltaArchiveManifest_InstallOperation_Type_REPLACE || \ |
252 operation.type() == \ | 253 operation.type() == \ |
253 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); | 254 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); |
254 | 255 |
255 // Since we delete data off the beginning of the buffer as we use it, | 256 // Since we delete data off the beginning of the buffer as we use it, |
256 // the data we need should be exactly at the beginning of the buffer. | 257 // the data we need should be exactly at the beginning of the buffer. |
257 CHECK_EQ(buffer_offset_, operation.data_offset()); | 258 CHECK_EQ(buffer_offset_, operation.data_offset()); |
258 CHECK_GE(buffer_.size(), operation.data_length()); | 259 CHECK_GE(buffer_.size(), operation.data_length()); |
259 | 260 |
| 261 // Don't include the signature data blob in the hash. |
| 262 bool do_hash = !ExtractSignatureMessage(operation); |
| 263 |
260 DirectExtentWriter direct_writer; | 264 DirectExtentWriter direct_writer; |
261 ZeroPadExtentWriter zero_pad_writer(&direct_writer); | 265 ZeroPadExtentWriter zero_pad_writer(&direct_writer); |
262 scoped_ptr<BzipExtentWriter> bzip_writer; | 266 scoped_ptr<BzipExtentWriter> bzip_writer; |
263 | 267 |
264 // Since bzip decompression is optional, we have a variable writer that will | 268 // Since bzip decompression is optional, we have a variable writer that will |
265 // point to one of the ExtentWriter objects above. | 269 // point to one of the ExtentWriter objects above. |
266 ExtentWriter* writer = NULL; | 270 ExtentWriter* writer = NULL; |
267 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) { | 271 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) { |
268 writer = &zero_pad_writer; | 272 writer = &zero_pad_writer; |
269 } else if (operation.type() == | 273 } else if (operation.type() == |
270 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { | 274 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { |
271 bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer)); | 275 bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer)); |
272 writer = bzip_writer.get(); | 276 writer = bzip_writer.get(); |
273 } else { | 277 } else { |
274 NOTREACHED(); | 278 NOTREACHED(); |
275 } | 279 } |
276 | 280 |
277 // Create a vector of extents to pass to the ExtentWriter. | 281 // Create a vector of extents to pass to the ExtentWriter. |
278 vector<Extent> extents; | 282 vector<Extent> extents; |
279 for (int i = 0; i < operation.dst_extents_size(); i++) { | 283 for (int i = 0; i < operation.dst_extents_size(); i++) { |
280 extents.push_back(operation.dst_extents(i)); | 284 extents.push_back(operation.dst_extents(i)); |
281 } | 285 } |
282 | 286 |
283 int fd = is_kernel_partition ? kernel_fd_ : fd_; | 287 int fd = is_kernel_partition ? kernel_fd_ : fd_; |
284 | 288 |
285 TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_)); | 289 TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_)); |
286 TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length())); | 290 TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length())); |
287 TEST_AND_RETURN_FALSE(writer->End()); | 291 TEST_AND_RETURN_FALSE(writer->End()); |
288 | 292 |
289 // Update buffer | 293 // Update buffer |
290 buffer_offset_ += operation.data_length(); | 294 buffer_offset_ += operation.data_length(); |
291 RemoveBufferHeadBytes(&buffer_, operation.data_length()); | 295 DiscardBufferHeadBytes(operation.data_length(), do_hash); |
292 return true; | 296 return true; |
293 } | 297 } |
294 | 298 |
295 bool DeltaPerformer::PerformMoveOperation( | 299 bool DeltaPerformer::PerformMoveOperation( |
296 const DeltaArchiveManifest_InstallOperation& operation, | 300 const DeltaArchiveManifest_InstallOperation& operation, |
297 bool is_kernel_partition) { | 301 bool is_kernel_partition) { |
298 // Calculate buffer size. Note, this function doesn't do a sliding | 302 // Calculate buffer size. Note, this function doesn't do a sliding |
299 // window to copy in case the source and destination blocks overlap. | 303 // window to copy in case the source and destination blocks overlap. |
300 // If we wanted to do a sliding window, we could program the server | 304 // If we wanted to do a sliding window, we could program the server |
301 // to generate deltas that effectively did a sliding window. | 305 // to generate deltas that effectively did a sliding window. |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 (last_extent.start_block() + last_extent.num_blocks()) * block_size_; | 428 (last_extent.start_block() + last_extent.num_blocks()) * block_size_; |
425 const uint64_t begin_byte = | 429 const uint64_t begin_byte = |
426 end_byte - (block_size_ - operation.dst_length() % block_size_); | 430 end_byte - (block_size_ - operation.dst_length() % block_size_); |
427 vector<char> zeros(end_byte - begin_byte); | 431 vector<char> zeros(end_byte - begin_byte); |
428 TEST_AND_RETURN_FALSE( | 432 TEST_AND_RETURN_FALSE( |
429 utils::PWriteAll(fd, &zeros[0], end_byte - begin_byte, begin_byte)); | 433 utils::PWriteAll(fd, &zeros[0], end_byte - begin_byte, begin_byte)); |
430 } | 434 } |
431 | 435 |
432 // Update buffer. | 436 // Update buffer. |
433 buffer_offset_ += operation.data_length(); | 437 buffer_offset_ += operation.data_length(); |
434 RemoveBufferHeadBytes(&buffer_, operation.data_length()); | 438 DiscardBufferHeadBytes(operation.data_length(), |
| 439 true); // do_hash |
435 return true; | 440 return true; |
436 } | 441 } |
437 | 442 |
| 443 bool DeltaPerformer::ExtractSignatureMessage( |
| 444 const DeltaArchiveManifest_InstallOperation& operation) { |
| 445 if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_REPLACE || |
| 446 !manifest_.has_signatures_offset() || |
| 447 manifest_.signatures_offset() != operation.data_offset()) { |
| 448 return false; |
| 449 } |
| 450 TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() && |
| 451 manifest_.signatures_size() == operation.data_length()); |
| 452 TEST_AND_RETURN_FALSE(signatures_message_data_.empty()); |
| 453 TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset()); |
| 454 TEST_AND_RETURN_FALSE(buffer_.size() >= manifest_.signatures_size()); |
| 455 signatures_message_data_.insert( |
| 456 signatures_message_data_.begin(), |
| 457 buffer_.begin(), |
| 458 buffer_.begin() + manifest_.signatures_size()); |
| 459 LOG(INFO) << "Extracted signature data of size " |
| 460 << manifest_.signatures_size() << " at " |
| 461 << manifest_.signatures_offset(); |
| 462 return true; |
| 463 } |
| 464 |
| 465 bool DeltaPerformer::VerifyPayload(const string& public_key_path) { |
| 466 string key_path = public_key_path; |
| 467 if (key_path.empty()) { |
| 468 key_path = kUpdatePayloadPublicKeyPath; |
| 469 } |
| 470 LOG(INFO) << "Verifying delta payload. Public key path: " << key_path; |
| 471 if (!utils::FileExists(key_path.c_str())) { |
| 472 LOG(WARNING) << "Not verifying delta payload due to missing public key."; |
| 473 return true; |
| 474 } |
| 475 TEST_AND_RETURN_FALSE(!signatures_message_data_.empty()); |
| 476 vector<char> signed_hash_data; |
| 477 TEST_AND_RETURN_FALSE(PayloadSigner::VerifySignature(signatures_message_data_, |
| 478 key_path, |
| 479 &signed_hash_data)); |
| 480 const vector<char>& hash_data = hash_calculator_.raw_hash(); |
| 481 TEST_AND_RETURN_FALSE(!hash_data.empty()); |
| 482 return hash_data == signed_hash_data; |
| 483 } |
| 484 |
| 485 void DeltaPerformer::DiscardBufferHeadBytes(size_t count, bool do_hash) { |
| 486 if (do_hash) { |
| 487 hash_calculator_.Update(&buffer_[0], count); |
| 488 } |
| 489 buffer_.erase(buffer_.begin(), buffer_.begin() + count); |
| 490 } |
| 491 |
438 } // namespace chromeos_update_engine | 492 } // namespace chromeos_update_engine |
OLD | NEW |