| Mike Frysinger | 8155d08 | 2012-04-06 15:23:18 -0400 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 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 "update_engine/delta_performer.h" | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 6 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 7 | #include <endian.h> | 
 | 8 | #include <errno.h> | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 9 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 10 | #include <algorithm> | 
 | 11 | #include <cstring> | 
| Ben Chan | 02f7c1d | 2014-10-18 15:18:02 -0700 | [diff] [blame] | 12 | #include <memory> | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 13 | #include <string> | 
 | 14 | #include <vector> | 
 | 15 |  | 
| Ben Chan | 06c76a4 | 2014-09-05 08:21:06 -0700 | [diff] [blame] | 16 | #include <base/files/file_util.h> | 
| Alex Deymo | 161c4a1 | 2014-05-16 15:56:21 -0700 | [diff] [blame] | 17 | #include <base/format_macros.h> | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 18 | #include <base/strings/string_util.h> | 
 | 19 | #include <base/strings/stringprintf.h> | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 20 | #include <google/protobuf/repeated_field.h> | 
 | 21 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 22 | #include "update_engine/bzip_extent_writer.h" | 
| Jay Srinivasan | d29695d | 2013-04-08 15:08:05 -0700 | [diff] [blame] | 23 | #include "update_engine/constants.h" | 
| Andrew de los Reyes | 353777c | 2010-10-08 10:34:30 -0700 | [diff] [blame] | 24 | #include "update_engine/extent_ranges.h" | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 25 | #include "update_engine/extent_writer.h" | 
| David Zeuthen | e7f8917 | 2013-10-31 10:21:04 -0700 | [diff] [blame] | 26 | #include "update_engine/hardware_interface.h" | 
| Alex Deymo | 161c4a1 | 2014-05-16 15:56:21 -0700 | [diff] [blame] | 27 | #include "update_engine/payload_constants.h" | 
| Jay Srinivasan | 55f50c2 | 2013-01-10 19:24:35 -0800 | [diff] [blame] | 28 | #include "update_engine/payload_state_interface.h" | 
| Alex Deymo | 923d8fa | 2014-07-15 17:58:51 -0700 | [diff] [blame] | 29 | #include "update_engine/payload_verifier.h" | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 30 | #include "update_engine/prefs_interface.h" | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 31 | #include "update_engine/subprocess.h" | 
| Darin Petkov | 9c0baf8 | 2010-10-07 13:44:48 -0700 | [diff] [blame] | 32 | #include "update_engine/terminator.h" | 
| Jay Srinivasan | 1c0fe79 | 2013-03-28 16:45:25 -0700 | [diff] [blame] | 33 | #include "update_engine/update_attempter.h" | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 34 |  | 
| Alex Deymo | 161c4a1 | 2014-05-16 15:56:21 -0700 | [diff] [blame] | 35 | using google::protobuf::RepeatedPtrField; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 36 | using std::min; | 
 | 37 | using std::string; | 
