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