blob: 8cf0226e5edbe0fae2e1db4b09e701ae58c282b2 [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 Reyes3270f742010-07-15 22:28:14 -0700196 LOG(INFO) << "Performing operation " << next_operation_num_ << "/"
197 << total_operations;
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700198 bool is_kernel_partition =
199 (next_operation_num_ >= manifest_.install_operations_size());
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700200 if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
201 op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700202 if (!PerformReplaceOperation(op, is_kernel_partition)) {
203 LOG(ERROR) << "Failed to perform replace operation "
204 << next_operation_num_;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700205 return -EINVAL;
206 }
207 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700208 if (!PerformMoveOperation(op, is_kernel_partition)) {
209 LOG(ERROR) << "Failed to perform move operation "
210 << next_operation_num_;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700211 return -EINVAL;
212 }
213 } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700214 if (!PerformBsdiffOperation(op, is_kernel_partition)) {
215 LOG(ERROR) << "Failed to perform bsdiff operation "
216 << next_operation_num_;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700217 return -EINVAL;
218 }
219 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700220 next_operation_num_++;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700221 }
222 return count;
223}
224
225bool DeltaPerformer::CanPerformInstallOperation(
226 const chromeos_update_engine::DeltaArchiveManifest_InstallOperation&
227 operation) {
228 // Move operations don't require any data blob, so they can always
229 // be performed
230 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE)
231 return true;
232
233 // See if we have the entire data blob in the buffer
234 if (operation.data_offset() < buffer_offset_) {
235 LOG(ERROR) << "we threw away data it seems?";
236 return false;
237 }
238
239 return (operation.data_offset() + operation.data_length()) <=
240 (buffer_offset_ + buffer_.size());
241}
242
243bool DeltaPerformer::PerformReplaceOperation(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700244 const DeltaArchiveManifest_InstallOperation& operation,
245 bool is_kernel_partition) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700246 CHECK(operation.type() == \
247 DeltaArchiveManifest_InstallOperation_Type_REPLACE || \
248 operation.type() == \
249 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
250
251 // Since we delete data off the beginning of the buffer as we use it,
252 // the data we need should be exactly at the beginning of the buffer.
253 CHECK_EQ(buffer_offset_, operation.data_offset());
254 CHECK_GE(buffer_.size(), operation.data_length());
255
256 DirectExtentWriter direct_writer;
257 ZeroPadExtentWriter zero_pad_writer(&direct_writer);
258 scoped_ptr<BzipExtentWriter> bzip_writer;
259
260 // Since bzip decompression is optional, we have a variable writer that will
261 // point to one of the ExtentWriter objects above.
262 ExtentWriter* writer = NULL;
263 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
264 writer = &zero_pad_writer;
265 } else if (operation.type() ==
266 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
267 bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer));
268 writer = bzip_writer.get();
269 } else {
270 NOTREACHED();
271 }
272
273 // Create a vector of extents to pass to the ExtentWriter.
274 vector<Extent> extents;
275 for (int i = 0; i < operation.dst_extents_size(); i++) {
276 extents.push_back(operation.dst_extents(i));
277 }
278
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700279 int fd = is_kernel_partition ? kernel_fd_ : fd_;
280
281 TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_));
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700282 TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length()));
283 TEST_AND_RETURN_FALSE(writer->End());
284
285 // Update buffer
286 buffer_offset_ += operation.data_length();
287 RemoveBufferHeadBytes(&buffer_, operation.data_length());
288 return true;
289}
290
291bool DeltaPerformer::PerformMoveOperation(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700292 const DeltaArchiveManifest_InstallOperation& operation,
293 bool is_kernel_partition) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700294 // Calculate buffer size. Note, this function doesn't do a sliding
295 // window to copy in case the source and destination blocks overlap.
296 // If we wanted to do a sliding window, we could program the server
297 // to generate deltas that effectively did a sliding window.
298
299 uint64_t blocks_to_read = 0;
300 for (int i = 0; i < operation.src_extents_size(); i++)
301 blocks_to_read += operation.src_extents(i).num_blocks();
302
303 uint64_t blocks_to_write = 0;
304 for (int i = 0; i < operation.dst_extents_size(); i++)
305 blocks_to_write += operation.dst_extents(i).num_blocks();
306
307 DCHECK_EQ(blocks_to_write, blocks_to_read);
308 vector<char> buf(blocks_to_write * block_size_);
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700309
310 int fd = is_kernel_partition ? kernel_fd_ : fd_;
311
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700312 // Read in bytes.
313 ssize_t bytes_read = 0;
314 for (int i = 0; i < operation.src_extents_size(); i++) {
315 ssize_t bytes_read_this_iteration = 0;
316 const Extent& extent = operation.src_extents(i);
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700317 TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700318 &buf[bytes_read],
319 extent.num_blocks() * block_size_,
320 extent.start_block() * block_size_,
321 &bytes_read_this_iteration));
322 TEST_AND_RETURN_FALSE(
323 bytes_read_this_iteration ==
324 static_cast<ssize_t>(extent.num_blocks() * block_size_));
325 bytes_read += bytes_read_this_iteration;
326 }
327
328 // Write bytes out.
329 ssize_t bytes_written = 0;
330 for (int i = 0; i < operation.dst_extents_size(); i++) {
331 const Extent& extent = operation.dst_extents(i);
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700332 TEST_AND_RETURN_FALSE(utils::PWriteAll(fd,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700333 &buf[bytes_written],
334 extent.num_blocks() * block_size_,
335 extent.start_block() * block_size_));
336 bytes_written += extent.num_blocks() * block_size_;
337 }
338 DCHECK_EQ(bytes_written, bytes_read);
339 DCHECK_EQ(bytes_written, static_cast<ssize_t>(buf.size()));
340 return true;
341}
342
343bool DeltaPerformer::ExtentsToBsdiffPositionsString(
344 const RepeatedPtrField<Extent>& extents,
345 uint64_t block_size,
346 uint64_t full_length,
347 string* positions_string) {
348 string ret;
349 uint64_t length = 0;
350 for (int i = 0; i < extents.size(); i++) {
351 Extent extent = extents.Get(i);
352 int64_t start = extent.start_block();
353 uint64_t this_length = min(full_length - length,
354 extent.num_blocks() * block_size);
355 if (start == static_cast<int64_t>(kSparseHole))
356 start = -1;
357 else
358 start *= block_size;
359 ret += StringPrintf("%" PRIi64 ":%" PRIu64 ",", start, this_length);
360 length += this_length;
361 }
362 TEST_AND_RETURN_FALSE(length == full_length);
363 if (!ret.empty())
364 ret.resize(ret.size() - 1); // Strip trailing comma off
365 *positions_string = ret;
366 return true;
367}
368
369bool DeltaPerformer::PerformBsdiffOperation(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700370 const DeltaArchiveManifest_InstallOperation& operation,
371 bool is_kernel_partition) {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700372 // Since we delete data off the beginning of the buffer as we use it,
373 // the data we need should be exactly at the beginning of the buffer.
374 CHECK_EQ(buffer_offset_, operation.data_offset());
375 CHECK_GE(buffer_.size(), operation.data_length());
376
377 string input_positions;
378 TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(),
379 block_size_,
380 operation.src_length(),
381 &input_positions));
382 string output_positions;
383 TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.dst_extents(),
384 block_size_,
385 operation.dst_length(),
386 &output_positions));
387
388 string temp_filename;
389 TEST_AND_RETURN_FALSE(utils::MakeTempFile("/tmp/au_patch.XXXXXX",
390 &temp_filename,
391 NULL));
392 ScopedPathUnlinker path_unlinker(temp_filename);
393 {
394 int fd = open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
395 ScopedFdCloser fd_closer(&fd);
396 TEST_AND_RETURN_FALSE(
397 utils::WriteAll(fd, &buffer_[0], operation.data_length()));
398 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700399
400 int fd = is_kernel_partition ? kernel_fd_ : fd_;
401 const string& path = is_kernel_partition ? kernel_path_ : path_;
402
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700403 vector<string> cmd;
404 cmd.push_back(kBspatchPath);
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700405 cmd.push_back(path);
406 cmd.push_back(path);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700407 cmd.push_back(temp_filename);
408 cmd.push_back(input_positions);
409 cmd.push_back(output_positions);
410 int return_code = 0;
411 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code));
412 TEST_AND_RETURN_FALSE(return_code == 0);
413
414 if (operation.dst_length() % block_size_) {
415 // Zero out rest of final block.
416 // TODO(adlr): build this into bspatch; it's more efficient that way.
417 const Extent& last_extent =
418 operation.dst_extents(operation.dst_extents_size() - 1);
419 const uint64_t end_byte =
420 (last_extent.start_block() + last_extent.num_blocks()) * block_size_;
421 const uint64_t begin_byte =
422 end_byte - (block_size_ - operation.dst_length() % block_size_);
423 vector<char> zeros(end_byte - begin_byte);
424 TEST_AND_RETURN_FALSE(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700425 utils::PWriteAll(fd, &zeros[0], end_byte - begin_byte, begin_byte));
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700426 }
427
428 // Update buffer.
429 buffer_offset_ += operation.data_length();
430 RemoveBufferHeadBytes(&buffer_, operation.data_length());
431 return true;
432}
433
434} // namespace chromeos_update_engine