| Ben Chan | 02f7c1d | 2014-10-18 15:18:02 -0700 | [diff] [blame] | 38 | using std::unique_ptr; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 39 | using std::vector; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 40 |  | 
 | 41 | namespace chromeos_update_engine { | 
 | 42 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 43 | const uint64_t DeltaPerformer::kDeltaVersionSize = 8; | 
 | 44 | const uint64_t DeltaPerformer::kDeltaManifestSizeSize = 8; | 
| Don Garrett | 4d03944 | 2013-10-28 18:40:06 -0700 | [diff] [blame] | 45 | const uint64_t DeltaPerformer::kSupportedMajorPayloadVersion = 1; | 
| Don Garrett | b8dd1d9 | 2013-11-22 17:40:02 -0800 | [diff] [blame] | 46 | const uint64_t DeltaPerformer::kSupportedMinorPayloadVersion = 1; | 
 | 47 | const uint64_t DeltaPerformer::kFullPayloadMinorVersion = 0; | 
| Don Garrett | 4d03944 | 2013-10-28 18:40:06 -0700 | [diff] [blame] | 48 |  | 
| Darin Petkov | abc7bc0 | 2011-02-23 14:39:43 -0800 | [diff] [blame] | 49 | const char DeltaPerformer::kUpdatePayloadPublicKeyPath[] = | 
 | 50 |     "/usr/share/update_engine/update-payload-key.pub.pem"; | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 51 | const unsigned DeltaPerformer::kProgressLogMaxChunks = 10; | 
 | 52 | const unsigned DeltaPerformer::kProgressLogTimeoutSeconds = 30; | 
 | 53 | const unsigned DeltaPerformer::kProgressDownloadWeight = 50; | 
 | 54 | const unsigned DeltaPerformer::kProgressOperationsWeight = 50; | 
| Darin Petkov | abc7bc0 | 2011-02-23 14:39:43 -0800 | [diff] [blame] | 55 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 56 | namespace { | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 57 | const int kUpdateStateOperationInvalid = -1; | 
| Darin Petkov | 6142614 | 2010-10-08 11:04:55 -0700 | [diff] [blame] | 58 | const int kMaxResumedUpdateFailures = 10; | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 59 | // Opens path for read/write, put the fd into *fd. On success returns true | 
 | 60 | // and sets *err to 0. On failure, returns false and sets *err to errno. | 
 | 61 | bool OpenFile(const char* path, int* fd, int* err) { | 
 | 62 |   if (*fd != -1) { | 
 | 63 |     LOG(ERROR) << "Can't open(" << path << "), *fd != -1 (it's " << *fd << ")"; | 
 | 64 |     *err = EINVAL; | 
 | 65 |     return false; | 
 | 66 |   } | 
 | 67 |   *fd = open(path, O_RDWR, 000); | 
 | 68 |   if (*fd < 0) { | 
 | 69 |     *err = errno; | 
 | 70 |     PLOG(ERROR) << "Unable to open file " << path; | 
 | 71 |     return false; | 
 | 72 |   } | 
 | 73 |   *err = 0; | 
 | 74 |   return true; | 
 | 75 | } | 
 | 76 |  | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 77 | }  // namespace | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 78 |  | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 79 |  | 
 | 80 | // Computes the ratio of |part| and |total|, scaled to |norm|, using integer | 
 | 81 | // arithmetic. | 
 | 82 | static uint64_t IntRatio(uint64_t part, uint64_t total, uint64_t norm) { | 
 | 83 |   return part * norm / total; | 
 | 84 | } | 
 | 85 |  | 
 | 86 | void DeltaPerformer::LogProgress(const char* message_prefix) { | 
 | 87 |   // Format operations total count and percentage. | 
 | 88 |   string total_operations_str("?"); | 
 | 89 |   string completed_percentage_str(""); | 
 | 90 |   if (num_total_operations_) { | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 91 |     total_operations_str = base::StringPrintf("%zu", num_total_operations_); | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 92 |     // Upcasting to 64-bit to avoid overflow, back to size_t for formatting. | 
 | 93 |     completed_percentage_str = | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 94 |         base::StringPrintf(" (%" PRIu64 "%%)", | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 95 |                      IntRatio(next_operation_num_, num_total_operations_, | 
 | 96 |                               100)); | 
 | 97 |   } | 
 | 98 |  | 
 | 99 |   // Format download total count and percentage. | 
 | 100 |   size_t payload_size = install_plan_->payload_size; | 
 | 101 |   string payload_size_str("?"); | 
 | 102 |   string downloaded_percentage_str(""); | 
 | 103 |   if (payload_size) { | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 104 |     payload_size_str = base::StringPrintf("%zu", payload_size); | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 105 |     // Upcasting to 64-bit to avoid overflow, back to size_t for formatting. | 
 | 106 |     downloaded_percentage_str = | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 107 |         base::StringPrintf(" (%" PRIu64 "%%)", | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 108 |                      IntRatio(total_bytes_received_, payload_size, 100)); | 
 | 109 |   } | 
 | 110 |  | 
 | 111 |   LOG(INFO) << (message_prefix ? message_prefix : "") << next_operation_num_ | 
 | 112 |             << "/" << total_operations_str << " operations" | 
 | 113 |             << completed_percentage_str << ", " << total_bytes_received_ | 
 | 114 |             << "/" << payload_size_str << " bytes downloaded" | 
 | 115 |             << downloaded_percentage_str << ", overall progress " | 
 | 116 |             << overall_progress_ << "%"; | 
 | 117 | } | 
 | 118 |  | 
 | 119 | void DeltaPerformer::UpdateOverallProgress(bool force_log, | 
 | 120 |                                            const char* message_prefix) { | 
 | 121 |   // Compute our download and overall progress. | 
 | 122 |   unsigned new_overall_progress = 0; | 
 | 123 |   COMPILE_ASSERT(kProgressDownloadWeight + kProgressOperationsWeight == 100, | 
 | 124 |                  progress_weight_dont_add_up); | 
 | 125 |   // Only consider download progress if its total size is known; otherwise | 
 | 126 |   // adjust the operations weight to compensate for the absence of download | 
 | 127 |   // progress. Also, make sure to cap the download portion at | 
 | 128 |   // kProgressDownloadWeight, in case we end up downloading more than we | 
 | 129 |   // initially expected (this indicates a problem, but could generally happen). | 
 | 130 |   // TODO(garnold) the correction of operations weight when we do not have the | 
 | 131 |   // total payload size, as well as the conditional guard below, should both be | 
 | 132 |   // eliminated once we ensure that the payload_size in the install plan is | 
 | 133 |   // always given and is non-zero. This currently isn't the case during unit | 
 | 134 |   // tests (see chromium-os:37969). | 
 | 135 |   size_t payload_size = install_plan_->payload_size; | 
 | 136 |   unsigned actual_operations_weight = kProgressOperationsWeight; | 
 | 137 |   if (payload_size) | 
 | 138 |     new_overall_progress += min( | 
 | 139 |         static_cast<unsigned>(IntRatio(total_bytes_received_, payload_size, | 
 | 140 |                                        kProgressDownloadWeight)), | 
 | 141 |         kProgressDownloadWeight); | 
 | 142 |   else | 
 | 143 |     actual_operations_weight += kProgressDownloadWeight; | 
 | 144 |  | 
 | 145 |   // Only add completed operations if their total number is known; we definitely | 
 | 146 |   // expect an update to have at least one operation, so the expectation is that | 
 | 147 |   // this will eventually reach |actual_operations_weight|. | 
 | 148 |   if (num_total_operations_) | 
 | 149 |     new_overall_progress += IntRatio(next_operation_num_, num_total_operations_, | 
 | 150 |                                      actual_operations_weight); | 
 | 151 |  | 
 | 152 |   // Progress ratio cannot recede, unless our assumptions about the total | 
 | 153 |   // payload size, total number of operations, or the monotonicity of progress | 
 | 154 |   // is breached. | 
 | 155 |   if (new_overall_progress < overall_progress_) { | 
 | 156 |     LOG(WARNING) << "progress counter receded from " << overall_progress_ | 
 | 157 |                  << "% down to " << new_overall_progress << "%; this is a bug"; | 
 | 158 |     force_log = true; | 
 | 159 |   } | 
 | 160 |   overall_progress_ = new_overall_progress; | 
 | 161 |  | 
 | 162 |   // Update chunk index, log as needed: if forced by called, or we completed a | 
 | 163 |   // progress chunk, or a timeout has expired. | 
 | 164 |   base::Time curr_time = base::Time::Now(); | 
 | 165 |   unsigned curr_progress_chunk = | 
 | 166 |       overall_progress_ * kProgressLogMaxChunks / 100; | 
 | 167 |   if (force_log || curr_progress_chunk > last_progress_chunk_ || | 
 | 168 |       curr_time > forced_progress_log_time_) { | 
 | 169 |     forced_progress_log_time_ = curr_time + forced_progress_log_wait_; | 
 | 170 |     LogProgress(message_prefix); | 
 | 171 |   } | 
 | 172 |   last_progress_chunk_ = curr_progress_chunk; | 
 | 173 | } | 
 | 174 |  | 
 | 175 |  | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 176 | size_t DeltaPerformer::CopyDataToBuffer(const char** bytes_p, size_t* count_p, | 
 | 177 |                                         size_t max) { | 
 | 178 |   const size_t count = *count_p; | 
 | 179 |   if (!count) | 
 | 180 |     return 0;  // Special case shortcut. | 
| Alex Deymo | f329b93 | 2014-10-30 01:37:48 -0700 | [diff] [blame] | 181 |   size_t read_len = min(count, max - buffer_.size()); | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 182 |   const char* bytes_start = *bytes_p; | 
 | 183 |   const char* bytes_end = bytes_start + read_len; | 
 | 184 |   buffer_.insert(buffer_.end(), bytes_start, bytes_end); | 
 | 185 |   *bytes_p = bytes_end; | 
 | 186 |   *count_p = count - read_len; | 
 | 187 |   return read_len; | 
 | 188 | } | 
 | 189 |  | 
 | 190 |  | 
 | 191 | bool DeltaPerformer::HandleOpResult(bool op_result, const char* op_type_name, | 
 | 192 |                                     ErrorCode* error) { | 
 | 193 |   if (op_result) | 
 | 194 |     return true; | 
 | 195 |  | 
 | 196 |   LOG(ERROR) << "Failed to perform " << op_type_name << " operation " | 
 | 197 |              << next_operation_num_; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 198 |   *error = ErrorCode::kDownloadOperationExecutionError; | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 199 |   return false; | 
 | 200 | } | 
 | 201 |  | 
 | 202 |  | 
| Andrew de los Reyes | 353777c | 2010-10-08 10:34:30 -0700 | [diff] [blame] | 203 | // Returns true if |op| is idempotent -- i.e., if we can interrupt it and repeat | 
 | 204 | // it safely. Returns false otherwise. | 
 | 205 | bool DeltaPerformer::IsIdempotentOperation( | 
 | 206 |     const DeltaArchiveManifest_InstallOperation& op) { | 
 | 207 |   if (op.src_extents_size() == 0) { | 
 | 208 |     return true; | 
 | 209 |   } | 
| Darin Petkov | 9fa7ec5 | 2010-10-18 11:45:23 -0700 | [diff] [blame] | 210 |   // When in doubt, it's safe to declare an op non-idempotent. Note that we | 
 | 211 |   // could detect other types of idempotent operations here such as a MOVE that | 
 | 212 |   // moves blocks onto themselves. However, we rely on the server to not send | 
 | 213 |   // such operations at all. | 
| Andrew de los Reyes | 353777c | 2010-10-08 10:34:30 -0700 | [diff] [blame] | 214 |   ExtentRanges src_ranges; | 
 | 215 |   src_ranges.AddRepeatedExtents(op.src_extents()); | 
 | 216 |   const uint64_t block_count = src_ranges.blocks(); | 
 | 217 |   src_ranges.SubtractRepeatedExtents(op.dst_extents()); | 
 | 218 |   return block_count == src_ranges.blocks(); | 
 | 219 | } | 
 | 220 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 221 | int DeltaPerformer::Open(const char* path, int flags, mode_t mode) { | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 222 |   int err; | 
 | 223 |   if (OpenFile(path, &fd_, &err)) | 
 | 224 |     path_ = path; | 
 | 225 |   return -err; | 
 | 226 | } | 
 | 227 |  | 
 | 228 | bool DeltaPerformer::OpenKernel(const char* kernel_path) { | 
 | 229 |   int err; | 
 | 230 |   bool success = OpenFile(kernel_path, &kernel_fd_, &err); | 
 | 231 |   if (success) | 
 | 232 |     kernel_path_ = kernel_path; | 
 | 233 |   return success; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 234 | } | 
 | 235 |  | 
 | 236 | int DeltaPerformer::Close() { | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 237 |   int err = 0; | 
 | 238 |   if (close(kernel_fd_) == -1) { | 
 | 239 |     err = errno; | 
 | 240 |     PLOG(ERROR) << "Unable to close kernel fd:"; | 
 | 241 |   } | 
 | 242 |   if (close(fd_) == -1) { | 
 | 243 |     err = errno; | 
 | 244 |     PLOG(ERROR) << "Unable to close rootfs fd:"; | 
 | 245 |   } | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 246 |   LOG_IF(ERROR, !hash_calculator_.Finalize()) << "Unable to finalize the hash."; | 
| Darin Petkov | 934bb41 | 2010-11-18 11:21:35 -0800 | [diff] [blame] | 247 |   fd_ = -2;  // Set to invalid so that calls to Open() will fail. | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 248 |   path_ = ""; | 
| Darin Petkov | 934bb41 | 2010-11-18 11:21:35 -0800 | [diff] [blame] | 249 |   if (!buffer_.empty()) { | 
| Jay Srinivasan | 1c0fe79 | 2013-03-28 16:45:25 -0700 | [diff] [blame] | 250 |     LOG(INFO) << "Discarding " << buffer_.size() << " unused downloaded bytes"; | 
 | 251 |     if (err >= 0) | 
| Darin Petkov | 934bb41 | 2010-11-18 11:21:35 -0800 | [diff] [blame] | 252 |       err = 1; | 
| Darin Petkov | 934bb41 | 2010-11-18 11:21:35 -0800 | [diff] [blame] | 253 |   } | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 254 |   return -err; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 255 | } | 
 | 256 |  | 
| Andrew de los Reyes | 89f17be | 2010-10-22 13:39:09 -0700 | [diff] [blame] | 257 | namespace { | 
 | 258 |  | 
 | 259 | void LogPartitionInfoHash(const PartitionInfo& info, const string& tag) { | 
 | 260 |   string sha256; | 
 | 261 |   if (OmahaHashCalculator::Base64Encode(info.hash().data(), | 
 | 262 |                                         info.hash().size(), | 
 | 263 |                                         &sha256)) { | 
| Darin Petkov | 3aefa86 | 2010-12-07 14:45:00 -0800 | [diff] [blame] | 264 |     LOG(INFO) << "PartitionInfo " << tag << " sha256: " << sha256 | 
 | 265 |               << " size: " << info.size(); | 
| Andrew de los Reyes | 89f17be | 2010-10-22 13:39:09 -0700 | [diff] [blame] | 266 |   } else { | 
 | 267 |     LOG(ERROR) << "Base64Encode failed for tag: " << tag; | 
 | 268 |   } | 
 | 269 | } | 
 | 270 |  | 
 | 271 | void LogPartitionInfo(const DeltaArchiveManifest& manifest) { | 
 | 272 |   if (manifest.has_old_kernel_info()) | 
 | 273 |     LogPartitionInfoHash(manifest.old_kernel_info(), "old_kernel_info"); | 
 | 274 |   if (manifest.has_old_rootfs_info()) | 
 | 275 |     LogPartitionInfoHash(manifest.old_rootfs_info(), "old_rootfs_info"); | 
 | 276 |   if (manifest.has_new_kernel_info()) | 
 | 277 |     LogPartitionInfoHash(manifest.new_kernel_info(), "new_kernel_info"); | 
 | 278 |   if (manifest.has_new_rootfs_info()) | 
 | 279 |     LogPartitionInfoHash(manifest.new_rootfs_info(), "new_rootfs_info"); | 
 | 280 | } | 
 | 281 |  | 
| Alex Vakulenko | d2779df | 2014-06-16 13:19:00 -0700 | [diff] [blame] | 282 | }  // namespace | 
| Andrew de los Reyes | 89f17be | 2010-10-22 13:39:09 -0700 | [diff] [blame] | 283 |  | 
| Don Garrett | 4d03944 | 2013-10-28 18:40:06 -0700 | [diff] [blame] | 284 | uint64_t DeltaPerformer::GetVersionOffset() { | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 285 |   // Manifest size is stored right after the magic string and the version. | 
 | 286 |   return strlen(kDeltaMagic); | 
| Don Garrett | 4d03944 | 2013-10-28 18:40:06 -0700 | [diff] [blame] | 287 | } | 
 | 288 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 289 | uint64_t DeltaPerformer::GetManifestSizeOffset() { | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 290 |   // Manifest size is stored right after the magic string and the version. | 
 | 291 |   return strlen(kDeltaMagic) + kDeltaVersionSize; | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 292 | } | 
 | 293 |  | 
 | 294 | uint64_t DeltaPerformer::GetManifestOffset() { | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 295 |   // Actual manifest begins right after the manifest size field. | 
 | 296 |   return GetManifestSizeOffset() + kDeltaManifestSizeSize; | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 297 | } | 
 | 298 |  | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 299 | uint64_t DeltaPerformer::GetMetadataSize() const { | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 300 |   return metadata_size_; | 
 | 301 | } | 
 | 302 |  | 
 | 303 | bool DeltaPerformer::GetManifest(DeltaArchiveManifest* out_manifest_p) const { | 
 | 304 |   if (!manifest_parsed_) | 
 | 305 |     return false; | 
 | 306 |   *out_manifest_p = manifest_; | 
 | 307 |   return true; | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 308 | } | 
 | 309 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 310 |  | 
| Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 311 | DeltaPerformer::MetadataParseResult DeltaPerformer::ParsePayloadMetadata( | 
| Alex Deymo | f329b93 | 2014-10-30 01:37:48 -0700 | [diff] [blame] | 312 |     const vector<char>& payload, | 
| David Zeuthen | a99981f | 2013-04-29 13:42:47 -0700 | [diff] [blame] | 313 |     ErrorCode* error) { | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 314 |   *error = ErrorCode::kSuccess; | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 315 |   const uint64_t manifest_offset = GetManifestOffset(); | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 316 |   uint64_t manifest_size = (metadata_size_ ? | 
 | 317 |                             metadata_size_ - manifest_offset : 0); | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 318 |  | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 319 |   if (!manifest_size) { | 
 | 320 |     // Ensure we have data to cover the payload header. | 
 | 321 |     if (payload.size() < manifest_offset) | 
 | 322 |       return kMetadataParseInsufficientData; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 323 |  | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 324 |     // Validate the magic string. | 
 | 325 |     if (memcmp(payload.data(), kDeltaMagic, strlen(kDeltaMagic)) != 0) { | 
 | 326 |       LOG(ERROR) << "Bad payload format -- invalid delta magic."; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 327 |       *error = ErrorCode::kDownloadInvalidMetadataMagicString; | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 328 |       return kMetadataParseError; | 
 | 329 |     } | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 330 |  | 
 | 331 |     // Extract the payload version from the metadata. | 
 | 332 |     uint64_t major_payload_version; | 
 | 333 |     COMPILE_ASSERT(sizeof(major_payload_version) == kDeltaVersionSize, | 
 | 334 |                    major_payload_version_size_mismatch); | 
 | 335 |     memcpy(&major_payload_version, | 
 | 336 |            &payload[GetVersionOffset()], | 
 | 337 |            kDeltaVersionSize); | 
 | 338 |     // switch big endian to host | 
 | 339 |     major_payload_version = be64toh(major_payload_version); | 
 | 340 |  | 
 | 341 |     if (major_payload_version != kSupportedMajorPayloadVersion) { | 
 | 342 |       LOG(ERROR) << "Bad payload format -- unsupported payload version: " | 
 | 343 |           << major_payload_version; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 344 |       *error = ErrorCode::kUnsupportedMajorPayloadVersion; | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 345 |       return kMetadataParseError; | 
 | 346 |     } | 
 | 347 |  | 
 | 348 |     // Next, parse the manifest size. | 
 | 349 |     COMPILE_ASSERT(sizeof(manifest_size) == kDeltaManifestSizeSize, | 
 | 350 |                    manifest_size_size_mismatch); | 
 | 351 |     memcpy(&manifest_size, | 
 | 352 |            &payload[GetManifestSizeOffset()], | 
 | 353 |            kDeltaManifestSizeSize); | 
 | 354 |     manifest_size = be64toh(manifest_size);  // switch big endian to host | 
 | 355 |  | 
 | 356 |     // If the metadata size is present in install plan, check for it immediately | 
 | 357 |     // even before waiting for that many number of bytes to be downloaded in the | 
 | 358 |     // payload. This will prevent any attack which relies on us downloading data | 
 | 359 |     // beyond the expected metadata size. | 
 | 360 |     metadata_size_ = manifest_offset + manifest_size; | 
 | 361 |     if (install_plan_->hash_checks_mandatory) { | 
 | 362 |       if (install_plan_->metadata_size != metadata_size_) { | 
 | 363 |         LOG(ERROR) << "Mandatory metadata size in Omaha response (" | 
 | 364 |                    << install_plan_->metadata_size | 
 | 365 |                    << ") is missing/incorrect, actual = " << metadata_size_; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 366 |         *error = ErrorCode::kDownloadInvalidMetadataSize; | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 367 |         return kMetadataParseError; | 
 | 368 |       } | 
 | 369 |     } | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 370 |   } | 
 | 371 |  | 
 | 372 |   // Now that we have validated the metadata size, we should wait for the full | 
 | 373 |   // metadata to be read in before we can parse it. | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 374 |   if (payload.size() < metadata_size_) | 
| Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 375 |     return kMetadataParseInsufficientData; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 376 |  | 
 | 377 |   // Log whether we validated the size or simply trusting what's in the payload | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 378 |   // here. This is logged here (after we received the full metadata data) so | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 379 |   // that we just log once (instead of logging n times) if it takes n | 
 | 380 |   // DeltaPerformer::Write calls to download the full manifest. | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 381 |   if (install_plan_->metadata_size == metadata_size_) { | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 382 |     LOG(INFO) << "Manifest size in payload matches expected value from Omaha"; | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 383 |   } else { | 
 | 384 |     // For mandatory-cases, we'd have already returned a kMetadataParseError | 
 | 385 |     // above. We'll be here only for non-mandatory cases. Just send a UMA stat. | 
 | 386 |     LOG(WARNING) << "Ignoring missing/incorrect metadata size (" | 
 | 387 |                  << install_plan_->metadata_size | 
 | 388 |                  << ") in Omaha response as validation is not mandatory. " | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 389 |                  << "Trusting metadata size in payload = " << metadata_size_; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 390 |     SendUmaStat(ErrorCode::kDownloadInvalidMetadataSize); | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 391 |   } | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 392 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 393 |   // We have the full metadata in |payload|. Verify its integrity | 
 | 394 |   // and authenticity based on the information we have in Omaha response. | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 395 |   *error = ValidateMetadataSignature(&payload[0], metadata_size_); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 396 |   if (*error != ErrorCode::kSuccess) { | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 397 |     if (install_plan_->hash_checks_mandatory) { | 
| David Zeuthen | bc27aac | 2013-11-26 11:17:48 -0800 | [diff] [blame] | 398 |       // The autoupdate_CatchBadSignatures test checks for this string | 
 | 399 |       // in log-files. Keep in sync. | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 400 |       LOG(ERROR) << "Mandatory metadata signature validation failed"; | 
 | 401 |       return kMetadataParseError; | 
 | 402 |     } | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 403 |  | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 404 |     // For non-mandatory cases, just send a UMA stat. | 
 | 405 |     LOG(WARNING) << "Ignoring metadata signature validation failures"; | 
 | 406 |     SendUmaStat(*error); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 407 |     *error = ErrorCode::kSuccess; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 408 |   } | 
 | 409 |  | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 410 |   // The payload metadata is deemed valid, it's safe to parse the protobuf. | 
 | 411 |   if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size)) { | 
| Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 412 |     LOG(ERROR) << "Unable to parse manifest in update file."; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 413 |     *error = ErrorCode::kDownloadManifestParseError; | 
| Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 414 |     return kMetadataParseError; | 
 | 415 |   } | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 416 |  | 
 | 417 |   manifest_parsed_ = true; | 
