| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 1 | // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 
|  | 2 | // Use of this source code is governed by a BSD-style license that can be | 
|  | 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> | 
|  | 12 | #include <string> | 
|  | 13 | #include <vector> | 
|  | 14 |  | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 15 | #include <base/scoped_ptr.h> | 
|  | 16 | #include <base/string_util.h> | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 17 | #include <google/protobuf/repeated_field.h> | 
|  | 18 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 19 | #include "update_engine/bzip_extent_writer.h" | 
|  | 20 | #include "update_engine/delta_diff_generator.h" | 
|  | 21 | #include "update_engine/extent_writer.h" | 
|  | 22 | #include "update_engine/graph_types.h" | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 23 | #include "update_engine/payload_signer.h" | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 24 | #include "update_engine/prefs_interface.h" | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 25 | #include "update_engine/subprocess.h" | 
|  | 26 |  | 
|  | 27 | using std::min; | 
|  | 28 | using std::string; | 
|  | 29 | using std::vector; | 
|  | 30 | using google::protobuf::RepeatedPtrField; | 
|  | 31 |  | 
|  | 32 | namespace chromeos_update_engine { | 
|  | 33 |  | 
|  | 34 | namespace { | 
|  | 35 |  | 
|  | 36 | const int kDeltaVersionLength = 8; | 
|  | 37 | const int kDeltaProtobufLengthLength = 8; | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 38 | const char kUpdatePayloadPublicKeyPath[] = | 
|  | 39 | "/usr/share/update_engine/update-payload-key.pub.pem"; | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 40 | const int kUpdateStateOperationInvalid = -1; | 
|  | 41 |  | 
|  | 42 | // Returns true if |op| is idempotent -- i.e., if we can interrupt it and repeat | 
|  | 43 | // it safely. Returns false otherwise. | 
|  | 44 | bool IsIdempotentOperation(const DeltaArchiveManifest_InstallOperation& op) { | 
|  | 45 | if (op.src_extents_size() == 0) { | 
|  | 46 | return true; | 
|  | 47 | } | 
|  | 48 | // TODO(petkov): Cover the case where the source and target extents don't | 
|  | 49 | // intersect. | 
|  | 50 | return false; | 
|  | 51 | } | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 52 |  | 
|  | 53 | // Converts extents to a human-readable string, for use by DumpUpdateProto(). | 
|  | 54 | string ExtentsToString(const RepeatedPtrField<Extent>& extents) { | 
|  | 55 | string ret; | 
|  | 56 | for (int i = 0; i < extents.size(); i++) { | 
|  | 57 | const Extent& extent = extents.Get(i); | 
|  | 58 | if (extent.start_block() == kSparseHole) { | 
|  | 59 | ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks()); | 
|  | 60 | } else { | 
|  | 61 | ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ", | 
|  | 62 | extent.start_block(), extent.num_blocks()); | 
|  | 63 | } | 
|  | 64 | } | 
|  | 65 | if (!ret.empty()) { | 
|  | 66 | DCHECK_GT(ret.size(), static_cast<size_t>(1)); | 
|  | 67 | ret.resize(ret.size() - 2); | 
|  | 68 | } | 
|  | 69 | return ret; | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | // LOGs a DeltaArchiveManifest object. Useful for debugging. | 
|  | 73 | void DumpUpdateProto(const DeltaArchiveManifest& manifest) { | 
|  | 74 | LOG(INFO) << "Update Proto:"; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 75 | LOG(INFO) << "  block_size: " << manifest.block_size(); | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 76 | for (int i = 0; i < (manifest.install_operations_size() + | 
|  | 77 | manifest.kernel_install_operations_size()); i++) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 78 | const DeltaArchiveManifest_InstallOperation& op = | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 79 | i < manifest.install_operations_size() ? | 
|  | 80 | manifest.install_operations(i) : | 
|  | 81 | manifest.kernel_install_operations( | 
|  | 82 | i - manifest.install_operations_size()); | 
|  | 83 | if (i == 0) | 
|  | 84 | LOG(INFO) << "  Rootfs ops:"; | 
|  | 85 | else if (i == manifest.install_operations_size()) | 
|  | 86 | LOG(INFO) << "   Kernel ops:"; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 87 | LOG(INFO) << "  operation(" << i << ")"; | 
|  | 88 | LOG(INFO) << "    type: " | 
|  | 89 | << DeltaArchiveManifest_InstallOperation_Type_Name(op.type()); | 
|  | 90 | if (op.has_data_offset()) | 
|  | 91 | LOG(INFO) << "    data_offset: " << op.data_offset(); | 
|  | 92 | if (op.has_data_length()) | 
|  | 93 | LOG(INFO) << "    data_length: " << op.data_length(); | 
|  | 94 | LOG(INFO) << "    src_extents: " << ExtentsToString(op.src_extents()); | 
|  | 95 | if (op.has_src_length()) | 
|  | 96 | LOG(INFO) << "    src_length: " << op.src_length(); | 
|  | 97 | LOG(INFO) << "    dst_extents: " << ExtentsToString(op.dst_extents()); | 
|  | 98 | if (op.has_dst_length()) | 
|  | 99 | LOG(INFO) << "    dst_length: " << op.dst_length(); | 
|  | 100 | } | 
|  | 101 | } | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 102 |  | 
|  | 103 | // Opens path for read/write, put the fd into *fd. On success returns true | 
|  | 104 | // and sets *err to 0. On failure, returns false and sets *err to errno. | 
|  | 105 | bool OpenFile(const char* path, int* fd, int* err) { | 
|  | 106 | if (*fd != -1) { | 
|  | 107 | LOG(ERROR) << "Can't open(" << path << "), *fd != -1 (it's " << *fd << ")"; | 
|  | 108 | *err = EINVAL; | 
|  | 109 | return false; | 
|  | 110 | } | 
|  | 111 | *fd = open(path, O_RDWR, 000); | 
|  | 112 | if (*fd < 0) { | 
|  | 113 | *err = errno; | 
|  | 114 | PLOG(ERROR) << "Unable to open file " << path; | 
|  | 115 | return false; | 
|  | 116 | } | 
|  | 117 | *err = 0; | 
|  | 118 | return true; | 
|  | 119 | } | 
|  | 120 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 121 | }  // namespace {} | 
|  | 122 |  | 
|  | 123 | 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] | 124 | int err; | 
|  | 125 | if (OpenFile(path, &fd_, &err)) | 
|  | 126 | path_ = path; | 
|  | 127 | return -err; | 
|  | 128 | } | 
|  | 129 |  | 
|  | 130 | bool DeltaPerformer::OpenKernel(const char* kernel_path) { | 
|  | 131 | int err; | 
|  | 132 | bool success = OpenFile(kernel_path, &kernel_fd_, &err); | 
|  | 133 | if (success) | 
|  | 134 | kernel_path_ = kernel_path; | 
|  | 135 | return success; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 136 | } | 
|  | 137 |  | 
|  | 138 | int DeltaPerformer::Close() { | 
|  | 139 | if (!buffer_.empty()) { | 
|  | 140 | LOG(ERROR) << "Called Close() while buffer not empty!"; | 
|  | 141 | return -1; | 
|  | 142 | } | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 143 | int err = 0; | 
|  | 144 | if (close(kernel_fd_) == -1) { | 
|  | 145 | err = errno; | 
|  | 146 | PLOG(ERROR) << "Unable to close kernel fd:"; | 
|  | 147 | } | 
|  | 148 | if (close(fd_) == -1) { | 
|  | 149 | err = errno; | 
|  | 150 | PLOG(ERROR) << "Unable to close rootfs fd:"; | 
|  | 151 | } | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 152 | LOG_IF(ERROR, !hash_calculator_.Finalize()) << "Unable to finalize the hash."; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 153 | fd_ = -2;  // Set so that isn't not valid AND calls to Open() will fail. | 
|  | 154 | path_ = ""; | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 155 | return -err; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 156 | } | 
|  | 157 |  | 
|  | 158 | // Wrapper around write. Returns bytes written on success or | 
|  | 159 | // -errno on error. | 
|  | 160 | // This function performs as many actions as it can, given the amount of | 
|  | 161 | // data received thus far. | 
| Andrew de los Reyes | 0cca421 | 2010-04-29 14:00:58 -0700 | [diff] [blame] | 162 | ssize_t DeltaPerformer::Write(const void* bytes, size_t count) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 163 | const char* c_bytes = reinterpret_cast<const char*>(bytes); | 
|  | 164 | buffer_.insert(buffer_.end(), c_bytes, c_bytes + count); | 
|  | 165 |  | 
|  | 166 | if (!manifest_valid_) { | 
|  | 167 | // See if we have enough bytes for the manifest yet | 
|  | 168 | if (buffer_.size() < strlen(kDeltaMagic) + | 
|  | 169 | kDeltaVersionLength + kDeltaProtobufLengthLength) { | 
|  | 170 | // Don't have enough bytes to even know the protobuf length | 
|  | 171 | return count; | 
|  | 172 | } | 
|  | 173 | uint64_t protobuf_length; | 
|  | 174 | COMPILE_ASSERT(sizeof(protobuf_length) == kDeltaProtobufLengthLength, | 
|  | 175 | protobuf_length_size_mismatch); | 
|  | 176 | memcpy(&protobuf_length, | 
|  | 177 | &buffer_[strlen(kDeltaMagic) + kDeltaVersionLength], | 
|  | 178 | kDeltaProtobufLengthLength); | 
|  | 179 | protobuf_length = be64toh(protobuf_length);  // switch big endian to host | 
|  | 180 | if (buffer_.size() < strlen(kDeltaMagic) + kDeltaVersionLength + | 
|  | 181 | kDeltaProtobufLengthLength + protobuf_length) { | 
|  | 182 | return count; | 
|  | 183 | } | 
|  | 184 | // We have the full proto buffer in buffer_. Parse it. | 
|  | 185 | const int offset = strlen(kDeltaMagic) + kDeltaVersionLength + | 
|  | 186 | kDeltaProtobufLengthLength; | 
|  | 187 | if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) { | 
|  | 188 | LOG(ERROR) << "Unable to parse manifest in update file."; | 
|  | 189 | return -EINVAL; | 
|  | 190 | } | 
|  | 191 | // Remove protobuf and header info from buffer_, so buffer_ contains | 
|  | 192 | // just data blobs | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 193 | size_t metadata_size = strlen(kDeltaMagic) + kDeltaVersionLength + | 
|  | 194 | kDeltaProtobufLengthLength + protobuf_length; | 
|  | 195 | DiscardBufferHeadBytes(metadata_size, | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 196 | true);  // do_hash | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 197 | LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestMetadataSize, | 
|  | 198 | metadata_size)) | 
|  | 199 | << "Unable to save the manifest metadata size."; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 200 | manifest_valid_ = true; | 
|  | 201 | block_size_ = manifest_.block_size(); | 
|  | 202 | } | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 203 | ssize_t total_operations = manifest_.install_operations_size() + | 
|  | 204 | manifest_.kernel_install_operations_size(); | 
|  | 205 | while (next_operation_num_ < total_operations) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 206 | const DeltaArchiveManifest_InstallOperation &op = | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 207 | next_operation_num_ < manifest_.install_operations_size() ? | 
|  | 208 | manifest_.install_operations(next_operation_num_) : | 
|  | 209 | manifest_.kernel_install_operations( | 
|  | 210 | next_operation_num_ - manifest_.install_operations_size()); | 
|  | 211 | if (!CanPerformInstallOperation(op)) | 
|  | 212 | break; | 
| Andrew de los Reyes | bef0c7d | 2010-08-20 10:20:10 -0700 | [diff] [blame] | 213 | // Log every thousandth operation, and also the first and last ones | 
|  | 214 | if ((next_operation_num_ % 1000 == 0) || | 
|  | 215 | (next_operation_num_ + 1 == total_operations)) { | 
|  | 216 | LOG(INFO) << "Performing operation " << (next_operation_num_ + 1) << "/" | 
|  | 217 | << total_operations; | 
|  | 218 | } | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 219 | bool is_kernel_partition = | 
|  | 220 | (next_operation_num_ >= manifest_.install_operations_size()); | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 221 | // If about to start a non-idempotent operation, clear the update state so | 
|  | 222 | // that if the operation gets interrupted, we don't try to resume the | 
|  | 223 | // update. | 
|  | 224 | if (!IsIdempotentOperation(op)) { | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 225 | ResetUpdateProgress(prefs_); | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 226 | } | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 227 | if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE || | 
|  | 228 | op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 229 | if (!PerformReplaceOperation(op, is_kernel_partition)) { | 
|  | 230 | LOG(ERROR) << "Failed to perform replace operation " | 
|  | 231 | << next_operation_num_; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 232 | return -EINVAL; | 
|  | 233 | } | 
|  | 234 | } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) { | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 235 | if (!PerformMoveOperation(op, is_kernel_partition)) { | 
|  | 236 | LOG(ERROR) << "Failed to perform move operation " | 
|  | 237 | << next_operation_num_; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 238 | return -EINVAL; | 
|  | 239 | } | 
|  | 240 | } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) { | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 241 | if (!PerformBsdiffOperation(op, is_kernel_partition)) { | 
|  | 242 | LOG(ERROR) << "Failed to perform bsdiff operation " | 
|  | 243 | << next_operation_num_; | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 244 | return -EINVAL; | 
|  | 245 | } | 
|  | 246 | } | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 247 | next_operation_num_++; | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 248 | CheckpointUpdateProgress(); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 249 | } | 
|  | 250 | return count; | 
|  | 251 | } | 
|  | 252 |  | 
|  | 253 | bool DeltaPerformer::CanPerformInstallOperation( | 
|  | 254 | const chromeos_update_engine::DeltaArchiveManifest_InstallOperation& | 
|  | 255 | operation) { | 
|  | 256 | // Move operations don't require any data blob, so they can always | 
|  | 257 | // be performed | 
|  | 258 | if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) | 
|  | 259 | return true; | 
|  | 260 |  | 
|  | 261 | // See if we have the entire data blob in the buffer | 
|  | 262 | if (operation.data_offset() < buffer_offset_) { | 
|  | 263 | LOG(ERROR) << "we threw away data it seems?"; | 
|  | 264 | return false; | 
|  | 265 | } | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 266 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 267 | return (operation.data_offset() + operation.data_length()) <= | 
|  | 268 | (buffer_offset_ + buffer_.size()); | 
|  | 269 | } | 
|  | 270 |  | 
|  | 271 | bool DeltaPerformer::PerformReplaceOperation( | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 272 | const DeltaArchiveManifest_InstallOperation& operation, | 
|  | 273 | bool is_kernel_partition) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 274 | CHECK(operation.type() == \ | 
|  | 275 | DeltaArchiveManifest_InstallOperation_Type_REPLACE || \ | 
|  | 276 | operation.type() == \ | 
|  | 277 | DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); | 
|  | 278 |  | 
|  | 279 | // Since we delete data off the beginning of the buffer as we use it, | 
|  | 280 | // the data we need should be exactly at the beginning of the buffer. | 
|  | 281 | CHECK_EQ(buffer_offset_, operation.data_offset()); | 
|  | 282 | CHECK_GE(buffer_.size(), operation.data_length()); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 283 |  | 
|  | 284 | // Don't include the signature data blob in the hash. | 
|  | 285 | bool do_hash = !ExtractSignatureMessage(operation); | 
|  | 286 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 287 | DirectExtentWriter direct_writer; | 
|  | 288 | ZeroPadExtentWriter zero_pad_writer(&direct_writer); | 
|  | 289 | scoped_ptr<BzipExtentWriter> bzip_writer; | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 290 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 291 | // Since bzip decompression is optional, we have a variable writer that will | 
|  | 292 | // point to one of the ExtentWriter objects above. | 
|  | 293 | ExtentWriter* writer = NULL; | 
|  | 294 | if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) { | 
|  | 295 | writer = &zero_pad_writer; | 
|  | 296 | } else if (operation.type() == | 
|  | 297 | DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { | 
|  | 298 | bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer)); | 
|  | 299 | writer = bzip_writer.get(); | 
|  | 300 | } else { | 
|  | 301 | NOTREACHED(); | 
|  | 302 | } | 
|  | 303 |  | 
|  | 304 | // Create a vector of extents to pass to the ExtentWriter. | 
|  | 305 | vector<Extent> extents; | 
|  | 306 | for (int i = 0; i < operation.dst_extents_size(); i++) { | 
|  | 307 | extents.push_back(operation.dst_extents(i)); | 
|  | 308 | } | 
|  | 309 |  | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 310 | int fd = is_kernel_partition ? kernel_fd_ : fd_; | 
|  | 311 |  | 
|  | 312 | TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_)); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 313 | TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length())); | 
|  | 314 | TEST_AND_RETURN_FALSE(writer->End()); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 315 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 316 | // Update buffer | 
|  | 317 | buffer_offset_ += operation.data_length(); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 318 | DiscardBufferHeadBytes(operation.data_length(), do_hash); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 319 | return true; | 
|  | 320 | } | 
|  | 321 |  | 
|  | 322 | bool DeltaPerformer::PerformMoveOperation( | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 323 | const DeltaArchiveManifest_InstallOperation& operation, | 
|  | 324 | bool is_kernel_partition) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 325 | // Calculate buffer size. Note, this function doesn't do a sliding | 
|  | 326 | // window to copy in case the source and destination blocks overlap. | 
|  | 327 | // If we wanted to do a sliding window, we could program the server | 
|  | 328 | // to generate deltas that effectively did a sliding window. | 
|  | 329 |  | 
|  | 330 | uint64_t blocks_to_read = 0; | 
|  | 331 | for (int i = 0; i < operation.src_extents_size(); i++) | 
|  | 332 | blocks_to_read += operation.src_extents(i).num_blocks(); | 
|  | 333 |  | 
|  | 334 | uint64_t blocks_to_write = 0; | 
|  | 335 | for (int i = 0; i < operation.dst_extents_size(); i++) | 
|  | 336 | blocks_to_write += operation.dst_extents(i).num_blocks(); | 
|  | 337 |  | 
|  | 338 | DCHECK_EQ(blocks_to_write, blocks_to_read); | 
|  | 339 | vector<char> buf(blocks_to_write * block_size_); | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 340 |  | 
|  | 341 | int fd = is_kernel_partition ? kernel_fd_ : fd_; | 
|  | 342 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 343 | // Read in bytes. | 
|  | 344 | ssize_t bytes_read = 0; | 
|  | 345 | for (int i = 0; i < operation.src_extents_size(); i++) { | 
|  | 346 | ssize_t bytes_read_this_iteration = 0; | 
|  | 347 | const Extent& extent = operation.src_extents(i); | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 348 | TEST_AND_RETURN_FALSE(utils::PReadAll(fd, | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 349 | &buf[bytes_read], | 
|  | 350 | extent.num_blocks() * block_size_, | 
|  | 351 | extent.start_block() * block_size_, | 
|  | 352 | &bytes_read_this_iteration)); | 
|  | 353 | TEST_AND_RETURN_FALSE( | 
|  | 354 | bytes_read_this_iteration == | 
|  | 355 | static_cast<ssize_t>(extent.num_blocks() * block_size_)); | 
|  | 356 | bytes_read += bytes_read_this_iteration; | 
|  | 357 | } | 
|  | 358 |  | 
|  | 359 | // Write bytes out. | 
|  | 360 | ssize_t bytes_written = 0; | 
|  | 361 | for (int i = 0; i < operation.dst_extents_size(); i++) { | 
|  | 362 | const Extent& extent = operation.dst_extents(i); | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 363 | TEST_AND_RETURN_FALSE(utils::PWriteAll(fd, | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 364 | &buf[bytes_written], | 
|  | 365 | extent.num_blocks() * block_size_, | 
|  | 366 | extent.start_block() * block_size_)); | 
|  | 367 | bytes_written += extent.num_blocks() * block_size_; | 
|  | 368 | } | 
|  | 369 | DCHECK_EQ(bytes_written, bytes_read); | 
|  | 370 | DCHECK_EQ(bytes_written, static_cast<ssize_t>(buf.size())); | 
|  | 371 | return true; | 
|  | 372 | } | 
|  | 373 |  | 
|  | 374 | bool DeltaPerformer::ExtentsToBsdiffPositionsString( | 
|  | 375 | const RepeatedPtrField<Extent>& extents, | 
|  | 376 | uint64_t block_size, | 
|  | 377 | uint64_t full_length, | 
|  | 378 | string* positions_string) { | 
|  | 379 | string ret; | 
|  | 380 | uint64_t length = 0; | 
|  | 381 | for (int i = 0; i < extents.size(); i++) { | 
|  | 382 | Extent extent = extents.Get(i); | 
|  | 383 | int64_t start = extent.start_block(); | 
|  | 384 | uint64_t this_length = min(full_length - length, | 
|  | 385 | extent.num_blocks() * block_size); | 
|  | 386 | if (start == static_cast<int64_t>(kSparseHole)) | 
|  | 387 | start = -1; | 
|  | 388 | else | 
|  | 389 | start *= block_size; | 
|  | 390 | ret += StringPrintf("%" PRIi64 ":%" PRIu64 ",", start, this_length); | 
|  | 391 | length += this_length; | 
|  | 392 | } | 
|  | 393 | TEST_AND_RETURN_FALSE(length == full_length); | 
|  | 394 | if (!ret.empty()) | 
|  | 395 | ret.resize(ret.size() - 1);  // Strip trailing comma off | 
|  | 396 | *positions_string = ret; | 
|  | 397 | return true; | 
|  | 398 | } | 
|  | 399 |  | 
|  | 400 | bool DeltaPerformer::PerformBsdiffOperation( | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 401 | const DeltaArchiveManifest_InstallOperation& operation, | 
|  | 402 | bool is_kernel_partition) { | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 403 | // Since we delete data off the beginning of the buffer as we use it, | 
|  | 404 | // the data we need should be exactly at the beginning of the buffer. | 
|  | 405 | CHECK_EQ(buffer_offset_, operation.data_offset()); | 
|  | 406 | CHECK_GE(buffer_.size(), operation.data_length()); | 
|  | 407 |  | 
|  | 408 | string input_positions; | 
|  | 409 | TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(), | 
|  | 410 | block_size_, | 
|  | 411 | operation.src_length(), | 
|  | 412 | &input_positions)); | 
|  | 413 | string output_positions; | 
|  | 414 | TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.dst_extents(), | 
|  | 415 | block_size_, | 
|  | 416 | operation.dst_length(), | 
|  | 417 | &output_positions)); | 
|  | 418 |  | 
|  | 419 | string temp_filename; | 
|  | 420 | TEST_AND_RETURN_FALSE(utils::MakeTempFile("/tmp/au_patch.XXXXXX", | 
|  | 421 | &temp_filename, | 
|  | 422 | NULL)); | 
|  | 423 | ScopedPathUnlinker path_unlinker(temp_filename); | 
|  | 424 | { | 
|  | 425 | int fd = open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); | 
|  | 426 | ScopedFdCloser fd_closer(&fd); | 
|  | 427 | TEST_AND_RETURN_FALSE( | 
|  | 428 | utils::WriteAll(fd, &buffer_[0], operation.data_length())); | 
|  | 429 | } | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 430 |  | 
|  | 431 | int fd = is_kernel_partition ? kernel_fd_ : fd_; | 
|  | 432 | const string& path = is_kernel_partition ? kernel_path_ : path_; | 
|  | 433 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 434 | vector<string> cmd; | 
|  | 435 | cmd.push_back(kBspatchPath); | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 436 | cmd.push_back(path); | 
|  | 437 | cmd.push_back(path); | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 438 | cmd.push_back(temp_filename); | 
|  | 439 | cmd.push_back(input_positions); | 
|  | 440 | cmd.push_back(output_positions); | 
|  | 441 | int return_code = 0; | 
|  | 442 | TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code)); | 
|  | 443 | TEST_AND_RETURN_FALSE(return_code == 0); | 
|  | 444 |  | 
|  | 445 | if (operation.dst_length() % block_size_) { | 
|  | 446 | // Zero out rest of final block. | 
|  | 447 | // TODO(adlr): build this into bspatch; it's more efficient that way. | 
|  | 448 | const Extent& last_extent = | 
|  | 449 | operation.dst_extents(operation.dst_extents_size() - 1); | 
|  | 450 | const uint64_t end_byte = | 
|  | 451 | (last_extent.start_block() + last_extent.num_blocks()) * block_size_; | 
|  | 452 | const uint64_t begin_byte = | 
|  | 453 | end_byte - (block_size_ - operation.dst_length() % block_size_); | 
|  | 454 | vector<char> zeros(end_byte - begin_byte); | 
|  | 455 | TEST_AND_RETURN_FALSE( | 
| Andrew de los Reyes | f4c7ef1 | 2010-04-30 10:37:00 -0700 | [diff] [blame] | 456 | 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] | 457 | } | 
|  | 458 |  | 
|  | 459 | // Update buffer. | 
|  | 460 | buffer_offset_ += operation.data_length(); | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 461 | DiscardBufferHeadBytes(operation.data_length(), | 
|  | 462 | true);  // do_hash | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 463 | return true; | 
|  | 464 | } | 
|  | 465 |  | 
| Darin Petkov | d7061ab | 2010-10-06 14:37:09 -0700 | [diff] [blame] | 466 | bool DeltaPerformer::ExtractSignatureMessage( | 
|  | 467 | const DeltaArchiveManifest_InstallOperation& operation) { | 
|  | 468 | if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_REPLACE || | 
|  | 469 | !manifest_.has_signatures_offset() || | 
|  | 470 | manifest_.signatures_offset() != operation.data_offset()) { | 
|  | 471 | return false; | 
|  | 472 | } | 
|  | 473 | TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() && | 
|  | 474 | manifest_.signatures_size() == operation.data_length()); | 
|  | 475 | TEST_AND_RETURN_FALSE(signatures_message_data_.empty()); | 
|  | 476 | TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset()); | 
|  | 477 | TEST_AND_RETURN_FALSE(buffer_.size() >= manifest_.signatures_size()); | 
|  | 478 | signatures_message_data_.insert( | 
|  | 479 | signatures_message_data_.begin(), | 
|  | 480 | buffer_.begin(), | 
|  | 481 | buffer_.begin() + manifest_.signatures_size()); | 
|  | 482 | LOG(INFO) << "Extracted signature data of size " | 
|  | 483 | << manifest_.signatures_size() << " at " | 
|  | 484 | << manifest_.signatures_offset(); | 
|  | 485 | return true; | 
|  | 486 | } | 
|  | 487 |  | 
|  | 488 | bool DeltaPerformer::VerifyPayload(const string& public_key_path) { | 
|  | 489 | string key_path = public_key_path; | 
|  | 490 | if (key_path.empty()) { | 
|  | 491 | key_path = kUpdatePayloadPublicKeyPath; | 
|  | 492 | } | 
|  | 493 | LOG(INFO) << "Verifying delta payload. Public key path: " << key_path; | 
|  | 494 | if (!utils::FileExists(key_path.c_str())) { | 
|  | 495 | LOG(WARNING) << "Not verifying delta payload due to missing public key."; | 
|  | 496 | return true; | 
|  | 497 | } | 
|  | 498 | TEST_AND_RETURN_FALSE(!signatures_message_data_.empty()); | 
|  | 499 | vector<char> signed_hash_data; | 
|  | 500 | TEST_AND_RETURN_FALSE(PayloadSigner::VerifySignature(signatures_message_data_, | 
|  | 501 | key_path, | 
|  | 502 | &signed_hash_data)); | 
|  | 503 | const vector<char>& hash_data = hash_calculator_.raw_hash(); | 
|  | 504 | TEST_AND_RETURN_FALSE(!hash_data.empty()); | 
|  | 505 | return hash_data == signed_hash_data; | 
|  | 506 | } | 
|  | 507 |  | 
|  | 508 | void DeltaPerformer::DiscardBufferHeadBytes(size_t count, bool do_hash) { | 
|  | 509 | if (do_hash) { | 
|  | 510 | hash_calculator_.Update(&buffer_[0], count); | 
|  | 511 | } | 
|  | 512 | buffer_.erase(buffer_.begin(), buffer_.begin() + count); | 
|  | 513 | } | 
|  | 514 |  | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 515 | bool DeltaPerformer::CanResumeUpdate(PrefsInterface* prefs, | 
|  | 516 | string update_check_response_hash) { | 
|  | 517 | int64_t next_operation = kUpdateStateOperationInvalid; | 
|  | 518 | TEST_AND_RETURN_FALSE(prefs->GetInt64(kPrefsUpdateStateNextOperation, | 
|  | 519 | &next_operation) && | 
|  | 520 | next_operation != kUpdateStateOperationInvalid && | 
|  | 521 | next_operation > 0); | 
|  | 522 |  | 
|  | 523 | string interrupted_hash; | 
|  | 524 | TEST_AND_RETURN_FALSE(prefs->GetString(kPrefsUpdateCheckResponseHash, | 
|  | 525 | &interrupted_hash) && | 
|  | 526 | !interrupted_hash.empty() && | 
|  | 527 | interrupted_hash == update_check_response_hash); | 
|  | 528 |  | 
|  | 529 | // Sanity check the rest. | 
|  | 530 | int64_t next_data_offset = -1; | 
|  | 531 | TEST_AND_RETURN_FALSE(prefs->GetInt64(kPrefsUpdateStateNextDataOffset, | 
|  | 532 | &next_data_offset) && | 
|  | 533 | next_data_offset >= 0); | 
|  | 534 |  | 
|  | 535 | string signed_sha256_context; | 
|  | 536 | TEST_AND_RETURN_FALSE( | 
|  | 537 | prefs->GetString(kPrefsUpdateStateSignedSHA256Context, | 
|  | 538 | &signed_sha256_context) && | 
|  | 539 | !signed_sha256_context.empty()); | 
|  | 540 |  | 
|  | 541 | int64_t manifest_metadata_size = 0; | 
|  | 542 | TEST_AND_RETURN_FALSE(prefs->GetInt64(kPrefsManifestMetadataSize, | 
|  | 543 | &manifest_metadata_size) && | 
|  | 544 | manifest_metadata_size > 0); | 
|  | 545 |  | 
|  | 546 | return true; | 
|  | 547 | } | 
|  | 548 |  | 
|  | 549 | bool DeltaPerformer::ResetUpdateProgress(PrefsInterface* prefs) { | 
|  | 550 | TEST_AND_RETURN_FALSE(prefs->SetInt64(kPrefsUpdateStateNextOperation, | 
|  | 551 | kUpdateStateOperationInvalid)); | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 552 | return true; | 
|  | 553 | } | 
|  | 554 |  | 
|  | 555 | bool DeltaPerformer::CheckpointUpdateProgress() { | 
|  | 556 | // First reset the progress in case we die in the middle of the state update. | 
| Darin Petkov | 0406e40 | 2010-10-06 21:33:11 -0700 | [diff] [blame] | 557 | ResetUpdateProgress(prefs_); | 
|  | 558 | if (last_updated_buffer_offset_ != buffer_offset_) { | 
|  | 559 | TEST_AND_RETURN_FALSE( | 
|  | 560 | prefs_->SetString(kPrefsUpdateStateSignedSHA256Context, | 
|  | 561 | hash_calculator_.GetContext())); | 
|  | 562 | TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataOffset, | 
|  | 563 | buffer_offset_)); | 
|  | 564 | last_updated_buffer_offset_ = buffer_offset_; | 
|  | 565 | } | 
| Darin Petkov | 73058b4 | 2010-10-06 16:32:19 -0700 | [diff] [blame] | 566 | TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextOperation, | 
|  | 567 | next_operation_num_)); | 
|  | 568 | return true; | 
|  | 569 | } | 
|  | 570 |  | 
| Andrew de los Reyes | 09e56d6 | 2010-04-23 13:45:53 -0700 | [diff] [blame] | 571 | }  // namespace chromeos_update_engine |