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