| Darin Petkov | 9574f7e | 2011-01-13 10:48:12 -0800 | [diff] [blame] | 418 |   return kMetadataParseSuccess; | 
 | 419 | } | 
 | 420 |  | 
 | 421 |  | 
| Don Garrett | e410e0f | 2011-11-10 15:39:01 -0800 | [diff] [blame] | 422 | // Wrapper around write. Returns true if all requested bytes | 
| Jay Srinivasan | 2b5a0f0 | 2012-12-19 17:25:56 -0800 | [diff] [blame] | 423 | // were written, or false on any error, regardless of progress | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 424 | // and stores an action exit code in |error|. | 
 | 425 | bool DeltaPerformer::Write(const void* bytes, size_t count, | 
| David Zeuthen | a99981f | 2013-04-29 13:42:47 -0700 | [diff] [blame] | 426 |                            ErrorCode *error) { | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 427 |   *error = ErrorCode::kSuccess; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 428 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 429 |   const char* c_bytes = reinterpret_cast<const char*>(bytes); | 
| Jay Srinivasan | 2b5a0f0 | 2012-12-19 17:25:56 -0800 | [diff] [blame] | 430 |   system_state_->payload_state()->DownloadProgress(count); | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 431 |  | 
 | 432 |   // Update the total byte downloaded count and the progress logs. | 
 | 433 |   total_bytes_received_ += count; | 
 | 434 |   UpdateOverallProgress(false, "Completed "); | 
 | 435 |  | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 436 |   while (!manifest_valid_) { | 
 | 437 |     // Read data up to the needed limit; this is either the payload header size, | 
 | 438 |     // or the full metadata size (once it becomes known). | 
 | 439 |     const bool do_read_header = !metadata_size_; | 
 | 440 |     CopyDataToBuffer(&c_bytes, &count, | 
 | 441 |                      (do_read_header ? GetManifestOffset() : | 
 | 442 |                       metadata_size_)); | 
 | 443 |  | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 444 |     MetadataParseResult result = ParsePayloadMetadata(buffer_, error); | 
| Gilad Arnold | 5cac591 | 2013-05-24 17:21:17 -0700 | [diff] [blame] | 445 |     if (result == kMetadataParseError) | 
| Don Garrett | e410e0f | 2011-11-10 15:39:01 -0800 | [diff] [blame] | 446 |       return false; | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 447 |     if (result == kMetadataParseInsufficientData) { | 
 | 448 |       // If we just processed the header, make an attempt on the manifest. | 
 | 449 |       if (do_read_header && metadata_size_) | 
 | 450 |         continue; | 
 | 451 |  | 
| Don Garrett | e410e0f | 2011-11-10 15:39:01 -0800 | [diff] [blame] | 452 |       return true; | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 453 |     } | 
| Gilad Arnold | 21504f0 | 2013-05-24 08:51:22 -0700 | [diff] [blame] | 454 |  | 
 | 455 |     // Checks the integrity of the payload manifest. | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 456 |     if ((*error = ValidateManifest()) != ErrorCode::kSuccess) | 
| Gilad Arnold | 21504f0 | 2013-05-24 08:51:22 -0700 | [diff] [blame] | 457 |       return false; | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 458 |     manifest_valid_ = true; | 
| Gilad Arnold | 21504f0 | 2013-05-24 08:51:22 -0700 | [diff] [blame] | 459 |  | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 460 |     // Clear the download buffer. | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 461 |     DiscardBuffer(false); | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 462 |     LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestMetadataSize, | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 463 |                                       metadata_size_)) | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 464 |         << "Unable to save the manifest metadata size."; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 465 |  | 
| Andrew de los Reyes | 89f17be | 2010-10-22 13:39:09 -0700 | [diff] [blame] | 466 |     LogPartitionInfo(manifest_); | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 467 |     if (!PrimeUpdateState()) { | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 468 |       *error = ErrorCode::kDownloadStateInitializationError; | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 469 |       LOG(ERROR) << "Unable to prime the update state."; | 
| Don Garrett | e410e0f | 2011-11-10 15:39:01 -0800 | [diff] [blame] | 470 |       return false; | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 471 |     } | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 472 |  | 
 | 473 |     num_rootfs_operations_ = manifest_.install_operations_size(); | 
 | 474 |     num_total_operations_ = | 
 | 475 |         num_rootfs_operations_ + manifest_.kernel_install_operations_size(); | 
 | 476 |     if (next_operation_num_ > 0) | 
 | 477 |       UpdateOverallProgress(true, "Resuming after "); | 
 | 478 |     LOG(INFO) << "Starting to apply update payload operations"; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 479 |   } | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 480 |  | 
 | 481 |   while (next_operation_num_ < num_total_operations_) { | 
| Jay Srinivasan | 1c0fe79 | 2013-03-28 16:45:25 -0700 | [diff] [blame] | 482 |     // Check if we should cancel the current attempt for any reason. | 
 | 483 |     // In this case, *error will have already been populated with the reason | 
 | 484 |     // why we're cancelling. | 
 | 485 |     if (system_state_->update_attempter()->ShouldCancel(error)) | 
 | 486 |       return false; | 
 | 487 |  | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 488 |     const bool is_kernel_partition = | 
 | 489 |         (next_operation_num_ >= num_rootfs_operations_); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 490 |     const DeltaArchiveManifest_InstallOperation &op = | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 491 |         is_kernel_partition ? | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 492 |         manifest_.kernel_install_operations( | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 493 |             next_operation_num_ - num_rootfs_operations_) : | 
 | 494 |         manifest_.install_operations(next_operation_num_); | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 495 |  | 
 | 496 |     CopyDataToBuffer(&c_bytes, &count, op.data_length()); | 
 | 497 |  | 
 | 498 |     // Check whether we received all of the next operation's data payload. | 
 | 499 |     if (!CanPerformInstallOperation(op)) | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 500 |       return true; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 501 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 502 |     // Validate the operation only if the metadata signature is present. | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 503 |     // Otherwise, keep the old behavior. This serves as a knob to disable | 
 | 504 |     // the validation logic in case we find some regression after rollout. | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 505 |     // NOTE: If hash checks are mandatory and if metadata_signature is empty, | 
 | 506 |     // we would have already failed in ParsePayloadMetadata method and thus not | 
 | 507 |     // even be here. So no need to handle that case again here. | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 508 |     if (!install_plan_->metadata_signature.empty()) { | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 509 |       // Note: Validate must be called only if CanPerformInstallOperation is | 
 | 510 |       // called. Otherwise, we might be failing operations before even if there | 
 | 511 |       // isn't sufficient data to compute the proper hash. | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 512 |       *error = ValidateOperationHash(op); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 513 |       if (*error != ErrorCode::kSuccess) { | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 514 |         if (install_plan_->hash_checks_mandatory) { | 
 | 515 |           LOG(ERROR) << "Mandatory operation hash check failed"; | 
 | 516 |           return false; | 
 | 517 |         } | 
| Jay Srinivasan | f057205 | 2012-10-23 18:12:56 -0700 | [diff] [blame] | 518 |  | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 519 |         // For non-mandatory cases, just send a UMA stat. | 
 | 520 |         LOG(WARNING) << "Ignoring operation validation errors"; | 
| Jay Srinivasan | edce283 | 2012-10-24 18:57:47 -0700 | [diff] [blame] | 521 |         SendUmaStat(*error); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 522 |         *error = ErrorCode::kSuccess; | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 523 |       } | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 524 |     } | 
 | 525 |  | 
| Darin Petkov | 45580e4 | 2010-10-08 14:02:40 -0700 | [diff] [blame] | 526 |     // Makes sure we unblock exit when this operation completes. | 
| Darin Petkov | 9c0baf8 | 2010-10-07 13:44:48 -0700 | [diff] [blame] | 527 |     ScopedTerminatorExitUnblocker exit_unblocker = | 
 | 528 |         ScopedTerminatorExitUnblocker();  // Avoids a compiler unused var bug. | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 529 |  | 
 | 530 |     bool op_result; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 531 |     if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE || | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 532 |         op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) | 
 | 533 |       op_result = HandleOpResult( | 
 | 534 |           PerformReplaceOperation(op, is_kernel_partition), "replace", error); | 
 | 535 |     else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) | 
 | 536 |       op_result = HandleOpResult( | 
 | 537 |           PerformMoveOperation(op, is_kernel_partition), "move", error); | 
 | 538 |     else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) | 
 | 539 |       op_result = HandleOpResult( | 
 | 540 |           PerformBsdiffOperation(op, is_kernel_partition), "bsdiff", error); | 
 | 541 |     else | 
 | 542 |       op_result = HandleOpResult(false, "unknown", error); | 
 | 543 |  | 
 | 544 |     if (!op_result) | 
 | 545 |       return false; | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 546 |  | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 547 |     next_operation_num_++; | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 548 |     UpdateOverallProgress(false, "Completed "); | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 549 |     CheckpointUpdateProgress(); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 550 |   } | 
