| 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 |
| 7 #include <endian.h> | 7 #include <endian.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 #include <cstring> | 11 #include <cstring> |
| 12 #include <string> | 12 #include <string> |
| 13 #include <vector> | 13 #include <vector> |
| 14 | 14 |
| 15 #include <base/scoped_ptr.h> | 15 #include <base/scoped_ptr.h> |
| 16 #include <base/string_util.h> | 16 #include <base/string_util.h> |
| 17 #include <google/protobuf/repeated_field.h> | 17 #include <google/protobuf/repeated_field.h> |
| 18 | 18 |
| 19 #include "update_engine/bzip_extent_writer.h" | 19 #include "update_engine/bzip_extent_writer.h" |
| 20 #include "update_engine/delta_diff_generator.h" | 20 #include "update_engine/delta_diff_generator.h" |
| 21 #include "update_engine/extent_writer.h" | 21 #include "update_engine/extent_writer.h" |
| 22 #include "update_engine/graph_types.h" | 22 #include "update_engine/graph_types.h" |
| 23 #include "update_engine/payload_signer.h" | 23 #include "update_engine/payload_signer.h" |
| 24 #include "update_engine/prefs_interface.h" |
| 24 #include "update_engine/subprocess.h" | 25 #include "update_engine/subprocess.h" |
| 25 | 26 |
| 26 using std::min; | 27 using std::min; |
| 27 using std::string; | 28 using std::string; |
| 28 using std::vector; | 29 using std::vector; |
| 29 using google::protobuf::RepeatedPtrField; | 30 using google::protobuf::RepeatedPtrField; |
| 30 | 31 |
| 31 namespace chromeos_update_engine { | 32 namespace chromeos_update_engine { |
| 32 | 33 |
| 33 namespace { | 34 namespace { |
| 34 | 35 |
| 35 const int kDeltaVersionLength = 8; | 36 const int kDeltaVersionLength = 8; |
| 36 const int kDeltaProtobufLengthLength = 8; | 37 const int kDeltaProtobufLengthLength = 8; |
| 37 const char kUpdatePayloadPublicKeyPath[] = | 38 const char kUpdatePayloadPublicKeyPath[] = |
| 38 "/usr/share/update_engine/update-payload-key.pub.pem"; | 39 "/usr/share/update_engine/update-payload-key.pub.pem"; |
| 40 const int kUpdateStateOperationInvalid = -1; |
| 41 |
| 42 // Returns true if |op| is idempotent -- i.e., if we can interrupt it and repeat |
| 43 // it safely. Returns false otherwise. |
| 44 bool IsIdempotentOperation(const DeltaArchiveManifest_InstallOperation& op) { |
| 45 if (op.src_extents_size() == 0) { |
| 46 return true; |
| 47 } |
| 48 // TODO(petkov): Cover the case where the source and target extents don't |
| 49 // intersect. |
| 50 return false; |
| 51 } |
| 39 | 52 |
| 40 // Converts extents to a human-readable string, for use by DumpUpdateProto(). | 53 // Converts extents to a human-readable string, for use by DumpUpdateProto(). |
| 41 string ExtentsToString(const RepeatedPtrField<Extent>& extents) { | 54 string ExtentsToString(const RepeatedPtrField<Extent>& extents) { |
| 42 string ret; | 55 string ret; |
| 43 for (int i = 0; i < extents.size(); i++) { | 56 for (int i = 0; i < extents.size(); i++) { |
| 44 const Extent& extent = extents.Get(i); | 57 const Extent& extent = extents.Get(i); |
| 45 if (extent.start_block() == kSparseHole) { | 58 if (extent.start_block() == kSparseHole) { |
| 46 ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks()); | 59 ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks()); |
| 47 } else { | 60 } else { |
| 48 ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ", | 61 ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ", |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 } | 183 } |
| 171 // We have the full proto buffer in buffer_. Parse it. | 184 // We have the full proto buffer in buffer_. Parse it. |
| 172 const int offset = strlen(kDeltaMagic) + kDeltaVersionLength + | 185 const int offset = strlen(kDeltaMagic) + kDeltaVersionLength + |
| 173 kDeltaProtobufLengthLength; | 186 kDeltaProtobufLengthLength; |
| 174 if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) { | 187 if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) { |
| 175 LOG(ERROR) << "Unable to parse manifest in update file."; | 188 LOG(ERROR) << "Unable to parse manifest in update file."; |
| 176 return -EINVAL; | 189 return -EINVAL; |
| 177 } | 190 } |
| 178 // Remove protobuf and header info from buffer_, so buffer_ contains | 191 // Remove protobuf and header info from buffer_, so buffer_ contains |
| 179 // just data blobs | 192 // just data blobs |
| 180 DiscardBufferHeadBytes(strlen(kDeltaMagic) + | 193 size_t metadata_size = strlen(kDeltaMagic) + kDeltaVersionLength + |
| 181 kDeltaVersionLength + | 194 kDeltaProtobufLengthLength + protobuf_length; |
| 182 kDeltaProtobufLengthLength + protobuf_length, | 195 DiscardBufferHeadBytes(metadata_size, |
| 183 true); // do_hash | 196 true); // do_hash |
| 197 LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestMetadataSize, |
| 198 metadata_size)) |
| 199 << "Unable to save the manifest metadata size."; |
| 184 manifest_valid_ = true; | 200 manifest_valid_ = true; |
| 185 block_size_ = manifest_.block_size(); | 201 block_size_ = manifest_.block_size(); |
| 186 } | 202 } |
| 187 ssize_t total_operations = manifest_.install_operations_size() + | 203 ssize_t total_operations = manifest_.install_operations_size() + |
| 188 manifest_.kernel_install_operations_size(); | 204 manifest_.kernel_install_operations_size(); |
| 189 while (next_operation_num_ < total_operations) { | 205 while (next_operation_num_ < total_operations) { |
| 190 const DeltaArchiveManifest_InstallOperation &op = | 206 const DeltaArchiveManifest_InstallOperation &op = |
| 191 next_operation_num_ < manifest_.install_operations_size() ? | 207 next_operation_num_ < manifest_.install_operations_size() ? |
| 192 manifest_.install_operations(next_operation_num_) : | 208 manifest_.install_operations(next_operation_num_) : |
| 193 manifest_.kernel_install_operations( | 209 manifest_.kernel_install_operations( |
| 194 next_operation_num_ - manifest_.install_operations_size()); | 210 next_operation_num_ - manifest_.install_operations_size()); |
| 195 if (!CanPerformInstallOperation(op)) | 211 if (!CanPerformInstallOperation(op)) |
| 196 break; | 212 break; |
| 197 // Log every thousandth operation, and also the first and last ones | 213 // Log every thousandth operation, and also the first and last ones |
| 198 if ((next_operation_num_ % 1000 == 0) || | 214 if ((next_operation_num_ % 1000 == 0) || |
| 199 (next_operation_num_ + 1 == total_operations)) { | 215 (next_operation_num_ + 1 == total_operations)) { |
| 200 LOG(INFO) << "Performing operation " << (next_operation_num_ + 1) << "/" | 216 LOG(INFO) << "Performing operation " << (next_operation_num_ + 1) << "/" |
| 201 << total_operations; | 217 << total_operations; |
| 202 } | 218 } |
| 203 bool is_kernel_partition = | 219 bool is_kernel_partition = |
| 204 (next_operation_num_ >= manifest_.install_operations_size()); | 220 (next_operation_num_ >= manifest_.install_operations_size()); |
| 221 // If about to start a non-idempotent operation, clear the update state so |
| 222 // that if the operation gets interrupted, we don't try to resume the |
| 223 // update. |
| 224 if (!IsIdempotentOperation(op)) { |
| 225 ResetUpdateProgress(); |
| 226 } |
| 205 if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE || | 227 if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE || |
| 206 op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { | 228 op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { |
| 207 if (!PerformReplaceOperation(op, is_kernel_partition)) { | 229 if (!PerformReplaceOperation(op, is_kernel_partition)) { |
| 208 LOG(ERROR) << "Failed to perform replace operation " | 230 LOG(ERROR) << "Failed to perform replace operation " |
| 209 << next_operation_num_; | 231 << next_operation_num_; |
| 210 return -EINVAL; | 232 return -EINVAL; |
| 211 } | 233 } |
| 212 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) { | 234 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) { |
| 213 if (!PerformMoveOperation(op, is_kernel_partition)) { | 235 if (!PerformMoveOperation(op, is_kernel_partition)) { |
| 214 LOG(ERROR) << "Failed to perform move operation " | 236 LOG(ERROR) << "Failed to perform move operation " |
| 215 << next_operation_num_; | 237 << next_operation_num_; |
| 216 return -EINVAL; | 238 return -EINVAL; |
| 217 } | 239 } |
| 218 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) { | 240 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) { |
| 219 if (!PerformBsdiffOperation(op, is_kernel_partition)) { | 241 if (!PerformBsdiffOperation(op, is_kernel_partition)) { |
| 220 LOG(ERROR) << "Failed to perform bsdiff operation " | 242 LOG(ERROR) << "Failed to perform bsdiff operation " |
| 221 << next_operation_num_; | 243 << next_operation_num_; |
| 222 return -EINVAL; | 244 return -EINVAL; |
| 223 } | 245 } |
| 224 } | 246 } |
| 225 next_operation_num_++; | 247 next_operation_num_++; |
| 248 CheckpointUpdateProgress(); |
| 226 } | 249 } |
| 227 return count; | 250 return count; |
| 228 } | 251 } |
| 229 | 252 |
| 230 bool DeltaPerformer::CanPerformInstallOperation( | 253 bool DeltaPerformer::CanPerformInstallOperation( |
| 231 const chromeos_update_engine::DeltaArchiveManifest_InstallOperation& | 254 const chromeos_update_engine::DeltaArchiveManifest_InstallOperation& |
| 232 operation) { | 255 operation) { |
| 233 // Move operations don't require any data blob, so they can always | 256 // Move operations don't require any data blob, so they can always |
| 234 // be performed | 257 // be performed |
| 235 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) | 258 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) |
| (...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 482 return hash_data == signed_hash_data; | 505 return hash_data == signed_hash_data; |
| 483 } | 506 } |
| 484 | 507 |
| 485 void DeltaPerformer::DiscardBufferHeadBytes(size_t count, bool do_hash) { | 508 void DeltaPerformer::DiscardBufferHeadBytes(size_t count, bool do_hash) { |
| 486 if (do_hash) { | 509 if (do_hash) { |
| 487 hash_calculator_.Update(&buffer_[0], count); | 510 hash_calculator_.Update(&buffer_[0], count); |
| 488 } | 511 } |
| 489 buffer_.erase(buffer_.begin(), buffer_.begin() + count); | 512 buffer_.erase(buffer_.begin(), buffer_.begin() + count); |
| 490 } | 513 } |
| 491 | 514 |
| 515 bool DeltaPerformer::ResetUpdateProgress() { |
| 516 TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextOperation, |
| 517 kUpdateStateOperationInvalid)); |
| 518 return true; |
| 519 } |
| 520 |
| 521 bool DeltaPerformer::CheckpointUpdateProgress() { |
| 522 // First reset the progress in case we die in the middle of the state update. |
| 523 ResetUpdateProgress(); |
| 524 TEST_AND_RETURN_FALSE(prefs_->SetString(kPrefsUpdateStateSignedSHA256Context, |
| 525 hash_calculator_.GetContext())); |
| 526 TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataOffset, |
| 527 buffer_offset_)); |
| 528 TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextOperation, |
| 529 next_operation_num_)); |
| 530 return true; |
| 531 } |
| 532 |
| 492 } // namespace chromeos_update_engine | 533 } // namespace chromeos_update_engine |
| OLD | NEW |