| 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 |