| Don Garrett | e410e0f | 2011-11-10 15:39:01 -0800 | [diff] [blame] | 551 |   return true; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 552 | } | 
 | 553 |  | 
| David Zeuthen | 8f191b2 | 2013-08-06 12:27:50 -0700 | [diff] [blame] | 554 | bool DeltaPerformer::IsManifestValid() { | 
 | 555 |   return manifest_valid_; | 
 | 556 | } | 
 | 557 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 558 | bool DeltaPerformer::CanPerformInstallOperation( | 
 | 559 |     const chromeos_update_engine::DeltaArchiveManifest_InstallOperation& | 
 | 560 |     operation) { | 
 | 561 |   // Move operations don't require any data blob, so they can always | 
 | 562 |   // be performed | 
 | 563 |   if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) | 
 | 564 |     return true; | 
 | 565 |  | 
 | 566 |   // See if we have the entire data blob in the buffer | 
 | 567 |   if (operation.data_offset() < buffer_offset_) { | 
 | 568 |     LOG(ERROR) << "we threw away data it seems?"; | 
 | 569 |     return false; | 
 | 570 |   } | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 571 |  | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 572 |   return (operation.data_offset() + operation.data_length() <= | 
 | 573 |           buffer_offset_ + buffer_.size()); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 574 | } | 
 | 575 |  | 
 | 576 | bool DeltaPerformer::PerformReplaceOperation( | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 577 |     const DeltaArchiveManifest_InstallOperation& operation, | 
 | 578 |     bool is_kernel_partition) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 579 |   CHECK(operation.type() == \ | 
 | 580 |         DeltaArchiveManifest_InstallOperation_Type_REPLACE || \ | 
 | 581 |         operation.type() == \ | 
 | 582 |         DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); | 
 | 583 |  | 
 | 584 |   // Since we delete data off the beginning of the buffer as we use it, | 
 | 585 |   // the data we need should be exactly at the beginning of the buffer. | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 586 |   TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset()); | 
 | 587 |   TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length()); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 588 |  | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 589 |   // Extract the signature message if it's in this operation. | 
 | 590 |   ExtractSignatureMessage(operation); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 591 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 592 |   DirectExtentWriter direct_writer; | 
 | 593 |   ZeroPadExtentWriter zero_pad_writer(&direct_writer); | 
| Ben Chan | 02f7c1d | 2014-10-18 15:18:02 -0700 | [diff] [blame] | 594 |   unique_ptr<BzipExtentWriter> bzip_writer; | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 595 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 596 |   // Since bzip decompression is optional, we have a variable writer that will | 
 | 597 |   // point to one of the ExtentWriter objects above. | 
| Alex Vakulenko | 88b591f | 2014-08-28 16:48:57 -0700 | [diff] [blame] | 598 |   ExtentWriter* writer = nullptr; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 599 |   if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) { | 
 | 600 |     writer = &zero_pad_writer; | 
 | 601 |   } else if (operation.type() == | 
 | 602 |              DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { | 
 | 603 |     bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer)); | 
 | 604 |     writer = bzip_writer.get(); | 
 | 605 |   } else { | 
 | 606 |     NOTREACHED(); | 
 | 607 |   } | 
 | 608 |  | 
 | 609 |   // Create a vector of extents to pass to the ExtentWriter. | 
 | 610 |   vector<Extent> extents; | 
 | 611 |   for (int i = 0; i < operation.dst_extents_size(); i++) { | 
 | 612 |     extents.push_back(operation.dst_extents(i)); | 
 | 613 |   } | 
 | 614 |  | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 615 |   int fd = is_kernel_partition ? kernel_fd_ : fd_; | 
 | 616 |  | 
 | 617 |   TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_)); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 618 |   TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length())); | 
 | 619 |   TEST_AND_RETURN_FALSE(writer->End()); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 620 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 621 |   // Update buffer | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 622 |   DiscardBuffer(true); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 623 |   return true; | 
 | 624 | } | 
 | 625 |  | 
 | 626 | bool DeltaPerformer::PerformMoveOperation( | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 627 |     const DeltaArchiveManifest_InstallOperation& operation, | 
 | 628 |     bool is_kernel_partition) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 629 |   // Calculate buffer size. Note, this function doesn't do a sliding | 
 | 630 |   // window to copy in case the source and destination blocks overlap. | 
 | 631 |   // If we wanted to do a sliding window, we could program the server | 
 | 632 |   // to generate deltas that effectively did a sliding window. | 
 | 633 |  | 
 | 634 |   uint64_t blocks_to_read = 0; | 
 | 635 |   for (int i = 0; i < operation.src_extents_size(); i++) | 
 | 636 |     blocks_to_read += operation.src_extents(i).num_blocks(); | 
 | 637 |  | 
 | 638 |   uint64_t blocks_to_write = 0; | 
 | 639 |   for (int i = 0; i < operation.dst_extents_size(); i++) | 
 | 640 |     blocks_to_write += operation.dst_extents(i).num_blocks(); | 
 | 641 |  | 
 | 642 |   DCHECK_EQ(blocks_to_write, blocks_to_read); | 
 | 643 |   vector<char> buf(blocks_to_write * block_size_); | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 644 |  | 
 | 645 |   int fd = is_kernel_partition ? kernel_fd_ : fd_; | 
 | 646 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 647 |   // Read in bytes. | 
 | 648 |   ssize_t bytes_read = 0; | 
 | 649 |   for (int i = 0; i < operation.src_extents_size(); i++) { | 
 | 650 |     ssize_t bytes_read_this_iteration = 0; | 
 | 651 |     const Extent& extent = operation.src_extents(i); | 
| Darin Petkov | 8a075a7 | 2013-04-25 14:46:09 +0200 | [diff] [blame] | 652 |     const size_t bytes = extent.num_blocks() * block_size_; | 
 | 653 |     if (extent.start_block() == kSparseHole) { | 
 | 654 |       bytes_read_this_iteration = bytes; | 
 | 655 |       memset(&buf[bytes_read], 0, bytes); | 
 | 656 |     } else { | 
 | 657 |       TEST_AND_RETURN_FALSE(utils::PReadAll(fd, | 
 | 658 |                                             &buf[bytes_read], | 
 | 659 |                                             bytes, | 
 | 660 |                                             extent.start_block() * block_size_, | 
 | 661 |                                             &bytes_read_this_iteration)); | 
 | 662 |     } | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 663 |     TEST_AND_RETURN_FALSE( | 
| Darin Petkov | 8a075a7 | 2013-04-25 14:46:09 +0200 | [diff] [blame] | 664 |         bytes_read_this_iteration == static_cast<ssize_t>(bytes)); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 665 |     bytes_read += bytes_read_this_iteration; | 
 | 666 |   } | 
 | 667 |  | 
| Darin Petkov | 45580e4 | 2010-10-08 14:02:40 -0700 | [diff] [blame] | 668 |   // If this is a non-idempotent operation, request a delayed exit and clear the | 
 | 669 |   // update state in case the operation gets interrupted. Do this as late as | 
 | 670 |   // possible. | 
 | 671 |   if (!IsIdempotentOperation(operation)) { | 
 | 672 |     Terminator::set_exit_blocked(true); | 
 | 673 |     ResetUpdateProgress(prefs_, true); | 
 | 674 |   } | 
 | 675 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 676 |   // Write bytes out. | 
 | 677 |   ssize_t bytes_written = 0; | 
 | 678 |   for (int i = 0; i < operation.dst_extents_size(); i++) { | 
 | 679 |     const Extent& extent = operation.dst_extents(i); | 
| Darin Petkov | 8a075a7 | 2013-04-25 14:46:09 +0200 | [diff] [blame] | 680 |     const size_t bytes = extent.num_blocks() * block_size_; | 
 | 681 |     if (extent.start_block() == kSparseHole) { | 
| Darin Petkov | 741a822 | 2013-05-02 10:02:34 +0200 | [diff] [blame] | 682 |       DCHECK(buf.begin() + bytes_written == | 
 | 683 |              std::search_n(buf.begin() + bytes_written, | 
 | 684 |                            buf.begin() + bytes_written + bytes, | 
 | 685 |                            bytes, 0)); | 
| Darin Petkov | 8a075a7 | 2013-04-25 14:46:09 +0200 | [diff] [blame] | 686 |     } else { | 
 | 687 |       TEST_AND_RETURN_FALSE( | 
 | 688 |           utils::PWriteAll(fd, | 
 | 689 |                            &buf[bytes_written], | 
 | 690 |                            bytes, | 
 | 691 |                            extent.start_block() * block_size_)); | 
 | 692 |     } | 
 | 693 |     bytes_written += bytes; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 694 |   } | 
 | 695 |   DCHECK_EQ(bytes_written, bytes_read); | 
 | 696 |   DCHECK_EQ(bytes_written, static_cast<ssize_t>(buf.size())); | 
 | 697 |   return true; | 
 | 698 | } | 
 | 699 |  | 
 | 700 | bool DeltaPerformer::ExtentsToBsdiffPositionsString( | 
 | 701 |     const RepeatedPtrField<Extent>& extents, | 
 | 702 |     uint64_t block_size, | 
 | 703 |     uint64_t full_length, | 
 | 704 |     string* positions_string) { | 
 | 705 |   string ret; | 
 | 706 |   uint64_t length = 0; | 
 | 707 |   for (int i = 0; i < extents.size(); i++) { | 
 | 708 |     Extent extent = extents.Get(i); | 
 | 709 |     int64_t start = extent.start_block(); | 
 | 710 |     uint64_t this_length = min(full_length - length, | 
 | 711 |                                extent.num_blocks() * block_size); | 
 | 712 |     if (start == static_cast<int64_t>(kSparseHole)) | 
 | 713 |       start = -1; | 
 | 714 |     else | 
 | 715 |       start *= block_size; | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 716 |     ret += base::StringPrintf("%" PRIi64 ":%" PRIu64 ",", start, this_length); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 717 |     length += this_length; | 
 | 718 |   } | 
 | 719 |   TEST_AND_RETURN_FALSE(length == full_length); | 
 | 720 |   if (!ret.empty()) | 
 | 721 |     ret.resize(ret.size() - 1);  // Strip trailing comma off | 
 | 722 |   *positions_string = ret; | 
 | 723 |   return true; | 
 | 724 | } | 
 | 725 |  | 
 | 726 | bool DeltaPerformer::PerformBsdiffOperation( | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 727 |     const DeltaArchiveManifest_InstallOperation& operation, | 
 | 728 |     bool is_kernel_partition) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 729 |   // Since we delete data off the beginning of the buffer as we use it, | 
 | 730 |   // the data we need should be exactly at the beginning of the buffer. | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 731 |   TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset()); | 
 | 732 |   TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length()); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 733 |  | 
 | 734 |   string input_positions; | 
 | 735 |   TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(), | 
 | 736 |                                                        block_size_, | 
 | 737 |                                                        operation.src_length(), | 
 | 738 |                                                        &input_positions)); | 
 | 739 |   string output_positions; | 
 | 740 |   TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.dst_extents(), | 
 | 741 |                                                        block_size_, | 
 | 742 |                                                        operation.dst_length(), | 
 | 743 |                                                        &output_positions)); | 
 | 744 |  | 
 | 745 |   string temp_filename; | 
 | 746 |   TEST_AND_RETURN_FALSE(utils::MakeTempFile("/tmp/au_patch.XXXXXX", | 
 | 747 |                                             &temp_filename, | 
| Alex Vakulenko | 88b591f | 2014-08-28 16:48:57 -0700 | [diff] [blame] | 748 |                                             nullptr)); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 749 |   ScopedPathUnlinker path_unlinker(temp_filename); | 
 | 750 |   { | 
 | 751 |     int fd = open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); | 
 | 752 |     ScopedFdCloser fd_closer(&fd); | 
 | 753 |     TEST_AND_RETURN_FALSE( | 
 | 754 |         utils::WriteAll(fd, &buffer_[0], operation.data_length())); | 
 | 755 |   } | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 756 |  | 
