OLD | NEW |
(Empty) | |
| 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" |
| 6 #include <endian.h> |
| 7 #include <errno.h> |
| 8 #include <string.h> |
| 9 #include <algorithm> |
| 10 #include <string> |
| 11 #include <vector> |
| 12 |
| 13 #include "base/scoped_ptr.h" |
| 14 #include "base/string_util.h" |
| 15 |
| 16 #include <google/protobuf/repeated_field.h> |
| 17 |
| 18 #include "update_engine/bzip_extent_writer.h" |
| 19 #include "update_engine/delta_diff_generator.h" |
| 20 #include "update_engine/extent_writer.h" |
| 21 #include "update_engine/graph_types.h" |
| 22 #include "update_engine/subprocess.h" |
| 23 |
| 24 using std::min; |
| 25 using std::string; |
| 26 using std::vector; |
| 27 using google::protobuf::RepeatedPtrField; |
| 28 |
| 29 namespace chromeos_update_engine { |
| 30 |
| 31 namespace { |
| 32 |
| 33 const int kDeltaVersionLength = 8; |
| 34 const int kDeltaProtobufLengthLength = 8; |
| 35 |
| 36 // Remove count bytes from the beginning of *buffer. |
| 37 void RemoveBufferHeadBytes(vector<char>* buffer, size_t count) { |
| 38 buffer->erase(buffer->begin(), buffer->begin() + count); |
| 39 } |
| 40 |
| 41 // Converts extents to a human-readable string, for use by DumpUpdateProto(). |
| 42 string ExtentsToString(const RepeatedPtrField<Extent>& extents) { |
| 43 string ret; |
| 44 for (int i = 0; i < extents.size(); i++) { |
| 45 const Extent& extent = extents.Get(i); |
| 46 if (extent.start_block() == kSparseHole) { |
| 47 ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks()); |
| 48 } else { |
| 49 ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ", |
| 50 extent.start_block(), extent.num_blocks()); |
| 51 } |
| 52 } |
| 53 if (!ret.empty()) { |
| 54 DCHECK_GT(ret.size(), static_cast<size_t>(1)); |
| 55 ret.resize(ret.size() - 2); |
| 56 } |
| 57 return ret; |
| 58 } |
| 59 |
| 60 // LOGs a DeltaArchiveManifest object. Useful for debugging. |
| 61 void DumpUpdateProto(const DeltaArchiveManifest& manifest) { |
| 62 LOG(INFO) << "Update Proto:"; |
| 63 LOG(INFO) << " src_checksum: " << manifest.src_checksum(); |
| 64 LOG(INFO) << " dst_checksum: " << manifest.dst_checksum(); |
| 65 LOG(INFO) << " block_size: " << manifest.block_size(); |
| 66 for (int i = 0; i < manifest.install_operations_size(); i++) { |
| 67 const DeltaArchiveManifest_InstallOperation& op = |
| 68 manifest.install_operations(i); |
| 69 LOG(INFO) << " operation(" << i << ")"; |
| 70 LOG(INFO) << " type: " |
| 71 << DeltaArchiveManifest_InstallOperation_Type_Name(op.type()); |
| 72 if (op.has_data_offset()) |
| 73 LOG(INFO) << " data_offset: " << op.data_offset(); |
| 74 if (op.has_data_length()) |
| 75 LOG(INFO) << " data_length: " << op.data_length(); |
| 76 LOG(INFO) << " src_extents: " << ExtentsToString(op.src_extents()); |
| 77 if (op.has_src_length()) |
| 78 LOG(INFO) << " src_length: " << op.src_length(); |
| 79 LOG(INFO) << " dst_extents: " << ExtentsToString(op.dst_extents()); |
| 80 if (op.has_dst_length()) |
| 81 LOG(INFO) << " dst_length: " << op.dst_length(); |
| 82 } |
| 83 } |
| 84 } // namespace {} |
| 85 |
| 86 int DeltaPerformer::Open(const char* path, int flags, mode_t mode) { |
| 87 if (fd_ != -1) { |
| 88 LOG(ERROR) << "Can't Open(), fd_ != -1 (it's " << fd_ << ")"; |
| 89 return -EINVAL; |
| 90 } |
| 91 path_ = path; |
| 92 fd_ = open(path, O_RDWR, 000); |
| 93 if (fd_ < 0) |
| 94 return -errno; |
| 95 return 0; |
| 96 } |
| 97 |
| 98 int DeltaPerformer::Close() { |
| 99 if (!buffer_.empty()) { |
| 100 LOG(ERROR) << "Called Close() while buffer not empty!"; |
| 101 return -1; |
| 102 } |
| 103 if (close(fd_) == -1) |
| 104 return -errno; |
| 105 fd_ = -2; // Set so that isn't not valid AND calls to Open() will fail. |
| 106 path_ = ""; |
| 107 return 0; |
| 108 } |
| 109 |
| 110 // Wrapper around write. Returns bytes written on success or |
| 111 // -errno on error. |
| 112 // This function performs as many actions as it can, given the amount of |
| 113 // data received thus far. |
| 114 int DeltaPerformer::Write(const void* bytes, size_t count) { |
| 115 const char* c_bytes = reinterpret_cast<const char*>(bytes); |
| 116 buffer_.insert(buffer_.end(), c_bytes, c_bytes + count); |
| 117 |
| 118 if (!manifest_valid_) { |
| 119 // See if we have enough bytes for the manifest yet |
| 120 if (buffer_.size() < strlen(kDeltaMagic) + |
| 121 kDeltaVersionLength + kDeltaProtobufLengthLength) { |
| 122 // Don't have enough bytes to even know the protobuf length |
| 123 return count; |
| 124 } |
| 125 uint64_t protobuf_length; |
| 126 COMPILE_ASSERT(sizeof(protobuf_length) == kDeltaProtobufLengthLength, |
| 127 protobuf_length_size_mismatch); |
| 128 memcpy(&protobuf_length, |
| 129 &buffer_[strlen(kDeltaMagic) + kDeltaVersionLength], |
| 130 kDeltaProtobufLengthLength); |
| 131 protobuf_length = be64toh(protobuf_length); // switch big endian to host |
| 132 if (buffer_.size() < strlen(kDeltaMagic) + kDeltaVersionLength + |
| 133 kDeltaProtobufLengthLength + protobuf_length) { |
| 134 return count; |
| 135 } |
| 136 // We have the full proto buffer in buffer_. Parse it. |
| 137 const int offset = strlen(kDeltaMagic) + kDeltaVersionLength + |
| 138 kDeltaProtobufLengthLength; |
| 139 if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) { |
| 140 LOG(ERROR) << "Unable to parse manifest in update file."; |
| 141 return -EINVAL; |
| 142 } |
| 143 // Remove protobuf and header info from buffer_, so buffer_ contains |
| 144 // just data blobs |
| 145 RemoveBufferHeadBytes(&buffer_, |
| 146 strlen(kDeltaMagic) + |
| 147 kDeltaVersionLength + |
| 148 kDeltaProtobufLengthLength + protobuf_length); |
| 149 manifest_valid_ = true; |
| 150 block_size_ = manifest_.block_size(); |
| 151 } |
| 152 while (next_operation_ < manifest_.install_operations_size() && |
| 153 CanPerformInstallOperation( |
| 154 manifest_.install_operations(next_operation_))) { |
| 155 const DeltaArchiveManifest_InstallOperation &op = |
| 156 manifest_.install_operations(next_operation_); |
| 157 if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE || |
| 158 op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { |
| 159 if (!PerformReplaceOperation(op)) { |
| 160 LOG(ERROR) << "Failed to perform replace operation " << next_operation_; |
| 161 return -EINVAL; |
| 162 } |
| 163 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) { |
| 164 if (!PerformMoveOperation(op)) { |
| 165 LOG(ERROR) << "Failed to perform move operation " << next_operation_; |
| 166 return -EINVAL; |
| 167 } |
| 168 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) { |
| 169 if (!PerformBsdiffOperation(op)) { |
| 170 LOG(ERROR) << "Failed to perform bsdiff operation " << next_operation_; |
| 171 return -EINVAL; |
| 172 } |
| 173 } |
| 174 next_operation_++; |
| 175 } |
| 176 return count; |
| 177 } |
| 178 |
| 179 bool DeltaPerformer::CanPerformInstallOperation( |
| 180 const chromeos_update_engine::DeltaArchiveManifest_InstallOperation& |
| 181 operation) { |
| 182 // Move operations don't require any data blob, so they can always |
| 183 // be performed |
| 184 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) |
| 185 return true; |
| 186 |
| 187 // See if we have the entire data blob in the buffer |
| 188 if (operation.data_offset() < buffer_offset_) { |
| 189 LOG(ERROR) << "we threw away data it seems?"; |
| 190 return false; |
| 191 } |
| 192 |
| 193 return (operation.data_offset() + operation.data_length()) <= |
| 194 (buffer_offset_ + buffer_.size()); |
| 195 } |
| 196 |
| 197 bool DeltaPerformer::PerformReplaceOperation( |
| 198 const DeltaArchiveManifest_InstallOperation& operation) { |
| 199 CHECK(operation.type() == \ |
| 200 DeltaArchiveManifest_InstallOperation_Type_REPLACE || \ |
| 201 operation.type() == \ |
| 202 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); |
| 203 |
| 204 // Since we delete data off the beginning of the buffer as we use it, |
| 205 // the data we need should be exactly at the beginning of the buffer. |
| 206 CHECK_EQ(buffer_offset_, operation.data_offset()); |
| 207 CHECK_GE(buffer_.size(), operation.data_length()); |
| 208 |
| 209 DirectExtentWriter direct_writer; |
| 210 ZeroPadExtentWriter zero_pad_writer(&direct_writer); |
| 211 scoped_ptr<BzipExtentWriter> bzip_writer; |
| 212 |
| 213 // Since bzip decompression is optional, we have a variable writer that will |
| 214 // point to one of the ExtentWriter objects above. |
| 215 ExtentWriter* writer = NULL; |
| 216 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) { |
| 217 writer = &zero_pad_writer; |
| 218 } else if (operation.type() == |
| 219 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) { |
| 220 bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer)); |
| 221 writer = bzip_writer.get(); |
| 222 } else { |
| 223 NOTREACHED(); |
| 224 } |
| 225 |
| 226 // Create a vector of extents to pass to the ExtentWriter. |
| 227 vector<Extent> extents; |
| 228 for (int i = 0; i < operation.dst_extents_size(); i++) { |
| 229 extents.push_back(operation.dst_extents(i)); |
| 230 } |
| 231 |
| 232 TEST_AND_RETURN_FALSE(writer->Init(fd_, extents, block_size_)); |
| 233 TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length())); |
| 234 TEST_AND_RETURN_FALSE(writer->End()); |
| 235 |
| 236 // Update buffer |
| 237 buffer_offset_ += operation.data_length(); |
| 238 RemoveBufferHeadBytes(&buffer_, operation.data_length()); |
| 239 return true; |
| 240 } |
| 241 |
| 242 bool DeltaPerformer::PerformMoveOperation( |
| 243 const DeltaArchiveManifest_InstallOperation& operation) { |
| 244 // Calculate buffer size. Note, this function doesn't do a sliding |
| 245 // window to copy in case the source and destination blocks overlap. |
| 246 // If we wanted to do a sliding window, we could program the server |
| 247 // to generate deltas that effectively did a sliding window. |
| 248 |
| 249 uint64_t blocks_to_read = 0; |
| 250 for (int i = 0; i < operation.src_extents_size(); i++) |
| 251 blocks_to_read += operation.src_extents(i).num_blocks(); |
| 252 |
| 253 uint64_t blocks_to_write = 0; |
| 254 for (int i = 0; i < operation.dst_extents_size(); i++) |
| 255 blocks_to_write += operation.dst_extents(i).num_blocks(); |
| 256 |
| 257 DCHECK_EQ(blocks_to_write, blocks_to_read); |
| 258 vector<char> buf(blocks_to_write * block_size_); |
| 259 |
| 260 // Read in bytes. |
| 261 ssize_t bytes_read = 0; |
| 262 for (int i = 0; i < operation.src_extents_size(); i++) { |
| 263 ssize_t bytes_read_this_iteration = 0; |
| 264 const Extent& extent = operation.src_extents(i); |
| 265 TEST_AND_RETURN_FALSE(utils::PReadAll(fd_, |
| 266 &buf[bytes_read], |
| 267 extent.num_blocks() * block_size_, |
| 268 extent.start_block() * block_size_, |
| 269 &bytes_read_this_iteration)); |
| 270 TEST_AND_RETURN_FALSE( |
| 271 bytes_read_this_iteration == |
| 272 static_cast<ssize_t>(extent.num_blocks() * block_size_)); |
| 273 bytes_read += bytes_read_this_iteration; |
| 274 } |
| 275 |
| 276 // Write bytes out. |
| 277 ssize_t bytes_written = 0; |
| 278 for (int i = 0; i < operation.dst_extents_size(); i++) { |
| 279 const Extent& extent = operation.dst_extents(i); |
| 280 TEST_AND_RETURN_FALSE(utils::PWriteAll(fd_, |
| 281 &buf[bytes_written], |
| 282 extent.num_blocks() * block_size_, |
| 283 extent.start_block() * block_size_)); |
| 284 bytes_written += extent.num_blocks() * block_size_; |
| 285 } |
| 286 DCHECK_EQ(bytes_written, bytes_read); |
| 287 DCHECK_EQ(bytes_written, static_cast<ssize_t>(buf.size())); |
| 288 return true; |
| 289 } |
| 290 |
| 291 bool DeltaPerformer::ExtentsToBsdiffPositionsString( |
| 292 const RepeatedPtrField<Extent>& extents, |
| 293 uint64_t block_size, |
| 294 uint64_t full_length, |
| 295 string* positions_string) { |
| 296 string ret; |
| 297 uint64_t length = 0; |
| 298 for (int i = 0; i < extents.size(); i++) { |
| 299 Extent extent = extents.Get(i); |
| 300 int64_t start = extent.start_block(); |
| 301 uint64_t this_length = min(full_length - length, |
| 302 extent.num_blocks() * block_size); |
| 303 if (start == static_cast<int64_t>(kSparseHole)) |
| 304 start = -1; |
| 305 else |
| 306 start *= block_size; |
| 307 ret += StringPrintf("%" PRIi64 ":%" PRIu64 ",", start, this_length); |
| 308 length += this_length; |
| 309 } |
| 310 TEST_AND_RETURN_FALSE(length == full_length); |
| 311 if (!ret.empty()) |
| 312 ret.resize(ret.size() - 1); // Strip trailing comma off |
| 313 *positions_string = ret; |
| 314 return true; |
| 315 } |
| 316 |
| 317 bool DeltaPerformer::PerformBsdiffOperation( |
| 318 const DeltaArchiveManifest_InstallOperation& operation) { |
| 319 // Since we delete data off the beginning of the buffer as we use it, |
| 320 // the data we need should be exactly at the beginning of the buffer. |
| 321 CHECK_EQ(buffer_offset_, operation.data_offset()); |
| 322 CHECK_GE(buffer_.size(), operation.data_length()); |
| 323 |
| 324 string input_positions; |
| 325 TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(), |
| 326 block_size_, |
| 327 operation.src_length(), |
| 328 &input_positions)); |
| 329 string output_positions; |
| 330 TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.dst_extents(), |
| 331 block_size_, |
| 332 operation.dst_length(), |
| 333 &output_positions)); |
| 334 |
| 335 string temp_filename; |
| 336 TEST_AND_RETURN_FALSE(utils::MakeTempFile("/tmp/au_patch.XXXXXX", |
| 337 &temp_filename, |
| 338 NULL)); |
| 339 ScopedPathUnlinker path_unlinker(temp_filename); |
| 340 { |
| 341 int fd = open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); |
| 342 ScopedFdCloser fd_closer(&fd); |
| 343 TEST_AND_RETURN_FALSE( |
| 344 utils::WriteAll(fd, &buffer_[0], operation.data_length())); |
| 345 } |
| 346 vector<string> cmd; |
| 347 cmd.push_back(kBspatchPath); |
| 348 cmd.push_back(path_); |
| 349 cmd.push_back(path_); |
| 350 cmd.push_back(temp_filename); |
| 351 cmd.push_back(input_positions); |
| 352 cmd.push_back(output_positions); |
| 353 int return_code = 0; |
| 354 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code)); |
| 355 TEST_AND_RETURN_FALSE(return_code == 0); |
| 356 |
| 357 if (operation.dst_length() % block_size_) { |
| 358 // Zero out rest of final block. |
| 359 // TODO(adlr): build this into bspatch; it's more efficient that way. |
| 360 const Extent& last_extent = |
| 361 operation.dst_extents(operation.dst_extents_size() - 1); |
| 362 const uint64_t end_byte = |
| 363 (last_extent.start_block() + last_extent.num_blocks()) * block_size_; |
| 364 const uint64_t begin_byte = |
| 365 end_byte - (block_size_ - operation.dst_length() % block_size_); |
| 366 vector<char> zeros(end_byte - begin_byte); |
| 367 TEST_AND_RETURN_FALSE( |
| 368 utils::PWriteAll(fd_, &zeros[0], end_byte - begin_byte, begin_byte)); |
| 369 } |
| 370 |
| 371 // Update buffer. |
| 372 buffer_offset_ += operation.data_length(); |
| 373 RemoveBufferHeadBytes(&buffer_, operation.data_length()); |
| 374 return true; |
| 375 } |
| 376 |
| 377 } // namespace chromeos_update_engine |
OLD | NEW |