blob: 04273f34e732037e543e29b64254da8485def354 [file] [log] [blame]
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001// 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 <algorithm>
9#include <cstring>
10#include <string>
11#include <vector>
12
13#include <google/protobuf/repeated_field.h>
14
15#include "base/scoped_ptr.h"
16#include "base/string_util.h"
17#include "update_engine/bzip_extent_writer.h"
18#include "update_engine/delta_diff_generator.h"
19#include "update_engine/extent_writer.h"
20#include "update_engine/graph_types.h"
21#include "update_engine/subprocess.h"
22
23using std::min;
24using std::string;
25using std::vector;
26using google::protobuf::RepeatedPtrField;
27
28namespace chromeos_update_engine {
29
30namespace {
31
32const int kDeltaVersionLength = 8;
33const int kDeltaProtobufLengthLength = 8;
34
35// Remove count bytes from the beginning of *buffer.
36void RemoveBufferHeadBytes(vector<char>* buffer, size_t count) {
37 buffer->erase(buffer->begin(), buffer->begin() + count);
38}
39
40// Converts extents to a human-readable string, for use by DumpUpdateProto().
41string ExtentsToString(const RepeatedPtrField<Extent>& extents) {
42 string ret;
43 for (int i = 0; i < extents.size(); i++) {
44 const Extent& extent = extents.Get(i);
45 if (extent.start_block() == kSparseHole) {
46 ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks());
47 } else {
48 ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ",
49 extent.start_block(), extent.num_blocks());
50 }
51 }
52 if (!ret.empty()) {
53 DCHECK_GT(ret.size(), static_cast<size_t>(1));
54 ret.resize(ret.size() - 2);
55 }
56 return ret;
57}
58
59// LOGs a DeltaArchiveManifest object. Useful for debugging.
60void DumpUpdateProto(const DeltaArchiveManifest& manifest) {
61 LOG(INFO) << "Update Proto:";
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070062 LOG(INFO) << " block_size: " << manifest.block_size();
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -070063 for (int i = 0; i < (manifest.install_operations_size() +
64 manifest.kernel_install_operations_size()); i++) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070065 const DeltaArchiveManifest_InstallOperation& op =
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -070066 i < manifest.install_operations_size() ?
67 manifest.install_operations(i) :
68 manifest.kernel_install_operations(
69 i - manifest.install_operations_size());
70 if (i == 0)
71 LOG(INFO) << " Rootfs ops:";
72 else if (i == manifest.install_operations_size())
73 LOG(INFO) << " Kernel ops:";
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070074 LOG(INFO) << " operation(" << i << ")";
75 LOG(INFO) << " type: "
76 << DeltaArchiveManifest_InstallOperation_Type_Name(op.type());
77 if (op.has_data_offset())
78 LOG(INFO) << " data_offset: " << op.data_offset();
79 if (op.has_data_length())
80 LOG(INFO) << " data_length: " << op.data_length();
81 LOG(INFO) << " src_extents: " << ExtentsToString(op.src_extents());
82 if (op.has_src_length())
83 LOG(INFO) << " src_length: " << op.src_length();
84 LOG(INFO) << " dst_extents: " << ExtentsToString(op.dst_extents());
85 if (op.has_dst_length())
86 LOG(INFO) << " dst_length: " << op.dst_length();
87 }
88}
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -070089
90// Opens path for read/write, put the fd into *fd. On success returns true
91// and sets *err to 0. On failure, returns false and sets *err to errno.
92bool OpenFile(const char* path, int* fd, int* err) {
93 if (*fd != -1) {
94 LOG(ERROR) << "Can't open(" << path << "), *fd != -1 (it's " << *fd << ")";
95 *err = EINVAL;
96 return false;
97 }
98 *fd = open(path, O_RDWR, 000);
99 if (*fd < 0) {
100 *err = errno;
101 PLOG(ERROR) << "Unable to open file " << path;
102 return false;
103 }
104 *err = 0;
105 return true;
106}
107
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700108} // namespace {}
109
110int DeltaPerformer::Open(const char* path, int flags, mode_t mode) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700111 int err;
112 if (OpenFile(path, &fd_, &err))
113 path_ = path;
114 return -err;
115}
116
117bool DeltaPerformer::OpenKernel(const char* kernel_path) {
118 int err;
119 bool success = OpenFile(kernel_path, &kernel_fd_, &err);
120 if (success)
121 kernel_path_ = kernel_path;
122 return success;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700123}
124
125int DeltaPerformer::Close() {
126 if (!buffer_.empty()) {
127 LOG(ERROR) << "Called Close() while buffer not empty!";
128 return -1;
129 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700130 int err = 0;
131 if (close(kernel_fd_) == -1) {
132 err = errno;
133 PLOG(ERROR) << "Unable to close kernel fd:";
134 }
135 if (close(fd_) == -1) {
136 err = errno;
137 PLOG(ERROR) << "Unable to close rootfs fd:";
138 }
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700139 fd_ = -2; // Set so that isn't not valid AND calls to Open() will fail.
140 path_ = "";
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700141 return -err;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700142}
143
144// Wrapper around write. Returns bytes written on success or
145// -errno on error.
146// This function performs as many actions as it can, given the amount of
147// data received thus far.
Andrew de los Reyes0cca4212010-04-29 14:00:58 -0700148ssize_t DeltaPerformer::Write(const void* bytes, size_t count) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700149 const char* c_bytes = reinterpret_cast<const char*>(bytes);
150 buffer_.insert(buffer_.end(), c_bytes, c_bytes + count);
151
152 if (!manifest_valid_) {
153 // See if we have enough bytes for the manifest yet
154 if (buffer_.size() < strlen(kDeltaMagic) +
155 kDeltaVersionLength + kDeltaProtobufLengthLength) {
156 // Don't have enough bytes to even know the protobuf length
157 return count;
158 }
159 uint64_t protobuf_length;
160 COMPILE_ASSERT(sizeof(protobuf_length) == kDeltaProtobufLengthLength,
161 protobuf_length_size_mismatch);
162 memcpy(&protobuf_length,
163 &buffer_[strlen(kDeltaMagic) + kDeltaVersionLength],
164 kDeltaProtobufLengthLength);
165 protobuf_length = be64toh(protobuf_length); // switch big endian to host
166 if (buffer_.size() < strlen(kDeltaMagic) + kDeltaVersionLength +
167 kDeltaProtobufLengthLength + protobuf_length) {
168 return count;
169 }
170 // We have the full proto buffer in buffer_. Parse it.
171 const int offset = strlen(kDeltaMagic) + kDeltaVersionLength +
172 kDeltaProtobufLengthLength;
173 if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) {
174 LOG(ERROR) << "Unable to parse manifest in update file.";
175 return -EINVAL;
176 }
177 // Remove protobuf and header info from buffer_, so buffer_ contains
178 // just data blobs
179 RemoveBufferHeadBytes(&buffer_,
180 strlen(kDeltaMagic) +
181 kDeltaVersionLength +
182 kDeltaProtobufLengthLength + protobuf_length);
183 manifest_valid_ = true;
184 block_size_ = manifest_.block_size();
185 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700186 ssize_t total_operations = manifest_.install_operations_size() +
187 manifest_.kernel_install_operations_size();
188 while (next_operation_num_ < total_operations) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700189 const DeltaArchiveManifest_InstallOperation &op =
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700190 next_operation_num_ < manifest_.install_operations_size() ?
191 manifest_.install_operations(next_operation_num_) :
192 manifest_.kernel_install_operations(
193 next_operation_num_ - manifest_.install_operations_size());
194 if (!CanPerformInstallOperation(op))
195 break;
Andrew de los Reyesbef0c7d2010-08-20 10:20:10 -0700196 // Log every thousandth operation, and also the first and last ones
197 if ((next_operation_num_ % 1000 == 0) ||
198 (next_operation_num_ + 1 == total_operations)) {
199 LOG(INFO) << "Performing operation " << (next_operation_num_ + 1) << "/"
200 << total_operations;
201 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700202 bool is_kernel_partition =
203 (next_operation_num_ >= manifest_.install_operations_size());
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700204 if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
205 op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700206 if (!PerformReplaceOperation(op, is_kernel_partition)) {
207 LOG(ERROR) << "Failed to perform replace operation "
208 << next_operation_num_;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700209 return -EINVAL;
210 }
211 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700212 if (!PerformMoveOperation(op, is_kernel_partition)) {
213 LOG(ERROR) << "Failed to perform move operation "
214 << next_operation_num_;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700215 return -EINVAL;
216 }
217 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700218 if (!PerformBsdiffOperation(op, is_kernel_partition)) {
219 LOG(ERROR) << "Failed to perform bsdiff operation "
220 << next_operation_num_;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700221 return -EINVAL;
222 }
223 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700224 next_operation_num_++;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700225 }
226 return count;
227}
228
229bool DeltaPerformer::CanPerformInstallOperation(
230 const chromeos_update_engine::DeltaArchiveManifest_InstallOperation&
231 operation) {
232 // Move operations don't require any data blob, so they can always
233 // be performed
234 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE)
235 return true;
236
237 // See if we have the entire data blob in the buffer
238 if (operation.data_offset() < buffer_offset_) {
239 LOG(ERROR) << "we threw away data it seems?";
240 return false;
241 }
242
243 return (operation.data_offset() + operation.data_length()) <=
244 (buffer_offset_ + buffer_.size());
245}
246
247bool DeltaPerformer::PerformReplaceOperation(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700248 const DeltaArchiveManifest_InstallOperation& operation,
249 bool is_kernel_partition) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700250 CHECK(operation.type() == \
251 DeltaArchiveManifest_InstallOperation_Type_REPLACE || \
252 operation.type() == \
253 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
254
255 // Since we delete data off the beginning of the buffer as we use it,
256 // the data we need should be exactly at the beginning of the buffer.
257 CHECK_EQ(buffer_offset_, operation.data_offset());
258 CHECK_GE(buffer_.size(), operation.data_length());
259
260 DirectExtentWriter direct_writer;
261 ZeroPadExtentWriter zero_pad_writer(&direct_writer);
262 scoped_ptr<BzipExtentWriter> bzip_writer;
263
264 // Since bzip decompression is optional, we have a variable writer that will
265 // point to one of the ExtentWriter objects above.
266 ExtentWriter* writer = NULL;
267 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
268 writer = &zero_pad_writer;
269 } else if (operation.type() ==
270 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
271 bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer));
272 writer = bzip_writer.get();
273 } else {
274 NOTREACHED();
275 }
276
277 // Create a vector of extents to pass to the ExtentWriter.
278 vector<Extent> extents;
279 for (int i = 0; i < operation.dst_extents_size(); i++) {
280 extents.push_back(operation.dst_extents(i));
281 }
282
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700283 int fd = is_kernel_partition ? kernel_fd_ : fd_;
284
285 TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_));
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700286 TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length()));
287 TEST_AND_RETURN_FALSE(writer->End());
288
289 // Update buffer
290 buffer_offset_ += operation.data_length();
291 RemoveBufferHeadBytes(&buffer_, operation.data_length());
292 return true;
293}
294
295bool DeltaPerformer::PerformMoveOperation(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700296 const DeltaArchiveManifest_InstallOperation& operation,
297 bool is_kernel_partition) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700298 // Calculate buffer size. Note, this function doesn't do a sliding
299 // window to copy in case the source and destination blocks overlap.
300 // If we wanted to do a sliding window, we could program the server
301 // to generate deltas that effectively did a sliding window.
302
303 uint64_t blocks_to_read = 0;
304 for (int i = 0; i < operation.src_extents_size(); i++)
305 blocks_to_read += operation.src_extents(i).num_blocks();
306
307 uint64_t blocks_to_write = 0;
308 for (int i = 0; i < operation.dst_extents_size(); i++)
309 blocks_to_write += operation.dst_extents(i).num_blocks();
310
311 DCHECK_EQ(blocks_to_write, blocks_to_read);
312 vector<char> buf(blocks_to_write * block_size_);
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700313
314 int fd = is_kernel_partition ? kernel_fd_ : fd_;
315
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700316 // Read in bytes.
317 ssize_t bytes_read = 0;
318 for (int i = 0; i < operation.src_extents_size(); i++) {
319 ssize_t bytes_read_this_iteration = 0;
320 const Extent& extent = operation.src_extents(i);
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700321 TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700322 &buf[bytes_read],
323 extent.num_blocks() * block_size_,
324 extent.start_block() * block_size_,
325 &bytes_read_this_iteration));
326 TEST_AND_RETURN_FALSE(
327 bytes_read_this_iteration ==
328 static_cast<ssize_t>(extent.num_blocks() * block_size_));
329 bytes_read += bytes_read_this_iteration;
330 }
331
332 // Write bytes out.
333 ssize_t bytes_written = 0;
334 for (int i = 0; i < operation.dst_extents_size(); i++) {
335 const Extent& extent = operation.dst_extents(i);
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700336 TEST_AND_RETURN_FALSE(utils::PWriteAll(fd,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700337 &buf[bytes_written],
338 extent.num_blocks() * block_size_,
339 extent.start_block() * block_size_));
340 bytes_written += extent.num_blocks() * block_size_;
341 }
342 DCHECK_EQ(bytes_written, bytes_read);
343 DCHECK_EQ(bytes_written, static_cast<ssize_t>(buf.size()));
344 return true;
345}
346
347bool DeltaPerformer::ExtentsToBsdiffPositionsString(
348 const RepeatedPtrField<Extent>& extents,
349 uint64_t block_size,
350 uint64_t full_length,
351 string* positions_string) {
352 string ret;
353 uint64_t length = 0;
354 for (int i = 0; i < extents.size(); i++) {
355 Extent extent = extents.Get(i);
356 int64_t start = extent.start_block();
357 uint64_t this_length = min(full_length - length,
358 extent.num_blocks() * block_size);
359 if (start == static_cast<int64_t>(kSparseHole))
360 start = -1;
361 else
362 start *= block_size;
363 ret += StringPrintf("%" PRIi64 ":%" PRIu64 ",", start, this_length);
364 length += this_length;
365 }
366 TEST_AND_RETURN_FALSE(length == full_length);
367 if (!ret.empty())
368 ret.resize(ret.size() - 1); // Strip trailing comma off
369 *positions_string = ret;
370 return true;
371}
372
373bool DeltaPerformer::PerformBsdiffOperation(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700374 const DeltaArchiveManifest_InstallOperation& operation,
375 bool is_kernel_partition) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700376 // Since we delete data off the beginning of the buffer as we use it,
377 // the data we need should be exactly at the beginning of the buffer.
378 CHECK_EQ(buffer_offset_, operation.data_offset());
379 CHECK_GE(buffer_.size(), operation.data_length());
380
381 string input_positions;
382 TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(),
383 block_size_,
384 operation.src_length(),
385 &input_positions));
386 string output_positions;
387 TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.dst_extents(),
388 block_size_,
389 operation.dst_length(),
390 &output_positions));
391
392 string temp_filename;
393 TEST_AND_RETURN_FALSE(utils::MakeTempFile("/tmp/au_patch.XXXXXX",
394 &temp_filename,
395 NULL));
396 ScopedPathUnlinker path_unlinker(temp_filename);
397 {
398 int fd = open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
399 ScopedFdCloser fd_closer(&fd);
400 TEST_AND_RETURN_FALSE(
401 utils::WriteAll(fd, &buffer_[0], operation.data_length()));
402 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700403
404 int fd = is_kernel_partition ? kernel_fd_ : fd_;
405 const string& path = is_kernel_partition ? kernel_path_ : path_;
406
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700407 vector<string> cmd;
408 cmd.push_back(kBspatchPath);
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700409 cmd.push_back(path);
410 cmd.push_back(path);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700411 cmd.push_back(temp_filename);
412 cmd.push_back(input_positions);
413 cmd.push_back(output_positions);
414 int return_code = 0;
415 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code));
416 TEST_AND_RETURN_FALSE(return_code == 0);
417
418 if (operation.dst_length() % block_size_) {
419 // Zero out rest of final block.
420 // TODO(adlr): build this into bspatch; it's more efficient that way.
421 const Extent& last_extent =
422 operation.dst_extents(operation.dst_extents_size() - 1);
423 const uint64_t end_byte =
424 (last_extent.start_block() + last_extent.num_blocks()) * block_size_;
425 const uint64_t begin_byte =
426 end_byte - (block_size_ - operation.dst_length() % block_size_);
427 vector<char> zeros(end_byte - begin_byte);
428 TEST_AND_RETURN_FALSE(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700429 utils::PWriteAll(fd, &zeros[0], end_byte - begin_byte, begin_byte));
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700430 }
431
432 // Update buffer.
433 buffer_offset_ += operation.data_length();
434 RemoveBufferHeadBytes(&buffer_, operation.data_length());
435 return true;
436}
437
438} // namespace chromeos_update_engine