| Darin Petkov | 7f2ec75 | 2013-04-03 14:45:19 +0200 | [diff] [blame] | 757 |   // Update the buffer to release the patch data memory as soon as the patch | 
 | 758 |   // file is written out. | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 759 |   DiscardBuffer(true); | 
| Darin Petkov | 7f2ec75 | 2013-04-03 14:45:19 +0200 | [diff] [blame] | 760 |  | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 761 |   int fd = is_kernel_partition ? kernel_fd_ : fd_; | 
| Alex Vakulenko | 75039d7 | 2014-03-25 12:36:28 -0700 | [diff] [blame] | 762 |   const string path = base::StringPrintf("/proc/self/fd/%d", fd); | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 763 |  | 
| Darin Petkov | 45580e4 | 2010-10-08 14:02:40 -0700 | [diff] [blame] | 764 |   // If this is a non-idempotent operation, request a delayed exit and clear the | 
 | 765 |   // update state in case the operation gets interrupted. Do this as late as | 
 | 766 |   // possible. | 
 | 767 |   if (!IsIdempotentOperation(operation)) { | 
 | 768 |     Terminator::set_exit_blocked(true); | 
 | 769 |     ResetUpdateProgress(prefs_, true); | 
 | 770 |   } | 
 | 771 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 772 |   vector<string> cmd; | 
 | 773 |   cmd.push_back(kBspatchPath); | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 774 |   cmd.push_back(path); | 
 | 775 |   cmd.push_back(path); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 776 |   cmd.push_back(temp_filename); | 
 | 777 |   cmd.push_back(input_positions); | 
 | 778 |   cmd.push_back(output_positions); | 
 | 779 |   int return_code = 0; | 
| Andrew de los Reyes | 5a23283 | 2010-10-12 16:20:54 -0700 | [diff] [blame] | 780 |   TEST_AND_RETURN_FALSE( | 
 | 781 |       Subprocess::SynchronousExecFlags(cmd, | 
| Darin Petkov | 85d02b7 | 2011-05-17 13:25:51 -0700 | [diff] [blame] | 782 |                                        G_SPAWN_LEAVE_DESCRIPTORS_OPEN, | 
| Andrew de los Reyes | 5a23283 | 2010-10-12 16:20:54 -0700 | [diff] [blame] | 783 |                                        &return_code, | 
| Alex Vakulenko | 88b591f | 2014-08-28 16:48:57 -0700 | [diff] [blame] | 784 |                                        nullptr)); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 785 |   TEST_AND_RETURN_FALSE(return_code == 0); | 
 | 786 |  | 
 | 787 |   if (operation.dst_length() % block_size_) { | 
 | 788 |     // Zero out rest of final block. | 
 | 789 |     // TODO(adlr): build this into bspatch; it's more efficient that way. | 
 | 790 |     const Extent& last_extent = | 
 | 791 |         operation.dst_extents(operation.dst_extents_size() - 1); | 
 | 792 |     const uint64_t end_byte = | 
 | 793 |         (last_extent.start_block() + last_extent.num_blocks()) * block_size_; | 
 | 794 |     const uint64_t begin_byte = | 
 | 795 |         end_byte - (block_size_ - operation.dst_length() % block_size_); | 
 | 796 |     vector<char> zeros(end_byte - begin_byte); | 
 | 797 |     TEST_AND_RETURN_FALSE( | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 798 |         utils::PWriteAll(fd, &zeros[0], end_byte - begin_byte, begin_byte)); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 799 |   } | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 800 |   return true; | 
 | 801 | } | 
 | 802 |  | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 803 | bool DeltaPerformer::ExtractSignatureMessage( | 
 | 804 |     const DeltaArchiveManifest_InstallOperation& operation) { | 
 | 805 |   if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_REPLACE || | 
 | 806 |       !manifest_.has_signatures_offset() || | 
 | 807 |       manifest_.signatures_offset() != operation.data_offset()) { | 
 | 808 |     return false; | 
 | 809 |   } | 
 | 810 |   TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() && | 
 | 811 |                         manifest_.signatures_size() == operation.data_length()); | 
 | 812 |   TEST_AND_RETURN_FALSE(signatures_message_data_.empty()); | 
 | 813 |   TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset()); | 
 | 814 |   TEST_AND_RETURN_FALSE(buffer_.size() >= manifest_.signatures_size()); | 
| Darin Petkov | 4f0a07b | 2011-05-25 16:47:20 -0700 | [diff] [blame] | 815 |   signatures_message_data_.assign( | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 816 |       buffer_.begin(), | 
 | 817 |       buffer_.begin() + manifest_.signatures_size()); | 
| Darin Petkov | 4f0a07b | 2011-05-25 16:47:20 -0700 | [diff] [blame] | 818 |  | 
 | 819 |   // Save the signature blob because if the update is interrupted after the | 
 | 820 |   // download phase we don't go through this path anymore. Some alternatives to | 
 | 821 |   // consider: | 
 | 822 |   // | 
 | 823 |   // 1. On resume, re-download the signature blob from the server and re-verify | 
 | 824 |   // it. | 
 | 825 |   // | 
 | 826 |   // 2. Verify the signature as soon as it's received and don't checkpoint the | 
 | 827 |   // blob and the signed sha-256 context. | 
 | 828 |   LOG_IF(WARNING, !prefs_->SetString(kPrefsUpdateStateSignatureBlob, | 
 | 829 |                                      string(&signatures_message_data_[0], | 
 | 830 |                                             signatures_message_data_.size()))) | 
 | 831 |       << "Unable to store the signature blob."; | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 832 |   // The hash of all data consumed so far should be verified against the signed | 
 | 833 |   // hash. | 
 | 834 |   signed_hash_context_ = hash_calculator_.GetContext(); | 
 | 835 |   LOG_IF(WARNING, !prefs_->SetString(kPrefsUpdateStateSignedSHA256Context, | 
 | 836 |                                      signed_hash_context_)) | 
 | 837 |       << "Unable to store the signed hash context."; | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 838 |   LOG(INFO) << "Extracted signature data of size " | 
 | 839 |             << manifest_.signatures_size() << " at " | 
 | 840 |             << manifest_.signatures_offset(); | 
 | 841 |   return true; | 
 | 842 | } | 
 | 843 |  | 
| David Zeuthen | e7f8917 | 2013-10-31 10:21:04 -0700 | [diff] [blame] | 844 | bool DeltaPerformer::GetPublicKeyFromResponse(base::FilePath *out_tmp_key) { | 
 | 845 |   if (system_state_->hardware()->IsOfficialBuild() || | 
 | 846 |       utils::FileExists(public_key_path_.c_str()) || | 
 | 847 |       install_plan_->public_key_rsa.empty()) | 
 | 848 |     return false; | 
 | 849 |  | 
 | 850 |   if (!utils::DecodeAndStoreBase64String(install_plan_->public_key_rsa, | 
 | 851 |                                          out_tmp_key)) | 
 | 852 |     return false; | 
 | 853 |  | 
 | 854 |   return true; | 
 | 855 | } | 
 | 856 |  | 
| David Zeuthen | a99981f | 2013-04-29 13:42:47 -0700 | [diff] [blame] | 857 | ErrorCode DeltaPerformer::ValidateMetadataSignature( | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 858 |     const char* metadata, uint64_t metadata_size) { | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 859 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 860 |   if (install_plan_->metadata_signature.empty()) { | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 861 |     if (install_plan_->hash_checks_mandatory) { | 
 | 862 |       LOG(ERROR) << "Missing mandatory metadata signature in Omaha response"; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 863 |       return ErrorCode::kDownloadMetadataSignatureMissingError; | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 864 |     } | 
 | 865 |  | 
 | 866 |     // For non-mandatory cases, just send a UMA stat. | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 867 |     LOG(WARNING) << "Cannot validate metadata as the signature is empty"; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 868 |     SendUmaStat(ErrorCode::kDownloadMetadataSignatureMissingError); | 
 | 869 |     return ErrorCode::kSuccess; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 870 |   } | 
 | 871 |  | 
 | 872 |   // Convert base64-encoded signature to raw bytes. | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 873 |   vector<char> metadata_signature; | 
 | 874 |   if (!OmahaHashCalculator::Base64Decode(install_plan_->metadata_signature, | 
 | 875 |                                          &metadata_signature)) { | 
 | 876 |     LOG(ERROR) << "Unable to decode base64 metadata signature: " | 
 | 877 |                << install_plan_->metadata_signature; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 878 |     return ErrorCode::kDownloadMetadataSignatureError; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 879 |   } | 
 | 880 |  | 
| David Zeuthen | e7f8917 | 2013-10-31 10:21:04 -0700 | [diff] [blame] | 881 |   // See if we should use the public RSA key in the Omaha response. | 
 | 882 |   base::FilePath path_to_public_key(public_key_path_); | 
 | 883 |   base::FilePath tmp_key; | 
 | 884 |   if (GetPublicKeyFromResponse(&tmp_key)) | 
 | 885 |     path_to_public_key = tmp_key; | 
 | 886 |   ScopedPathUnlinker tmp_key_remover(tmp_key.value()); | 
 | 887 |   if (tmp_key.empty()) | 
 | 888 |     tmp_key_remover.set_should_remove(false); | 
 | 889 |  | 
 | 890 |   LOG(INFO) << "Verifying metadata hash signature using public key: " | 
 | 891 |             << path_to_public_key.value(); | 
 | 892 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 893 |   vector<char> expected_metadata_hash; | 
| Alex Deymo | 923d8fa | 2014-07-15 17:58:51 -0700 | [diff] [blame] | 894 |   if (!PayloadVerifier::GetRawHashFromSignature(metadata_signature, | 
 | 895 |                                                 path_to_public_key.value(), | 
 | 896 |                                                 &expected_metadata_hash)) { | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 897 |     LOG(ERROR) << "Unable to compute expected hash from metadata signature"; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 898 |     return ErrorCode::kDownloadMetadataSignatureError; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 899 |   } | 
 | 900 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 901 |   OmahaHashCalculator metadata_hasher; | 
 | 902 |   metadata_hasher.Update(metadata, metadata_size); | 
 | 903 |   if (!metadata_hasher.Finalize()) { | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 904 |     LOG(ERROR) << "Unable to compute actual hash of manifest"; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 905 |     return ErrorCode::kDownloadMetadataSignatureVerificationError; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 906 |   } | 
 | 907 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 908 |   vector<char> calculated_metadata_hash = metadata_hasher.raw_hash(); | 
| Alex Deymo | 923d8fa | 2014-07-15 17:58:51 -0700 | [diff] [blame] | 909 |   PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash); | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 910 |   if (calculated_metadata_hash.empty()) { | 
 | 911 |     LOG(ERROR) << "Computed actual hash of metadata is empty."; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 912 |     return ErrorCode::kDownloadMetadataSignatureVerificationError; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 913 |   } | 
 | 914 |  | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 915 |   if (calculated_metadata_hash != expected_metadata_hash) { | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 916 |     LOG(ERROR) << "Manifest hash verification failed. Expected hash = "; | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 917 |     utils::HexDumpVector(expected_metadata_hash); | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 918 |     LOG(ERROR) << "Calculated hash = "; | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 919 |     utils::HexDumpVector(calculated_metadata_hash); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 920 |     return ErrorCode::kDownloadMetadataSignatureMismatch; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 921 |   } | 
 | 922 |  | 
| David Zeuthen | bc27aac | 2013-11-26 11:17:48 -0800 | [diff] [blame] | 923 |   // The autoupdate_CatchBadSignatures test checks for this string in | 
 | 924 |   // log-files. Keep in sync. | 
| David Zeuthen | e7f8917 | 2013-10-31 10:21:04 -0700 | [diff] [blame] | 925 |   LOG(INFO) << "Metadata hash signature matches value in Omaha response."; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 926 |   return ErrorCode::kSuccess; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 927 | } | 
 | 928 |  | 
| Gilad Arnold | 21504f0 | 2013-05-24 08:51:22 -0700 | [diff] [blame] | 929 | ErrorCode DeltaPerformer::ValidateManifest() { | 
| Don Garrett | b8dd1d9 | 2013-11-22 17:40:02 -0800 | [diff] [blame] | 930 |   // Perform assorted checks to sanity check the manifest, make sure it | 
 | 931 |   // matches data from other sources, and that it is a supported version. | 
| Gilad Arnold | 21504f0 | 2013-05-24 08:51:22 -0700 | [diff] [blame] | 932 |   // | 
 | 933 |   // TODO(garnold) in general, the presence of an old partition hash should be | 
 | 934 |   // the sole indicator for a delta update, as we would generally like update | 
 | 935 |   // payloads to be self contained and not assume an Omaha response to tell us | 
 | 936 |   // that. However, since this requires some massive reengineering of the update | 
 | 937 |   // flow (making filesystem copying happen conditionally only *after* | 
 | 938 |   // downloading and parsing of the update manifest) we'll put it off for now. | 
 | 939 |   // See chromium-os:7597 for further discussion. | 
| Don Garrett | b8dd1d9 | 2013-11-22 17:40:02 -0800 | [diff] [blame] | 940 |   if (install_plan_->is_full_update) { | 
 | 941 |     if (manifest_.has_old_kernel_info() || manifest_.has_old_rootfs_info()) { | 
 | 942 |       LOG(ERROR) << "Purported full payload contains old partition " | 
 | 943 |                     "hash(es), aborting update"; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 944 |       return ErrorCode::kPayloadMismatchedType; | 
| Don Garrett | b8dd1d9 | 2013-11-22 17:40:02 -0800 | [diff] [blame] | 945 |     } | 
 | 946 |  | 
 | 947 |     if (manifest_.minor_version() != kFullPayloadMinorVersion) { | 
 | 948 |       LOG(ERROR) << "Manifest contains minor version " | 
 | 949 |                  << manifest_.minor_version() | 
 | 950 |                  << ", but all full payloads should have version " | 
 | 951 |                  << kFullPayloadMinorVersion << "."; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 952 |       return ErrorCode::kUnsupportedMinorPayloadVersion; | 
| Don Garrett | b8dd1d9 | 2013-11-22 17:40:02 -0800 | [diff] [blame] | 953 |     } | 
 | 954 |   } else { | 
 | 955 |     if (manifest_.minor_version() != kSupportedMinorPayloadVersion) { | 
 | 956 |       LOG(ERROR) << "Manifest contains minor version " | 
 | 957 |                  << manifest_.minor_version() | 
 | 958 |                  << " not the supported " | 
 | 959 |                  << kSupportedMinorPayloadVersion; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 960 |       return ErrorCode::kUnsupportedMinorPayloadVersion; | 
| Don Garrett | b8dd1d9 | 2013-11-22 17:40:02 -0800 | [diff] [blame] | 961 |     } | 
| Gilad Arnold | 21504f0 | 2013-05-24 08:51:22 -0700 | [diff] [blame] | 962 |   } | 
 | 963 |  | 
 | 964 |   // TODO(garnold) we should be adding more and more manifest checks, such as | 
 | 965 |   // partition boundaries etc (see chromium-os:37661). | 
 | 966 |  | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 967 |   return ErrorCode::kSuccess; | 
| Gilad Arnold | 21504f0 | 2013-05-24 08:51:22 -0700 | [diff] [blame] | 968 | } | 
 | 969 |  | 
| David Zeuthen | a99981f | 2013-04-29 13:42:47 -0700 | [diff] [blame] | 970 | ErrorCode DeltaPerformer::ValidateOperationHash( | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 971 |     const DeltaArchiveManifest_InstallOperation& operation) { | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 972 |  | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 973 |   if (!operation.data_sha256_hash().size()) { | 
 | 974 |     if (!operation.data_length()) { | 
 | 975 |       // Operations that do not have any data blob won't have any operation hash | 
 | 976 |       // either. So, these operations are always considered validated since the | 
| Jay Srinivasan | f431870 | 2012-09-24 11:56:24 -0700 | [diff] [blame] | 977 |       // metadata that contains all the non-data-blob portions of the operation | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 978 |       // has already been validated. This is true for both HTTP and HTTPS cases. | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 979 |       return ErrorCode::kSuccess; | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 980 |     } | 
 | 981 |  | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 982 |     // No hash is present for an operation that has data blobs. This shouldn't | 
 | 983 |     // happen normally for any client that has this code, because the | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 984 |     // corresponding update should have been produced with the operation | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 985 |     // hashes. So if it happens it means either we've turned operation hash | 
 | 986 |     // generation off in DeltaDiffGenerator or it's a regression of some sort. | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 987 |     // One caveat though: The last operation is a dummy signature operation | 
 | 988 |     // that doesn't have a hash at the time the manifest is created. So we | 
 | 989 |     // should not complaint about that operation. This operation can be | 
 | 990 |     // recognized by the fact that it's offset is mentioned in the manifest. | 
 | 991 |     if (manifest_.signatures_offset() && | 
 | 992 |         manifest_.signatures_offset() == operation.data_offset()) { | 
 | 993 |       LOG(INFO) << "Skipping hash verification for signature operation " | 
 | 994 |                 << next_operation_num_ + 1; | 
 | 995 |     } else { | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 996 |       if (install_plan_->hash_checks_mandatory) { | 
 | 997 |         LOG(ERROR) << "Missing mandatory operation hash for operation " | 
 | 998 |                    << next_operation_num_ + 1; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 999 |         return ErrorCode::kDownloadOperationHashMissingError; | 
| Jay Srinivasan | 738fdf3 | 2012-12-07 17:40:54 -0800 | [diff] [blame] | 1000 |       } | 
 | 1001 |  | 
 | 1002 |       // For non-mandatory cases, just send a UMA stat. | 
 | 1003 |       LOG(WARNING) << "Cannot validate operation " << next_operation_num_ + 1 | 
 | 1004 |                    << " as there's no operation hash in manifest"; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1005 |       SendUmaStat(ErrorCode::kDownloadOperationHashMissingError); | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 1006 |     } | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1007 |     return ErrorCode::kSuccess; | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 1008 |   } | 
 | 1009 |  | 
 | 1010 |   vector<char> expected_op_hash; | 
 | 1011 |   expected_op_hash.assign(operation.data_sha256_hash().data(), | 
 | 1012 |                           (operation.data_sha256_hash().data() + | 
 | 1013 |                            operation.data_sha256_hash().size())); | 
 | 1014 |  | 
 | 1015 |   OmahaHashCalculator operation_hasher; | 
 | 1016 |   operation_hasher.Update(&buffer_[0], operation.data_length()); | 
 | 1017 |   if (!operation_hasher.Finalize()) { | 
 | 1018 |     LOG(ERROR) << "Unable to compute actual hash of operation " | 
 | 1019 |                << next_operation_num_; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1020 |     return ErrorCode::kDownloadOperationHashVerificationError; | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 1021 |   } | 
 | 1022 |  | 
 | 1023 |   vector<char> calculated_op_hash = operation_hasher.raw_hash(); | 
 | 1024 |   if (calculated_op_hash != expected_op_hash) { | 
 | 1025 |     LOG(ERROR) << "Hash verification failed for operation " | 
 | 1026 |                << next_operation_num_ << ". Expected hash = "; | 
 | 1027 |     utils::HexDumpVector(expected_op_hash); | 
 | 1028 |     LOG(ERROR) << "Calculated hash over " << operation.data_length() | 
 | 1029 |                << " bytes at offset: " << operation.data_offset() << " = "; | 
 | 1030 |     utils::HexDumpVector(calculated_op_hash); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1031 |     return ErrorCode::kDownloadOperationHashMismatch; | 
| Jay Srinivasan | 00f76b6 | 2012-09-17 18:48:36 -0700 | [diff] [blame] | 1032 |   } | 
 | 1033 |  | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1034 |   return ErrorCode::kSuccess; | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1035 | } | 
 | 1036 |  | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1037 | #define TEST_AND_RETURN_VAL(_retval, _condition)                \ | 
 | 1038 |   do {                                                          \ | 
 | 1039 |     if (!(_condition)) {                                        \ | 
 | 1040 |       LOG(ERROR) << "VerifyPayload failure: " << #_condition;   \ | 
 | 1041 |       return _retval;                                           \ | 
 | 1042 |     }                                                           \ | 
 | 1043 |   } while (0); | 
| Andrew de los Reyes | fb830ba | 2011-04-04 11:42:43 -0700 | [diff] [blame] | 1044 |  | 
| David Zeuthen | a99981f | 2013-04-29 13:42:47 -0700 | [diff] [blame] | 1045 | ErrorCode DeltaPerformer::VerifyPayload( | 
| Alex Deymo | f329b93 | 2014-10-30 01:37:48 -0700 | [diff] [blame] | 1046 |     const string& update_check_response_hash, | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1047 |     const uint64_t update_check_response_size) { | 
| David Zeuthen | e7f8917 | 2013-10-31 10:21:04 -0700 | [diff] [blame] | 1048 |  | 
 | 1049 |   // See if we should use the public RSA key in the Omaha response. | 
 | 1050 |   base::FilePath path_to_public_key(public_key_path_); | 
 | 1051 |   base::FilePath tmp_key; | 
 | 1052 |   if (GetPublicKeyFromResponse(&tmp_key)) | 
 | 1053 |     path_to_public_key = tmp_key; | 
 | 1054 |   ScopedPathUnlinker tmp_key_remover(tmp_key.value()); | 
 | 1055 |   if (tmp_key.empty()) | 
 | 1056 |     tmp_key_remover.set_should_remove(false); | 
 | 1057 |  | 
 | 1058 |   LOG(INFO) << "Verifying payload using public key: " | 
 | 1059 |             << path_to_public_key.value(); | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 1060 |  | 
| Jay Srinivasan | 0d8fb40 | 2012-05-07 19:19:38 -0700 | [diff] [blame] | 1061 |   // Verifies the download size. | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1062 |   TEST_AND_RETURN_VAL(ErrorCode::kPayloadSizeMismatchError, | 
| Jay Srinivasan | 0d8fb40 | 2012-05-07 19:19:38 -0700 | [diff] [blame] | 1063 |                       update_check_response_size == | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 1064 |                       metadata_size_ + buffer_offset_); | 
| Jay Srinivasan | 0d8fb40 | 2012-05-07 19:19:38 -0700 | [diff] [blame] | 1065 |  | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1066 |   // Verifies the payload hash. | 
 | 1067 |   const string& payload_hash_data = hash_calculator_.hash(); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1068 |   TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadVerificationError, | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1069 |                       !payload_hash_data.empty()); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1070 |   TEST_AND_RETURN_VAL(ErrorCode::kPayloadHashMismatchError, | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1071 |                       payload_hash_data == update_check_response_hash); | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 1072 |  | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 1073 |   // Verifies the signed payload hash. | 
| David Zeuthen | e7f8917 | 2013-10-31 10:21:04 -0700 | [diff] [blame] | 1074 |   if (!utils::FileExists(path_to_public_key.value().c_str())) { | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 1075 |     LOG(WARNING) << "Not verifying signed delta payload -- missing public key."; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1076 |     return ErrorCode::kSuccess; | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 1077 |   } | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1078 |   TEST_AND_RETURN_VAL(ErrorCode::kSignedDeltaPayloadExpectedError, | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1079 |                       !signatures_message_data_.empty()); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 1080 |   vector<char> signed_hash_data; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1081 |   TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError, | 
| Alex Deymo | 923d8fa | 2014-07-15 17:58:51 -0700 | [diff] [blame] | 1082 |                       PayloadVerifier::VerifySignature( | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1083 |                           signatures_message_data_, | 
| David Zeuthen | e7f8917 | 2013-10-31 10:21:04 -0700 | [diff] [blame] | 1084 |                           path_to_public_key.value(), | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1085 |                           &signed_hash_data)); | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 1086 |   OmahaHashCalculator signed_hasher; | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1087 |   TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError, | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1088 |                       signed_hasher.SetContext(signed_hash_context_)); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1089 |   TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError, | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1090 |                       signed_hasher.Finalize()); | 
| Andrew de los Reyes | bdfaaf0 | 2011-03-30 10:35:12 -0700 | [diff] [blame] | 1091 |   vector<char> hash_data = signed_hasher.raw_hash(); | 
| Alex Deymo | 923d8fa | 2014-07-15 17:58:51 -0700 | [diff] [blame] | 1092 |   PayloadVerifier::PadRSA2048SHA256Hash(&hash_data); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1093 |   TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError, | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1094 |                       !hash_data.empty()); | 
| Andrew de los Reyes | fb830ba | 2011-04-04 11:42:43 -0700 | [diff] [blame] | 1095 |   if (hash_data != signed_hash_data) { | 
| David Zeuthen | bc27aac | 2013-11-26 11:17:48 -0800 | [diff] [blame] | 1096 |     // The autoupdate_CatchBadSignatures test checks for this string | 
 | 1097 |     // in log-files. Keep in sync. | 
| Andrew de los Reyes | 771e1bd | 2011-08-30 14:47:23 -0700 | [diff] [blame] | 1098 |     LOG(ERROR) << "Public key verification failed, thus update failed. " | 
| Andrew de los Reyes | fb830ba | 2011-04-04 11:42:43 -0700 | [diff] [blame] | 1099 |         "Attached Signature:"; | 
 | 1100 |     utils::HexDumpVector(signed_hash_data); | 
 | 1101 |     LOG(ERROR) << "Computed Signature:"; | 
 | 1102 |     utils::HexDumpVector(hash_data); | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1103 |     return ErrorCode::kDownloadPayloadPubKeyVerificationError; | 
| Andrew de los Reyes | fb830ba | 2011-04-04 11:42:43 -0700 | [diff] [blame] | 1104 |   } | 
| Jay Srinivasan | 2b5a0f0 | 2012-12-19 17:25:56 -0800 | [diff] [blame] | 1105 |  | 
| David Zeuthen | e7f8917 | 2013-10-31 10:21:04 -0700 | [diff] [blame] | 1106 |   LOG(INFO) << "Payload hash matches value in payload."; | 
 | 1107 |  | 
| Jay Srinivasan | 2b5a0f0 | 2012-12-19 17:25:56 -0800 | [diff] [blame] | 1108 |   // At this point, we are guaranteed to have downloaded a full payload, i.e | 
 | 1109 |   // the one whose size matches the size mentioned in Omaha response. If any | 
 | 1110 |   // errors happen after this, it's likely a problem with the payload itself or | 
 | 1111 |   // the state of the system and not a problem with the URL or network.  So, | 
| Jay Srinivasan | 0826288 | 2012-12-28 19:29:43 -0800 | [diff] [blame] | 1112 |   // indicate that to the payload state so that AU can backoff appropriately. | 
| Jay Srinivasan | 2b5a0f0 | 2012-12-19 17:25:56 -0800 | [diff] [blame] | 1113 |   system_state_->payload_state()->DownloadComplete(); | 
 | 1114 |  | 
| Gilad Arnold | d1c4d2d | 2014-06-05 14:07:53 -0700 | [diff] [blame] | 1115 |   return ErrorCode::kSuccess; | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 1116 | } | 
 | 1117 |  | 
| Darin Petkov | 3aefa86 | 2010-12-07 14:45:00 -0800 | [diff] [blame] | 1118 | bool DeltaPerformer::GetNewPartitionInfo(uint64_t* kernel_size, | 
 | 1119 |                                          vector<char>* kernel_hash, | 
 | 1120 |                                          uint64_t* rootfs_size, | 
 | 1121 |                                          vector<char>* rootfs_hash) { | 
| Darin Petkov | 2dd0109 | 2010-10-08 15:43:05 -0700 | [diff] [blame] | 1122 |   TEST_AND_RETURN_FALSE(manifest_valid_ && | 
 | 1123 |                         manifest_.has_new_kernel_info() && | 
 | 1124 |                         manifest_.has_new_rootfs_info()); | 
| Darin Petkov | 3aefa86 | 2010-12-07 14:45:00 -0800 | [diff] [blame] | 1125 |   *kernel_size = manifest_.new_kernel_info().size(); | 
 | 1126 |   *rootfs_size = manifest_.new_rootfs_info().size(); | 
 | 1127 |   vector<char> new_kernel_hash(manifest_.new_kernel_info().hash().begin(), | 
 | 1128 |                                manifest_.new_kernel_info().hash().end()); | 
 | 1129 |   vector<char> new_rootfs_hash(manifest_.new_rootfs_info().hash().begin(), | 
 | 1130 |                                manifest_.new_rootfs_info().hash().end()); | 
 | 1131 |   kernel_hash->swap(new_kernel_hash); | 
 | 1132 |   rootfs_hash->swap(new_rootfs_hash); | 
| Darin Petkov | 2dd0109 | 2010-10-08 15:43:05 -0700 | [diff] [blame] | 1133 |   return true; | 
 | 1134 | } | 
 | 1135 |  | 
| Andrew de los Reyes | 100bb7d | 2011-08-09 17:35:07 -0700 | [diff] [blame] | 1136 | namespace { | 
 | 1137 | void LogVerifyError(bool is_kern, | 
 | 1138 |                     const string& local_hash, | 
 | 1139 |                     const string& expected_hash) { | 
 | 1140 |   const char* type = is_kern ? "kernel" : "rootfs"; | 
 | 1141 |   LOG(ERROR) << "This is a server-side error due to " | 
 | 1142 |              << "mismatched delta update image!"; | 
 | 1143 |   LOG(ERROR) << "The delta I've been given contains a " << type << " delta " | 
 | 1144 |              << "update that must be applied over a " << type << " with " | 
 | 1145 |              << "a specific checksum, but the " << type << " we're starting " | 
 | 1146 |              << "with doesn't have that checksum! This means that " | 
 | 1147 |              << "the delta I've been given doesn't match my existing " | 
 | 1148 |              << "system. The " << type << " partition I have has hash: " | 
 | 1149 |              << local_hash << " but the update expected me to have " | 
 | 1150 |              << expected_hash << " ."; | 
 | 1151 |   if (is_kern) { | 
 | 1152 |     LOG(INFO) << "To get the checksum of a kernel partition on a " | 
 | 1153 |               << "booted machine, run this command (change /dev/sda2 " | 
 | 1154 |               << "as needed): dd if=/dev/sda2 bs=1M 2>/dev/null | " | 
 | 1155 |               << "openssl dgst -sha256 -binary | openssl base64"; | 
 | 1156 |   } else { | 
 | 1157 |     LOG(INFO) << "To get the checksum of a rootfs partition on a " | 
 | 1158 |               << "booted machine, run this command (change /dev/sda3 " | 
 | 1159 |               << "as needed): dd if=/dev/sda3 bs=1M count=$(( " | 
 | 1160 |               << "$(dumpe2fs /dev/sda3  2>/dev/null | grep 'Block count' " | 
 | 1161 |               << "| sed 's/[^0-9]*//') / 256 )) | " | 
 | 1162 |               << "openssl dgst -sha256 -binary | openssl base64"; | 
 | 1163 |   } | 
 | 1164 |   LOG(INFO) << "To get the checksum of partitions in a bin file, " | 
 | 1165 |             << "run: .../src/scripts/sha256_partitions.sh .../file.bin"; | 
 | 1166 | } | 
 | 1167 |  | 
 | 1168 | string StringForHashBytes(const void* bytes, size_t size) { | 
 | 1169 |   string ret; | 
 | 1170 |   if (!OmahaHashCalculator::Base64Encode(bytes, size, &ret)) { | 
 | 1171 |     ret = "<unknown>"; | 
 | 1172 |   } | 
 | 1173 |   return ret; | 
 | 1174 | } | 
 | 1175 | }  // namespace | 
 | 1176 |  | 
| Darin Petkov | 698d041 | 2010-10-13 10:59:44 -0700 | [diff] [blame] | 1177 | bool DeltaPerformer::VerifySourcePartitions() { | 
 | 1178 |   LOG(INFO) << "Verifying source partitions."; | 
 | 1179 |   CHECK(manifest_valid_); | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1180 |   CHECK(install_plan_); | 
| Darin Petkov | 698d041 | 2010-10-13 10:59:44 -0700 | [diff] [blame] | 1181 |   if (manifest_.has_old_kernel_info()) { | 
 | 1182 |     const PartitionInfo& info = manifest_.old_kernel_info(); | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1183 |     bool valid = | 
 | 1184 |         !install_plan_->kernel_hash.empty() && | 
 | 1185 |         install_plan_->kernel_hash.size() == info.hash().size() && | 
 | 1186 |         memcmp(install_plan_->kernel_hash.data(), | 
| Andrew de los Reyes | 100bb7d | 2011-08-09 17:35:07 -0700 | [diff] [blame] | 1187 |                info.hash().data(), | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1188 |                install_plan_->kernel_hash.size()) == 0; | 
| Andrew de los Reyes | 100bb7d | 2011-08-09 17:35:07 -0700 | [diff] [blame] | 1189 |     if (!valid) { | 
 | 1190 |       LogVerifyError(true, | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1191 |                      StringForHashBytes(install_plan_->kernel_hash.data(), | 
 | 1192 |                                         install_plan_->kernel_hash.size()), | 
| Andrew de los Reyes | 100bb7d | 2011-08-09 17:35:07 -0700 | [diff] [blame] | 1193 |                      StringForHashBytes(info.hash().data(), | 
 | 1194 |                                         info.hash().size())); | 
 | 1195 |     } | 
 | 1196 |     TEST_AND_RETURN_FALSE(valid); | 
| Darin Petkov | 698d041 | 2010-10-13 10:59:44 -0700 | [diff] [blame] | 1197 |   } | 
 | 1198 |   if (manifest_.has_old_rootfs_info()) { | 
 | 1199 |     const PartitionInfo& info = manifest_.old_rootfs_info(); | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1200 |     bool valid = | 
 | 1201 |         !install_plan_->rootfs_hash.empty() && | 
 | 1202 |         install_plan_->rootfs_hash.size() == info.hash().size() && | 
 | 1203 |         memcmp(install_plan_->rootfs_hash.data(), | 
| Andrew de los Reyes | 100bb7d | 2011-08-09 17:35:07 -0700 | [diff] [blame] | 1204 |                info.hash().data(), | 
| Jay Srinivasan | 51dcf26 | 2012-09-13 17:24:32 -0700 | [diff] [blame] | 1205 |                install_plan_->rootfs_hash.size()) == 0; | 
| Andrew de los Reyes | 100bb7d | 2011-08-09 17:35:07 -0700 | [diff] [blame] | 1206 |     if (!valid) { | 
 | 1207 |       LogVerifyError(false, | 
| Chris Sosa | 670d680 | 2013-03-29 14:17:45 -0700 | [diff] [blame] | 1208 |                      StringForHashBytes(install_plan_->rootfs_hash.data(), | 
 | 1209 |                                         install_plan_->rootfs_hash.size()), | 
| Andrew de los Reyes | 100bb7d | 2011-08-09 17:35:07 -0700 | [diff] [blame] | 1210 |                      StringForHashBytes(info.hash().data(), | 
 | 1211 |                                         info.hash().size())); | 
 | 1212 |     } | 
 | 1213 |     TEST_AND_RETURN_FALSE(valid); | 
| Darin Petkov | 698d041 | 2010-10-13 10:59:44 -0700 | [diff] [blame] | 1214 |   } | 
 | 1215 |   return true; | 
 | 1216 | } | 
 | 1217 |  | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 1218 | void DeltaPerformer::DiscardBuffer(bool do_advance_offset) { | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 1219 |   // Update the buffer offset. | 
| Gilad Arnold | daa2740 | 2014-01-23 11:56:17 -0800 | [diff] [blame] | 1220 |   if (do_advance_offset) | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 1221 |     buffer_offset_ += buffer_.size(); | 
 | 1222 |  | 
 | 1223 |   // Hash the content. | 
 | 1224 |   hash_calculator_.Update(&buffer_[0], buffer_.size()); | 
 | 1225 |  | 
 | 1226 |   // Swap content with an empty vector to ensure that all memory is released. | 
 | 1227 |   vector<char>().swap(buffer_); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 1228 | } | 
 | 1229 |  | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1230 | bool DeltaPerformer::CanResumeUpdate(PrefsInterface* prefs, | 
 | 1231 |                                      string update_check_response_hash) { | 
 | 1232 |   int64_t next_operation = kUpdateStateOperationInvalid; | 
 | 1233 |   TEST_AND_RETURN_FALSE(prefs->GetInt64(kPrefsUpdateStateNextOperation, | 
 | 1234 |                                         &next_operation) && | 
 | 1235 |                         next_operation != kUpdateStateOperationInvalid && | 
 | 1236 |                         next_operation > 0); | 
 | 1237 |  | 
 | 1238 |   string interrupted_hash; | 
 | 1239 |   TEST_AND_RETURN_FALSE(prefs->GetString(kPrefsUpdateCheckResponseHash, | 
 | 1240 |                                          &interrupted_hash) && | 
| David Zeuthen | c41c228 | 2013-06-17 16:33:06 -0700 | [diff] [blame] | 1241 |                         !interrupted_hash.empty() && | 
 | 1242 |                         interrupted_hash == update_check_response_hash); | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1243 |  | 
| Darin Petkov | 6142614 | 2010-10-08 11:04:55 -0700 | [diff] [blame] | 1244 |   int64_t resumed_update_failures; | 
 | 1245 |   TEST_AND_RETURN_FALSE(!prefs->GetInt64(kPrefsResumedUpdateFailures, | 
 | 1246 |                                          &resumed_update_failures) || | 
 | 1247 |                         resumed_update_failures <= kMaxResumedUpdateFailures); | 
 | 1248 |  | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1249 |   // Sanity check the rest. | 
 | 1250 |   int64_t next_data_offset = -1; | 
 | 1251 |   TEST_AND_RETURN_FALSE(prefs->GetInt64(kPrefsUpdateStateNextDataOffset, | 
 | 1252 |                                         &next_data_offset) && | 
 | 1253 |                         next_data_offset >= 0); | 
 | 1254 |  | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 1255 |   string sha256_context; | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1256 |   TEST_AND_RETURN_FALSE( | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 1257 |       prefs->GetString(kPrefsUpdateStateSHA256Context, &sha256_context) && | 
 | 1258 |       !sha256_context.empty()); | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1259 |  | 
 | 1260 |   int64_t manifest_metadata_size = 0; | 
 | 1261 |   TEST_AND_RETURN_FALSE(prefs->GetInt64(kPrefsManifestMetadataSize, | 
 | 1262 |                                         &manifest_metadata_size) && | 
 | 1263 |                         manifest_metadata_size > 0); | 
 | 1264 |  | 
 | 1265 |   return true; | 
 | 1266 | } | 
 | 1267 |  | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1268 | bool DeltaPerformer::ResetUpdateProgress(PrefsInterface* prefs, bool quick) { | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1269 |   TEST_AND_RETURN_FALSE(prefs->SetInt64(kPrefsUpdateStateNextOperation, | 
 | 1270 |                                         kUpdateStateOperationInvalid)); | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1271 |   if (!quick) { | 
 | 1272 |     prefs->SetString(kPrefsUpdateCheckResponseHash, ""); | 
 | 1273 |     prefs->SetInt64(kPrefsUpdateStateNextDataOffset, -1); | 
| David Zeuthen | 41996ad | 2013-09-24 15:43:24 -0700 | [diff] [blame] | 1274 |     prefs->SetInt64(kPrefsUpdateStateNextDataLength, 0); | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1275 |     prefs->SetString(kPrefsUpdateStateSHA256Context, ""); | 
 | 1276 |     prefs->SetString(kPrefsUpdateStateSignedSHA256Context, ""); | 
| Darin Petkov | 4f0a07b | 2011-05-25 16:47:20 -0700 | [diff] [blame] | 1277 |     prefs->SetString(kPrefsUpdateStateSignatureBlob, ""); | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1278 |     prefs->SetInt64(kPrefsManifestMetadataSize, -1); | 
| Darin Petkov | 6142614 | 2010-10-08 11:04:55 -0700 | [diff] [blame] | 1279 |     prefs->SetInt64(kPrefsResumedUpdateFailures, 0); | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1280 |   } | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 1281 |   return true; | 
 | 1282 | } | 
 | 1283 |  | 
 | 1284 | bool DeltaPerformer::CheckpointUpdateProgress() { | 
| Darin Petkov | 9c0baf8 | 2010-10-07 13:44:48 -0700 | [diff] [blame] | 1285 |   Terminator::set_exit_blocked(true); | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1286 |   if (last_updated_buffer_offset_ != buffer_offset_) { | 
| Darin Petkov | 9c0baf8 | 2010-10-07 13:44:48 -0700 | [diff] [blame] | 1287 |     // Resets the progress in case we die in the middle of the state update. | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1288 |     ResetUpdateProgress(prefs_, true); | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1289 |     TEST_AND_RETURN_FALSE( | 
| Darin Petkov | 437adc4 | 2010-10-07 13:12:24 -0700 | [diff] [blame] | 1290 |         prefs_->SetString(kPrefsUpdateStateSHA256Context, | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1291 |                           hash_calculator_.GetContext())); | 
 | 1292 |     TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataOffset, | 
 | 1293 |                                            buffer_offset_)); | 
 | 1294 |     last_updated_buffer_offset_ = buffer_offset_; | 
| David Zeuthen | 41996ad | 2013-09-24 15:43:24 -0700 | [diff] [blame] | 1295 |  | 
 | 1296 |     if (next_operation_num_ < num_total_operations_) { | 
 | 1297 |       const bool is_kernel_partition = | 
 | 1298 |           next_operation_num_ >= num_rootfs_operations_; | 
 | 1299 |       const DeltaArchiveManifest_InstallOperation &op = | 
 | 1300 |           is_kernel_partition ? | 
 | 1301 |           manifest_.kernel_install_operations( | 
 | 1302 |               next_operation_num_ - num_rootfs_operations_) : | 
 | 1303 |           manifest_.install_operations(next_operation_num_); | 
 | 1304 |       TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataLength, | 
 | 1305 |                                              op.data_length())); | 
 | 1306 |     } else { | 
 | 1307 |       TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataLength, | 
 | 1308 |                                              0)); | 
 | 1309 |     } | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 1310 |   } | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 1311 |   TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextOperation, | 
 | 1312 |                                          next_operation_num_)); | 
 | 1313 |   return true; | 
 | 1314 | } | 
 | 1315 |  | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1316 | bool DeltaPerformer::PrimeUpdateState() { | 
 | 1317 |   CHECK(manifest_valid_); | 
 | 1318 |   block_size_ = manifest_.block_size(); | 
 | 1319 |  | 
 | 1320 |   int64_t next_operation = kUpdateStateOperationInvalid; | 
 | 1321 |   if (!prefs_->GetInt64(kPrefsUpdateStateNextOperation, &next_operation) || | 
 | 1322 |       next_operation == kUpdateStateOperationInvalid || | 
 | 1323 |       next_operation <= 0) { | 
 | 1324 |     // Initiating a new update, no more state needs to be initialized. | 
| Darin Petkov | 698d041 | 2010-10-13 10:59:44 -0700 | [diff] [blame] | 1325 |     TEST_AND_RETURN_FALSE(VerifySourcePartitions()); | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1326 |     return true; | 
 | 1327 |   } | 
 | 1328 |   next_operation_num_ = next_operation; | 
 | 1329 |  | 
 | 1330 |   // Resuming an update -- load the rest of the update state. | 
 | 1331 |   int64_t next_data_offset = -1; | 
 | 1332 |   TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, | 
 | 1333 |                                          &next_data_offset) && | 
 | 1334 |                         next_data_offset >= 0); | 
 | 1335 |   buffer_offset_ = next_data_offset; | 
 | 1336 |  | 
| Darin Petkov | 4f0a07b | 2011-05-25 16:47:20 -0700 | [diff] [blame] | 1337 |   // The signed hash context and the signature blob may be empty if the | 
 | 1338 |   // interrupted update didn't reach the signature. | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1339 |   prefs_->GetString(kPrefsUpdateStateSignedSHA256Context, | 
 | 1340 |                     &signed_hash_context_); | 
| Darin Petkov | 4f0a07b | 2011-05-25 16:47:20 -0700 | [diff] [blame] | 1341 |   string signature_blob; | 
 | 1342 |   if (prefs_->GetString(kPrefsUpdateStateSignatureBlob, &signature_blob)) { | 
 | 1343 |     signatures_message_data_.assign(signature_blob.begin(), | 
 | 1344 |                                     signature_blob.end()); | 
 | 1345 |   } | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1346 |  | 
 | 1347 |   string hash_context; | 
 | 1348 |   TEST_AND_RETURN_FALSE(prefs_->GetString(kPrefsUpdateStateSHA256Context, | 
 | 1349 |                                           &hash_context) && | 
 | 1350 |                         hash_calculator_.SetContext(hash_context)); | 
 | 1351 |  | 
 | 1352 |   int64_t manifest_metadata_size = 0; | 
 | 1353 |   TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsManifestMetadataSize, | 
 | 1354 |                                          &manifest_metadata_size) && | 
 | 1355 |                         manifest_metadata_size > 0); | 
| Gilad Arnold | fe13393 | 2014-01-14 12:25:50 -0800 | [diff] [blame] | 1356 |   metadata_size_ = manifest_metadata_size; | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1357 |  | 
| Gilad Arnold | 8a86fa5 | 2013-01-15 12:35:05 -0800 | [diff] [blame] | 1358 |   // Advance the download progress to reflect what doesn't need to be | 
 | 1359 |   // re-downloaded. | 
 | 1360 |   total_bytes_received_ += buffer_offset_; | 
 | 1361 |  | 
| Darin Petkov | 6142614 | 2010-10-08 11:04:55 -0700 | [diff] [blame] | 1362 |   // Speculatively count the resume as a failure. | 
 | 1363 |   int64_t resumed_update_failures; | 
 | 1364 |   if (prefs_->GetInt64(kPrefsResumedUpdateFailures, &resumed_update_failures)) { | 
 | 1365 |     resumed_update_failures++; | 
 | 1366 |   } else { | 
 | 1367 |     resumed_update_failures = 1; | 
 | 1368 |   } | 
 | 1369 |   prefs_->SetInt64(kPrefsResumedUpdateFailures, resumed_update_failures); | 
| Darin Petkov | 9b23057 | 2010-10-08 10:20:09 -0700 | [diff] [blame] | 1370 |   return true; | 
 | 1371 | } | 
 | 1372 |  | 
| David Zeuthen | a99981f | 2013-04-29 13:42:47 -0700 | [diff] [blame] | 1373 | void DeltaPerformer::SendUmaStat(ErrorCode code) { | 
| Jay Srinivasan | 55f50c2 | 2013-01-10 19:24:35 -0800 | [diff] [blame] | 1374 |   utils::SendErrorCodeToUma(system_state_, code); | 
| Jay Srinivasan | f057205 | 2012-10-23 18:12:56 -0700 | [diff] [blame] | 1375 | } | 
 | 1376 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 1377 | }  // namespace chromeos_update_engine |