blob: d42b296e499ac8ab88153b9dfcc54eb8fcbdbfb1 [file] [log] [blame]
Don Garrettf4b28742012-03-27 20:48:06 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// 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_diff_generator.h"
Darin Petkov880335c2010-10-01 15:52:53 -07006
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07007#include <errno.h>
8#include <fcntl.h>
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07009#include <inttypes.h>
Darin Petkov880335c2010-10-01 15:52:53 -070010#include <sys/stat.h>
11#include <sys/types.h>
12
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070013#include <algorithm>
Andrew de los Reyesef017552010-10-06 17:57:52 -070014#include <map>
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070015#include <set>
16#include <string>
17#include <utility>
18#include <vector>
Darin Petkov880335c2010-10-01 15:52:53 -070019
Darin Petkov8e447e02013-04-16 16:23:50 +020020#include <base/file_path.h>
21#include <base/file_util.h>
Darin Petkov880335c2010-10-01 15:52:53 -070022#include <base/logging.h>
Darin Petkov7438a5c2011-08-29 11:56:44 -070023#include <base/memory/scoped_ptr.h>
Darin Petkov8e447e02013-04-16 16:23:50 +020024#include <base/string_number_conversions.h>
Darin Petkov880335c2010-10-01 15:52:53 -070025#include <base/string_util.h>
Mike Frysinger8155d082012-04-06 15:23:18 -040026#include <base/stringprintf.h>
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070027#include <bzlib.h>
Darin Petkov880335c2010-10-01 15:52:53 -070028
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070029#include "update_engine/bzip.h"
30#include "update_engine/cycle_breaker.h"
Don Garrettb8dd1d92013-11-22 17:40:02 -080031#include "update_engine/delta_performer.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070032#include "update_engine/extent_mapper.h"
Andrew de los Reyesef017552010-10-06 17:57:52 -070033#include "update_engine/extent_ranges.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070034#include "update_engine/file_writer.h"
35#include "update_engine/filesystem_iterator.h"
Darin Petkov7a22d792010-11-08 14:10:00 -080036#include "update_engine/full_update_generator.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070037#include "update_engine/graph_types.h"
38#include "update_engine/graph_utils.h"
Thieu Le5c7d9752010-12-15 16:09:28 -080039#include "update_engine/metadata.h"
Darin Petkov36a58222010-10-07 22:00:09 -070040#include "update_engine/omaha_hash_calculator.h"
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070041#include "update_engine/payload_signer.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070042#include "update_engine/subprocess.h"
43#include "update_engine/topological_sort.h"
44#include "update_engine/update_metadata.pb.h"
45#include "update_engine/utils.h"
46
47using std::make_pair;
Andrew de los Reyesef017552010-10-06 17:57:52 -070048using std::map;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070049using std::max;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070050using std::min;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -070051using std::pair;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070052using std::set;
53using std::string;
54using std::vector;
55
56namespace chromeos_update_engine {
57
58typedef DeltaDiffGenerator::Block Block;
Darin Petkov9fa7ec52010-10-18 11:45:23 -070059typedef map<const DeltaArchiveManifest_InstallOperation*,
Darin Petkov8e447e02013-04-16 16:23:50 +020060 string> OperationNameMap;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070061
Chris Sosaf586b012013-05-21 13:33:42 -070062// bytes
63const size_t kRootFSPartitionSize = static_cast<size_t>(2) * 1024 * 1024 * 1024;
Chris Sosad5ae1562013-04-23 13:20:18 -070064const uint64_t kVersionNumber = 1;
65const uint64_t kFullUpdateChunkSize = 1024 * 1024; // bytes
66
Gilad Arnoldfa404502014-01-01 23:36:12 -080067// Needed for testing purposes, in case we can't use actual filesystem objects.
68// TODO(garnold)(chromium:331965) Replace this hack with a properly injected
69// parameter in form of a mockable abstract class.
70bool (*get_extents_with_chunk_func)(const std::string&, off_t, off_t,
71 std::vector<Extent>*) =
72 extent_mapper::ExtentsForFileChunkFibmap;
73
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070074namespace {
Andrew de los Reyes27f7d372010-10-07 11:26:07 -070075const size_t kBlockSize = 4096; // bytes
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -080076const string kNonexistentPath = "";
Andrew de los Reyes927179d2010-12-02 11:26:48 -080077
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070078
Darin Petkov68c10d12010-10-14 09:24:37 -070079static const char* kInstallOperationTypes[] = {
80 "REPLACE",
81 "REPLACE_BZ",
82 "MOVE",
83 "BSDIFF"
84};
85
Gilad Arnoldfa404502014-01-01 23:36:12 -080086// Stores all the extents of |path| into |extents|. Returns true on success.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070087bool GatherExtents(const string& path,
Darin Petkov8e447e02013-04-16 16:23:50 +020088 off_t chunk_offset,
89 off_t chunk_size,
Gilad Arnoldfa404502014-01-01 23:36:12 -080090 vector<Extent>* extents) {
91 extents->clear();
Darin Petkov8e447e02013-04-16 16:23:50 +020092 TEST_AND_RETURN_FALSE(
Gilad Arnoldfa404502014-01-01 23:36:12 -080093 get_extents_with_chunk_func(
94 path, chunk_offset, chunk_size, extents));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070095 return true;
96}
97
Andrew de los Reyesef017552010-10-06 17:57:52 -070098// For a given regular file which must exist at new_root + path, and
99// may exist at old_root + path, creates a new InstallOperation and
100// adds it to the graph. Also, populates the |blocks| array as
101// necessary, if |blocks| is non-NULL. Also, writes the data
102// necessary to send the file down to the client into data_fd, which
103// has length *data_file_size. *data_file_size is updated
104// appropriately. If |existing_vertex| is no kInvalidIndex, use that
105// rather than allocating a new vertex. Returns true on success.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700106bool DeltaReadFile(Graph* graph,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700107 Vertex::Index existing_vertex,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700108 vector<Block>* blocks,
109 const string& old_root,
110 const string& new_root,
111 const string& path, // within new_root
Darin Petkov8e447e02013-04-16 16:23:50 +0200112 off_t chunk_offset,
113 off_t chunk_size,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700114 int data_fd,
115 off_t* data_file_size) {
116 vector<char> data;
117 DeltaArchiveManifest_InstallOperation operation;
118
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800119 string old_path = (old_root == kNonexistentPath) ? kNonexistentPath :
120 old_root + path;
121
Don Garrett1d787092013-03-11 18:07:28 -0700122 // If bsdiff breaks again, blacklist the problem file by using:
123 // bsdiff_allowed = (path != "/foo/bar")
Don Garrett36e60772012-03-29 10:31:20 -0700124 //
Don Garrett1d787092013-03-11 18:07:28 -0700125 // TODO(dgarrett): chromium-os:15274 connect this test to the command line.
Don Garrett36e60772012-03-29 10:31:20 -0700126 bool bsdiff_allowed = true;
Don Garrettf4b28742012-03-27 20:48:06 -0700127
Don Garrett36e60772012-03-29 10:31:20 -0700128 if (!bsdiff_allowed)
129 LOG(INFO) << "bsdiff blacklisting: " << path;
Don Garrettf4b28742012-03-27 20:48:06 -0700130
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800131 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ReadFileToDiff(old_path,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700132 new_root + path,
Darin Petkov8e447e02013-04-16 16:23:50 +0200133 chunk_offset,
134 chunk_size,
Don Garrett36e60772012-03-29 10:31:20 -0700135 bsdiff_allowed,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700136 &data,
Darin Petkov68c10d12010-10-14 09:24:37 -0700137 &operation,
138 true));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700139
Gilad Arnoldfa404502014-01-01 23:36:12 -0800140 // Check if the operation writes nothing.
141 if (operation.dst_extents_size() == 0) {
142 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
143 LOG(INFO) << "Empty MOVE operation (" << new_root + path << "), skipping";
144 return true;
145 } else {
146 LOG(ERROR) << "Empty non-MOVE operation";
147 return false;
148 }
149 }
150
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700151 // Write the data
152 if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) {
153 operation.set_data_offset(*data_file_size);
154 operation.set_data_length(data.size());
155 }
156
157 TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, &data[0], data.size()));
158 *data_file_size += data.size();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700159
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700160 // Now, insert into graph and blocks vector
Andrew de los Reyesef017552010-10-06 17:57:52 -0700161 Vertex::Index vertex = existing_vertex;
162 if (vertex == Vertex::kInvalidIndex) {
163 graph->resize(graph->size() + 1);
164 vertex = graph->size() - 1;
165 }
166 (*graph)[vertex].op = operation;
167 CHECK((*graph)[vertex].op.has_type());
168 (*graph)[vertex].file_name = path;
Darin Petkov8e447e02013-04-16 16:23:50 +0200169 (*graph)[vertex].chunk_offset = chunk_offset;
170 (*graph)[vertex].chunk_size = chunk_size;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700171
Andrew de los Reyesef017552010-10-06 17:57:52 -0700172 if (blocks)
Thieu Le5c7d9752010-12-15 16:09:28 -0800173 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::AddInstallOpToBlocksVector(
174 (*graph)[vertex].op,
175 *graph,
176 vertex,
177 blocks));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700178 return true;
179}
180
181// For each regular file within new_root, creates a node in the graph,
182// determines the best way to compress it (REPLACE, REPLACE_BZ, COPY, BSDIFF),
183// and writes any necessary data to the end of data_fd.
184bool DeltaReadFiles(Graph* graph,
185 vector<Block>* blocks,
186 const string& old_root,
187 const string& new_root,
Darin Petkov8e447e02013-04-16 16:23:50 +0200188 off_t chunk_size,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700189 int data_fd,
190 off_t* data_file_size) {
191 set<ino_t> visited_inodes;
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800192 set<ino_t> visited_src_inodes;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700193 for (FilesystemIterator fs_iter(new_root,
194 utils::SetWithValue<string>("/lost+found"));
195 !fs_iter.IsEnd(); fs_iter.Increment()) {
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800196 // We never diff symlinks (here, we check that dst file is not a symlink).
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700197 if (!S_ISREG(fs_iter.GetStat().st_mode))
198 continue;
199
200 // Make sure we visit each inode only once.
201 if (utils::SetContainsKey(visited_inodes, fs_iter.GetStat().st_ino))
202 continue;
203 visited_inodes.insert(fs_iter.GetStat().st_ino);
Darin Petkov8e447e02013-04-16 16:23:50 +0200204 off_t dst_size = fs_iter.GetStat().st_size;
205 if (dst_size == 0)
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700206 continue;
207
208 LOG(INFO) << "Encoding file " << fs_iter.GetPartialPath();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700209
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800210 // We can't visit each dst image inode more than once, as that would
211 // duplicate work. Here, we avoid visiting each source image inode
212 // more than once. Technically, we could have multiple operations
213 // that read the same blocks from the source image for diffing, but
214 // we choose not to to avoid complexity. Eventually we will move away
215 // from using a graph/cycle detection/etc to generate diffs, and at that
216 // time, it will be easy (non-complex) to have many operations read
217 // from the same source blocks. At that time, this code can die. -adlr
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800218 bool should_diff_from_source = false;
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800219 string src_path = old_root + fs_iter.GetPartialPath();
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800220 struct stat src_stbuf;
221 // We never diff symlinks (here, we check that src file is not a symlink).
222 if (0 == lstat(src_path.c_str(), &src_stbuf) &&
223 S_ISREG(src_stbuf.st_mode)) {
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800224 should_diff_from_source = !utils::SetContainsKey(visited_src_inodes,
225 src_stbuf.st_ino);
226 visited_src_inodes.insert(src_stbuf.st_ino);
227 }
228
Darin Petkov8e447e02013-04-16 16:23:50 +0200229 off_t size = chunk_size == -1 ? dst_size : chunk_size;
230 off_t step = size;
231 for (off_t offset = 0; offset < dst_size; offset += step) {
232 if (offset + size >= dst_size) {
233 size = -1; // Read through the end of the file.
234 }
235 TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
236 Vertex::kInvalidIndex,
237 blocks,
238 (should_diff_from_source ?
239 old_root :
240 kNonexistentPath),
241 new_root,
242 fs_iter.GetPartialPath(),
243 offset,
244 size,
245 data_fd,
246 data_file_size));
247 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700248 }
249 return true;
250}
251
Andrew de los Reyesef017552010-10-06 17:57:52 -0700252// This class allocates non-existent temp blocks, starting from
253// kTempBlockStart. Other code is responsible for converting these
254// temp blocks into real blocks, as the client can't read or write to
255// these blocks.
256class DummyExtentAllocator {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700257 public:
Andrew de los Reyesef017552010-10-06 17:57:52 -0700258 explicit DummyExtentAllocator()
259 : next_block_(kTempBlockStart) {}
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700260 vector<Extent> Allocate(const uint64_t block_count) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700261 vector<Extent> ret(1);
262 ret[0].set_start_block(next_block_);
263 ret[0].set_num_blocks(block_count);
264 next_block_ += block_count;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700265 return ret;
266 }
267 private:
Andrew de los Reyesef017552010-10-06 17:57:52 -0700268 uint64_t next_block_;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700269};
270
271// Reads blocks from image_path that are not yet marked as being written
272// in the blocks array. These blocks that remain are non-file-data blocks.
273// In the future we might consider intelligent diffing between this data
274// and data in the previous image, but for now we just bzip2 compress it
275// and include it in the update.
276// Creates a new node in the graph to write these blocks and writes the
277// appropriate blob to blobs_fd. Reads and updates blobs_length;
278bool ReadUnwrittenBlocks(const vector<Block>& blocks,
279 int blobs_fd,
280 off_t* blobs_length,
281 const string& image_path,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700282 Vertex* vertex) {
Darin Petkovabe7cc92010-10-08 12:29:32 -0700283 vertex->file_name = "<rootfs-non-file-data>";
284
Andrew de los Reyesef017552010-10-06 17:57:52 -0700285 DeltaArchiveManifest_InstallOperation* out_op = &vertex->op;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700286 int image_fd = open(image_path.c_str(), O_RDONLY, 000);
287 TEST_AND_RETURN_FALSE_ERRNO(image_fd >= 0);
288 ScopedFdCloser image_fd_closer(&image_fd);
289
290 string temp_file_path;
Gilad Arnolda6742b32014-01-11 00:18:34 -0800291 TEST_AND_RETURN_FALSE(utils::MakeTempFile("CrAU_temp_data.XXXXXX",
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700292 &temp_file_path,
293 NULL));
294
295 FILE* file = fopen(temp_file_path.c_str(), "w");
296 TEST_AND_RETURN_FALSE(file);
297 int err = BZ_OK;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700298
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700299 BZFILE* bz_file = BZ2_bzWriteOpen(&err,
300 file,
301 9, // max compression
302 0, // verbosity
303 0); // default work factor
304 TEST_AND_RETURN_FALSE(err == BZ_OK);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700305
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700306 vector<Extent> extents;
307 vector<Block>::size_type block_count = 0;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700308
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700309 LOG(INFO) << "Appending left over blocks to extents";
310 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
311 if (blocks[i].writer != Vertex::kInvalidIndex)
312 continue;
Andrew de los Reyesef017552010-10-06 17:57:52 -0700313 if (blocks[i].reader != Vertex::kInvalidIndex) {
314 graph_utils::AddReadBeforeDep(vertex, blocks[i].reader, i);
315 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700316 graph_utils::AppendBlockToExtents(&extents, i);
317 block_count++;
318 }
319
320 // Code will handle 'buf' at any size that's a multiple of kBlockSize,
321 // so we arbitrarily set it to 1024 * kBlockSize.
322 vector<char> buf(1024 * kBlockSize);
323
324 LOG(INFO) << "Reading left over blocks";
325 vector<Block>::size_type blocks_copied_count = 0;
326
327 // For each extent in extents, write the data into BZ2_bzWrite which
328 // sends it to an output file.
329 // We use the temporary buffer 'buf' to hold the data, which may be
330 // smaller than the extent, so in that case we have to loop to get
331 // the extent's data (that's the inner while loop).
332 for (vector<Extent>::const_iterator it = extents.begin();
333 it != extents.end(); ++it) {
334 vector<Block>::size_type blocks_read = 0;
Andrew de los Reyes4b8740f2010-11-08 17:09:11 -0800335 float printed_progress = -1;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700336 while (blocks_read < it->num_blocks()) {
337 const int copy_block_cnt =
338 min(buf.size() / kBlockSize,
339 static_cast<vector<char>::size_type>(
340 it->num_blocks() - blocks_read));
341 ssize_t rc = pread(image_fd,
342 &buf[0],
343 copy_block_cnt * kBlockSize,
344 (it->start_block() + blocks_read) * kBlockSize);
345 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
346 TEST_AND_RETURN_FALSE(static_cast<size_t>(rc) ==
347 copy_block_cnt * kBlockSize);
348 BZ2_bzWrite(&err, bz_file, &buf[0], copy_block_cnt * kBlockSize);
349 TEST_AND_RETURN_FALSE(err == BZ_OK);
350 blocks_read += copy_block_cnt;
351 blocks_copied_count += copy_block_cnt;
Andrew de los Reyes4b8740f2010-11-08 17:09:11 -0800352 float current_progress =
353 static_cast<float>(blocks_copied_count) / block_count;
354 if (printed_progress + 0.1 < current_progress ||
355 blocks_copied_count == block_count) {
356 LOG(INFO) << "progress: " << current_progress;
357 printed_progress = current_progress;
358 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700359 }
360 }
361 BZ2_bzWriteClose(&err, bz_file, 0, NULL, NULL);
362 TEST_AND_RETURN_FALSE(err == BZ_OK);
363 bz_file = NULL;
364 TEST_AND_RETURN_FALSE_ERRNO(0 == fclose(file));
365 file = NULL;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700366
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700367 vector<char> compressed_data;
368 LOG(INFO) << "Reading compressed data off disk";
369 TEST_AND_RETURN_FALSE(utils::ReadFile(temp_file_path, &compressed_data));
370 TEST_AND_RETURN_FALSE(unlink(temp_file_path.c_str()) == 0);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700371
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700372 // Add node to graph to write these blocks
373 out_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
374 out_op->set_data_offset(*blobs_length);
375 out_op->set_data_length(compressed_data.size());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700376 LOG(INFO) << "Rootfs non-data blocks compressed take up "
377 << compressed_data.size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700378 *blobs_length += compressed_data.size();
379 out_op->set_dst_length(kBlockSize * block_count);
380 DeltaDiffGenerator::StoreExtents(extents, out_op->mutable_dst_extents());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700381
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700382 TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd,
383 &compressed_data[0],
384 compressed_data.size()));
385 LOG(INFO) << "done with extra blocks";
386 return true;
387}
388
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700389// Writes the uint64_t passed in in host-endian to the file as big-endian.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700390// Returns true on success.
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700391bool WriteUint64AsBigEndian(FileWriter* writer, const uint64_t value) {
392 uint64_t value_be = htobe64(value);
Don Garrette410e0f2011-11-10 15:39:01 -0800393 TEST_AND_RETURN_FALSE(writer->Write(&value_be, sizeof(value_be)));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700394 return true;
395}
396
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700397// Adds each operation from |graph| to |out_manifest| in the order specified by
398// |order| while building |out_op_name_map| with operation to name
399// mappings. Adds all |kernel_ops| to |out_manifest|. Filters out no-op
400// operations.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700401void InstallOperationsToManifest(
402 const Graph& graph,
403 const vector<Vertex::Index>& order,
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700404 const vector<DeltaArchiveManifest_InstallOperation>& kernel_ops,
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700405 DeltaArchiveManifest* out_manifest,
406 OperationNameMap* out_op_name_map) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700407 for (vector<Vertex::Index>::const_iterator it = order.begin();
408 it != order.end(); ++it) {
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700409 const Vertex& vertex = graph[*it];
410 const DeltaArchiveManifest_InstallOperation& add_op = vertex.op;
411 if (DeltaDiffGenerator::IsNoopOperation(add_op)) {
412 continue;
413 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700414 DeltaArchiveManifest_InstallOperation* op =
415 out_manifest->add_install_operations();
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700416 *op = add_op;
Darin Petkov8e447e02013-04-16 16:23:50 +0200417 string name = vertex.file_name;
418 if (vertex.chunk_offset || vertex.chunk_size != -1) {
419 string offset = base::Int64ToString(vertex.chunk_offset);
420 if (vertex.chunk_size != -1) {
421 name += " [" + offset + ", " +
422 base::Int64ToString(vertex.chunk_offset + vertex.chunk_size - 1) +
423 "]";
424 } else {
425 name += " [" + offset + ", end]";
426 }
427 }
428 (*out_op_name_map)[op] = name;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700429 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700430 for (vector<DeltaArchiveManifest_InstallOperation>::const_iterator it =
431 kernel_ops.begin(); it != kernel_ops.end(); ++it) {
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700432 const DeltaArchiveManifest_InstallOperation& add_op = *it;
433 if (DeltaDiffGenerator::IsNoopOperation(add_op)) {
434 continue;
435 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700436 DeltaArchiveManifest_InstallOperation* op =
437 out_manifest->add_kernel_install_operations();
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700438 *op = add_op;
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700439 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700440}
441
442void CheckGraph(const Graph& graph) {
443 for (Graph::const_iterator it = graph.begin(); it != graph.end(); ++it) {
444 CHECK(it->op.has_type());
445 }
446}
447
Darin Petkov68c10d12010-10-14 09:24:37 -0700448// Delta compresses a kernel partition |new_kernel_part| with knowledge of the
449// old kernel partition |old_kernel_part|. If |old_kernel_part| is an empty
450// string, generates a full update of the partition.
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700451bool DeltaCompressKernelPartition(
452 const string& old_kernel_part,
453 const string& new_kernel_part,
454 vector<DeltaArchiveManifest_InstallOperation>* ops,
455 int blobs_fd,
456 off_t* blobs_length) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700457 LOG(INFO) << "Delta compressing kernel partition...";
Darin Petkov68c10d12010-10-14 09:24:37 -0700458 LOG_IF(INFO, old_kernel_part.empty()) << "Generating full kernel update...";
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700459
Gilad Arnoldfa404502014-01-01 23:36:12 -0800460 DeltaArchiveManifest_InstallOperation op;
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700461 vector<char> data;
Don Garrett36e60772012-03-29 10:31:20 -0700462 TEST_AND_RETURN_FALSE(
463 DeltaDiffGenerator::ReadFileToDiff(old_kernel_part,
464 new_kernel_part,
Darin Petkov8e447e02013-04-16 16:23:50 +0200465 0, // chunk_offset
466 -1, // chunk_size
Don Garrett36e60772012-03-29 10:31:20 -0700467 true, // bsdiff_allowed
468 &data,
Gilad Arnoldfa404502014-01-01 23:36:12 -0800469 &op,
Don Garrett36e60772012-03-29 10:31:20 -0700470 false));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700471
Gilad Arnoldfa404502014-01-01 23:36:12 -0800472 // Check if the operation writes nothing.
473 if (op.dst_extents_size() == 0) {
474 if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
475 LOG(INFO) << "Empty MOVE operation, nothing to do.";
476 return true;
477 } else {
478 LOG(ERROR) << "Empty non-MOVE operation";
479 return false;
480 }
Darin Petkov68c10d12010-10-14 09:24:37 -0700481 }
Andrew de los Reyes36f37362010-09-03 09:20:04 -0700482
Gilad Arnoldfa404502014-01-01 23:36:12 -0800483 // Write the data.
484 if (op.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) {
485 op.set_data_offset(*blobs_length);
486 op.set_data_length(data.size());
487 }
488
489 // Add the new install operation.
490 ops->clear();
491 ops->push_back(op);
492
Darin Petkov68c10d12010-10-14 09:24:37 -0700493 TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd, &data[0], data.size()));
494 *blobs_length += data.size();
Andrew de los Reyes36f37362010-09-03 09:20:04 -0700495
Darin Petkov68c10d12010-10-14 09:24:37 -0700496 LOG(INFO) << "Done delta compressing kernel partition: "
Gilad Arnoldfa404502014-01-01 23:36:12 -0800497 << kInstallOperationTypes[op.type()];
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700498 return true;
499}
500
Darin Petkov880335c2010-10-01 15:52:53 -0700501struct DeltaObject {
502 DeltaObject(const string& in_name, const int in_type, const off_t in_size)
503 : name(in_name),
504 type(in_type),
505 size(in_size) {}
506 bool operator <(const DeltaObject& object) const {
Darin Petkovd43d6902010-10-14 11:17:50 -0700507 return (size != object.size) ? (size < object.size) : (name < object.name);
Darin Petkov880335c2010-10-01 15:52:53 -0700508 }
509 string name;
510 int type;
511 off_t size;
512};
513
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700514void ReportPayloadUsage(const DeltaArchiveManifest& manifest,
515 const int64_t manifest_metadata_size,
516 const OperationNameMap& op_name_map) {
Darin Petkov880335c2010-10-01 15:52:53 -0700517 vector<DeltaObject> objects;
518 off_t total_size = 0;
519
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700520 // Rootfs install operations.
521 for (int i = 0; i < manifest.install_operations_size(); ++i) {
522 const DeltaArchiveManifest_InstallOperation& op =
523 manifest.install_operations(i);
Darin Petkov8e447e02013-04-16 16:23:50 +0200524 objects.push_back(DeltaObject(op_name_map.find(&op)->second,
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700525 op.type(),
526 op.data_length()));
527 total_size += op.data_length();
Darin Petkov880335c2010-10-01 15:52:53 -0700528 }
529
Darin Petkov880335c2010-10-01 15:52:53 -0700530 // Kernel install operations.
531 for (int i = 0; i < manifest.kernel_install_operations_size(); ++i) {
532 const DeltaArchiveManifest_InstallOperation& op =
533 manifest.kernel_install_operations(i);
534 objects.push_back(DeltaObject(StringPrintf("<kernel-operation-%d>", i),
535 op.type(),
536 op.data_length()));
537 total_size += op.data_length();
538 }
539
Darin Petkov95cf01f2010-10-12 14:59:13 -0700540 objects.push_back(DeltaObject("<manifest-metadata>",
541 -1,
542 manifest_metadata_size));
543 total_size += manifest_metadata_size;
544
Darin Petkov880335c2010-10-01 15:52:53 -0700545 std::sort(objects.begin(), objects.end());
546
547 static const char kFormatString[] = "%6.2f%% %10llu %-10s %s\n";
548 for (vector<DeltaObject>::const_iterator it = objects.begin();
549 it != objects.end(); ++it) {
550 const DeltaObject& object = *it;
551 fprintf(stderr, kFormatString,
552 object.size * 100.0 / total_size,
553 object.size,
Darin Petkov95cf01f2010-10-12 14:59:13 -0700554 object.type >= 0 ? kInstallOperationTypes[object.type] : "-",
Darin Petkov880335c2010-10-01 15:52:53 -0700555 object.name.c_str());
556 }
557 fprintf(stderr, kFormatString, 100.0, total_size, "", "<total>");
558}
559
Gilad Arnoldfa404502014-01-01 23:36:12 -0800560// Process a range of blocks from |range_start| to |range_end| in the extent at
561// position |*idx_p| of |extents|. If |do_remove| is true, this range will be
562// removed, which may cause the extent to be trimmed, split or removed entirely.
563// The value of |*idx_p| is updated to point to the next extent to be processed.
564// Returns true iff the next extent to process is a new or updated one.
565bool ProcessExtentBlockRange(vector<Extent>* extents, size_t* idx_p,
566 const bool do_remove, uint64_t range_start,
567 uint64_t range_end) {
568 size_t idx = *idx_p;
569 uint64_t start_block = (*extents)[idx].start_block();
570 uint64_t num_blocks = (*extents)[idx].num_blocks();
571 uint64_t range_size = range_end - range_start;
572
573 if (do_remove) {
574 if (range_size == num_blocks) {
575 // Remove the entire extent.
576 extents->erase(extents->begin() + idx);
577 } else if (range_end == num_blocks) {
578 // Trim the end of the extent.
579 (*extents)[idx].set_num_blocks(num_blocks - range_size);
580 idx++;
581 } else if (range_start == 0) {
582 // Trim the head of the extent.
583 (*extents)[idx].set_start_block(start_block + range_size);
584 (*extents)[idx].set_num_blocks(num_blocks - range_size);
585 } else {
586 // Trim the middle, splitting the remainder into two parts.
587 (*extents)[idx].set_num_blocks(range_start);
588 Extent e;
589 e.set_start_block(start_block + range_end);
590 e.set_num_blocks(num_blocks - range_end);
591 idx++;
592 extents->insert(extents->begin() + idx, e);
593 }
594 } else if (range_end == num_blocks) {
595 // Done with this extent.
596 idx++;
597 } else {
598 return false;
599 }
600
601 *idx_p = idx;
602 return true;
603}
604
605// Remove identical corresponding block ranges in |src_extents| and
606// |dst_extents|. Used for preventing moving of blocks onto themselves during
607// MOVE operations.
608void RemoveIdenticalBlockRanges(vector<Extent>* src_extents,
609 vector<Extent>* dst_extents) {
610 size_t src_idx = 0;
611 size_t dst_idx = 0;
612 uint64_t src_offset = 0, dst_offset = 0;
613 bool new_src = true, new_dst = true;
614 while (src_idx < src_extents->size() && dst_idx < dst_extents->size()) {
615 if (new_src) {
616 src_offset = 0;
617 new_src = false;
618 }
619 if (new_dst) {
620 dst_offset = 0;
621 new_dst = false;
622 }
623
624 bool do_remove = ((*src_extents)[src_idx].start_block() + src_offset ==
625 (*dst_extents)[dst_idx].start_block() + dst_offset);
626
627 uint64_t src_num_blocks = (*src_extents)[src_idx].num_blocks();
628 uint64_t dst_num_blocks = (*dst_extents)[dst_idx].num_blocks();
629 uint64_t min_num_blocks = min(src_num_blocks - src_offset,
630 dst_num_blocks - dst_offset);
631 uint64_t prev_src_offset = src_offset;
632 uint64_t prev_dst_offset = dst_offset;
633 src_offset += min_num_blocks;
634 dst_offset += min_num_blocks;
635
636 new_src = ProcessExtentBlockRange(src_extents, &src_idx, do_remove,
637 prev_src_offset, src_offset);
638 new_dst = ProcessExtentBlockRange(dst_extents, &dst_idx, do_remove,
639 prev_dst_offset, dst_offset);
640 }
641}
642
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700643} // namespace {}
644
645bool DeltaDiffGenerator::ReadFileToDiff(
646 const string& old_filename,
647 const string& new_filename,
Darin Petkov8e447e02013-04-16 16:23:50 +0200648 off_t chunk_offset,
649 off_t chunk_size,
Don Garrett36e60772012-03-29 10:31:20 -0700650 bool bsdiff_allowed,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700651 vector<char>* out_data,
Darin Petkov68c10d12010-10-14 09:24:37 -0700652 DeltaArchiveManifest_InstallOperation* out_op,
653 bool gather_extents) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700654 // Read new data in
655 vector<char> new_data;
Darin Petkov8e447e02013-04-16 16:23:50 +0200656 TEST_AND_RETURN_FALSE(
657 utils::ReadFileChunk(new_filename, chunk_offset, chunk_size, &new_data));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700658
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700659 TEST_AND_RETURN_FALSE(!new_data.empty());
Darin Petkov8e447e02013-04-16 16:23:50 +0200660 TEST_AND_RETURN_FALSE(chunk_size == -1 ||
661 static_cast<off_t>(new_data.size()) <= chunk_size);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700662
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700663 vector<char> new_data_bz;
664 TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz));
665 CHECK(!new_data_bz.empty());
666
667 vector<char> data; // Data blob that will be written to delta file.
668
669 DeltaArchiveManifest_InstallOperation operation;
670 size_t current_best_size = 0;
671 if (new_data.size() <= new_data_bz.size()) {
672 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
673 current_best_size = new_data.size();
674 data = new_data;
675 } else {
676 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
677 current_best_size = new_data_bz.size();
678 data = new_data_bz;
679 }
680
681 // Do we have an original file to consider?
682 struct stat old_stbuf;
Don Garrettf4b28742012-03-27 20:48:06 -0700683 bool original = !old_filename.empty();
684 if (original && 0 != stat(old_filename.c_str(), &old_stbuf)) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700685 // If stat-ing the old file fails, it should be because it doesn't exist.
686 TEST_AND_RETURN_FALSE(errno == ENOTDIR || errno == ENOENT);
Don Garrettf4b28742012-03-27 20:48:06 -0700687 original = false;
Darin Petkov68c10d12010-10-14 09:24:37 -0700688 }
Don Garrettf4b28742012-03-27 20:48:06 -0700689
Darin Petkov8e447e02013-04-16 16:23:50 +0200690 vector<char> old_data;
Don Garrettf4b28742012-03-27 20:48:06 -0700691 if (original) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700692 // Read old data
Darin Petkov8e447e02013-04-16 16:23:50 +0200693 TEST_AND_RETURN_FALSE(
694 utils::ReadFileChunk(
695 old_filename, chunk_offset, chunk_size, &old_data));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700696 if (old_data == new_data) {
697 // No change in data.
698 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
699 current_best_size = 0;
700 data.clear();
Darin Petkov8e447e02013-04-16 16:23:50 +0200701 } else if (!old_data.empty() && bsdiff_allowed) {
Don Garrett36e60772012-03-29 10:31:20 -0700702 // If the source file is considered bsdiff safe (no bsdiff bugs
703 // triggered), see if BSDIFF encoding is smaller.
Darin Petkov8e447e02013-04-16 16:23:50 +0200704 FilePath old_chunk;
705 TEST_AND_RETURN_FALSE(file_util::CreateTemporaryFile(&old_chunk));
706 ScopedPathUnlinker old_unlinker(old_chunk.value());
707 TEST_AND_RETURN_FALSE(
708 utils::WriteFile(old_chunk.value().c_str(),
709 &old_data[0], old_data.size()));
710 FilePath new_chunk;
711 TEST_AND_RETURN_FALSE(file_util::CreateTemporaryFile(&new_chunk));
712 ScopedPathUnlinker new_unlinker(new_chunk.value());
713 TEST_AND_RETURN_FALSE(
714 utils::WriteFile(new_chunk.value().c_str(),
715 &new_data[0], new_data.size()));
716
Don Garrett36e60772012-03-29 10:31:20 -0700717 vector<char> bsdiff_delta;
718 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200719 BsdiffFiles(old_chunk.value(), new_chunk.value(), &bsdiff_delta));
Don Garrett36e60772012-03-29 10:31:20 -0700720 CHECK_GT(bsdiff_delta.size(), static_cast<vector<char>::size_type>(0));
721 if (bsdiff_delta.size() < current_best_size) {
722 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
723 current_best_size = bsdiff_delta.size();
724 data = bsdiff_delta;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700725 }
726 }
727 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700728
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700729 // Set parameters of the operations
730 CHECK_EQ(data.size(), current_best_size);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700731
Gilad Arnoldfa404502014-01-01 23:36:12 -0800732 vector<Extent> src_extents, dst_extents;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700733 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE ||
734 operation.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) {
Darin Petkov68c10d12010-10-14 09:24:37 -0700735 if (gather_extents) {
736 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200737 GatherExtents(old_filename,
738 chunk_offset,
739 chunk_size,
Gilad Arnoldfa404502014-01-01 23:36:12 -0800740 &src_extents));
Darin Petkov68c10d12010-10-14 09:24:37 -0700741 } else {
742 Extent* src_extent = operation.add_src_extents();
743 src_extent->set_start_block(0);
744 src_extent->set_num_blocks(
745 (old_stbuf.st_size + kBlockSize - 1) / kBlockSize);
746 }
Darin Petkov8e447e02013-04-16 16:23:50 +0200747 operation.set_src_length(old_data.size());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700748 }
749
Darin Petkov68c10d12010-10-14 09:24:37 -0700750 if (gather_extents) {
751 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200752 GatherExtents(new_filename,
753 chunk_offset,
754 chunk_size,
Gilad Arnoldfa404502014-01-01 23:36:12 -0800755 &dst_extents));
Darin Petkov68c10d12010-10-14 09:24:37 -0700756 } else {
757 Extent* dst_extent = operation.add_dst_extents();
758 dst_extent->set_start_block(0);
759 dst_extent->set_num_blocks((new_data.size() + kBlockSize - 1) / kBlockSize);
760 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700761 operation.set_dst_length(new_data.size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700762
Gilad Arnoldfa404502014-01-01 23:36:12 -0800763 if (gather_extents) {
764 // Remove identical src/dst block ranges in MOVE operations.
765 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE)
766 RemoveIdenticalBlockRanges(&src_extents, &dst_extents);
767
768 // Embed extents in the operation.
769 DeltaDiffGenerator::StoreExtents(src_extents,
770 operation.mutable_src_extents());
771 DeltaDiffGenerator::StoreExtents(dst_extents,
772 operation.mutable_dst_extents());
773 }
774
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700775 out_data->swap(data);
776 *out_op = operation;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700777
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700778 return true;
779}
780
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700781bool DeltaDiffGenerator::InitializePartitionInfo(bool is_kernel,
782 const string& partition,
783 PartitionInfo* info) {
Darin Petkov7ea32332010-10-13 10:46:11 -0700784 int64_t size = 0;
785 if (is_kernel) {
786 size = utils::FileSize(partition);
787 } else {
788 int block_count = 0, block_size = 0;
789 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(partition,
790 &block_count,
791 &block_size));
792 size = static_cast<int64_t>(block_count) * block_size;
793 }
794 TEST_AND_RETURN_FALSE(size > 0);
Darin Petkov36a58222010-10-07 22:00:09 -0700795 info->set_size(size);
796 OmahaHashCalculator hasher;
Darin Petkov7ea32332010-10-13 10:46:11 -0700797 TEST_AND_RETURN_FALSE(hasher.UpdateFile(partition, size) == size);
Darin Petkov36a58222010-10-07 22:00:09 -0700798 TEST_AND_RETURN_FALSE(hasher.Finalize());
799 const vector<char>& hash = hasher.raw_hash();
800 info->set_hash(hash.data(), hash.size());
Darin Petkovd43d6902010-10-14 11:17:50 -0700801 LOG(INFO) << partition << ": size=" << size << " hash=" << hasher.hash();
Darin Petkov36a58222010-10-07 22:00:09 -0700802 return true;
803}
804
805bool InitializePartitionInfos(const string& old_kernel,
806 const string& new_kernel,
807 const string& old_rootfs,
808 const string& new_rootfs,
809 DeltaArchiveManifest* manifest) {
Darin Petkovd43d6902010-10-14 11:17:50 -0700810 if (!old_kernel.empty()) {
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700811 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
812 true,
813 old_kernel,
814 manifest->mutable_old_kernel_info()));
Darin Petkovd43d6902010-10-14 11:17:50 -0700815 }
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700816 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
817 true,
818 new_kernel,
819 manifest->mutable_new_kernel_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700820 if (!old_rootfs.empty()) {
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700821 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
822 false,
823 old_rootfs,
824 manifest->mutable_old_rootfs_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700825 }
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700826 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
827 false,
828 new_rootfs,
829 manifest->mutable_new_rootfs_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700830 return true;
831}
832
Andrew de los Reyesef017552010-10-06 17:57:52 -0700833namespace {
834
835// Takes a collection (vector or RepeatedPtrField) of Extent and
836// returns a vector of the blocks referenced, in order.
837template<typename T>
838vector<uint64_t> ExpandExtents(const T& extents) {
839 vector<uint64_t> ret;
840 for (size_t i = 0, e = static_cast<size_t>(extents.size()); i != e; ++i) {
841 const Extent extent = graph_utils::GetElement(extents, i);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700842 if (extent.start_block() == kSparseHole) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700843 ret.resize(ret.size() + extent.num_blocks(), kSparseHole);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700844 } else {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700845 for (uint64_t block = extent.start_block();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700846 block < (extent.start_block() + extent.num_blocks()); block++) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700847 ret.push_back(block);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700848 }
849 }
850 }
Andrew de los Reyesef017552010-10-06 17:57:52 -0700851 return ret;
852}
853
854// Takes a vector of blocks and returns an equivalent vector of Extent
855// objects.
856vector<Extent> CompressExtents(const vector<uint64_t>& blocks) {
857 vector<Extent> new_extents;
858 for (vector<uint64_t>::const_iterator it = blocks.begin(), e = blocks.end();
859 it != e; ++it) {
860 graph_utils::AppendBlockToExtents(&new_extents, *it);
861 }
862 return new_extents;
863}
864
865} // namespace {}
866
867void DeltaDiffGenerator::SubstituteBlocks(
868 Vertex* vertex,
869 const vector<Extent>& remove_extents,
870 const vector<Extent>& replace_extents) {
871 // First, expand out the blocks that op reads from
872 vector<uint64_t> read_blocks = ExpandExtents(vertex->op.src_extents());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700873 {
874 // Expand remove_extents and replace_extents
Andrew de los Reyesef017552010-10-06 17:57:52 -0700875 vector<uint64_t> remove_extents_expanded =
876 ExpandExtents(remove_extents);
877 vector<uint64_t> replace_extents_expanded =
878 ExpandExtents(replace_extents);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700879 CHECK_EQ(remove_extents_expanded.size(), replace_extents_expanded.size());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700880 map<uint64_t, uint64_t> conversion;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700881 for (vector<uint64_t>::size_type i = 0;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700882 i < replace_extents_expanded.size(); i++) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700883 conversion[remove_extents_expanded[i]] = replace_extents_expanded[i];
884 }
885 utils::ApplyMap(&read_blocks, conversion);
886 for (Vertex::EdgeMap::iterator it = vertex->out_edges.begin(),
887 e = vertex->out_edges.end(); it != e; ++it) {
888 vector<uint64_t> write_before_deps_expanded =
889 ExpandExtents(it->second.write_extents);
890 utils::ApplyMap(&write_before_deps_expanded, conversion);
891 it->second.write_extents = CompressExtents(write_before_deps_expanded);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700892 }
893 }
894 // Convert read_blocks back to extents
Andrew de los Reyesef017552010-10-06 17:57:52 -0700895 vertex->op.clear_src_extents();
896 vector<Extent> new_extents = CompressExtents(read_blocks);
897 DeltaDiffGenerator::StoreExtents(new_extents,
898 vertex->op.mutable_src_extents());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700899}
900
901bool DeltaDiffGenerator::CutEdges(Graph* graph,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700902 const set<Edge>& edges,
903 vector<CutEdgeVertexes>* out_cuts) {
904 DummyExtentAllocator scratch_allocator;
905 vector<CutEdgeVertexes> cuts;
906 cuts.reserve(edges.size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700907
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700908 uint64_t scratch_blocks_used = 0;
909 for (set<Edge>::const_iterator it = edges.begin();
910 it != edges.end(); ++it) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700911 cuts.resize(cuts.size() + 1);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700912 vector<Extent> old_extents =
913 (*graph)[it->first].out_edges[it->second].extents;
914 // Choose some scratch space
915 scratch_blocks_used += graph_utils::EdgeWeight(*graph, *it);
Andrew de los Reyesef017552010-10-06 17:57:52 -0700916 cuts.back().tmp_extents =
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700917 scratch_allocator.Allocate(graph_utils::EdgeWeight(*graph, *it));
918 // create vertex to copy original->scratch
Andrew de los Reyesef017552010-10-06 17:57:52 -0700919 cuts.back().new_vertex = graph->size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700920 graph->resize(graph->size() + 1);
Andrew de los Reyesef017552010-10-06 17:57:52 -0700921 cuts.back().old_src = it->first;
922 cuts.back().old_dst = it->second;
Darin Petkov36a58222010-10-07 22:00:09 -0700923
Andrew de los Reyesef017552010-10-06 17:57:52 -0700924 EdgeProperties& cut_edge_properties =
925 (*graph)[it->first].out_edges.find(it->second)->second;
926
927 // This should never happen, as we should only be cutting edges between
928 // real file nodes, and write-before relationships are created from
929 // a real file node to a temp copy node:
930 CHECK(cut_edge_properties.write_extents.empty())
931 << "Can't cut edge that has write-before relationship.";
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700932
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700933 // make node depend on the copy operation
934 (*graph)[it->first].out_edges.insert(make_pair(graph->size() - 1,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700935 cut_edge_properties));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700936
937 // Set src/dst extents and other proto variables for copy operation
938 graph->back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
939 DeltaDiffGenerator::StoreExtents(
Andrew de los Reyesef017552010-10-06 17:57:52 -0700940 cut_edge_properties.extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700941 graph->back().op.mutable_src_extents());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700942 DeltaDiffGenerator::StoreExtents(cuts.back().tmp_extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700943 graph->back().op.mutable_dst_extents());
944 graph->back().op.set_src_length(
945 graph_utils::EdgeWeight(*graph, *it) * kBlockSize);
946 graph->back().op.set_dst_length(graph->back().op.src_length());
947
948 // make the dest node read from the scratch space
949 DeltaDiffGenerator::SubstituteBlocks(
Andrew de los Reyesef017552010-10-06 17:57:52 -0700950 &((*graph)[it->second]),
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700951 (*graph)[it->first].out_edges[it->second].extents,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700952 cuts.back().tmp_extents);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700953
954 // delete the old edge
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500955 CHECK_EQ(static_cast<Graph::size_type>(1),
956 (*graph)[it->first].out_edges.erase(it->second));
Chris Masone790e62e2010-08-12 10:41:18 -0700957
Andrew de los Reyesd12784c2010-07-26 13:55:14 -0700958 // Add an edge from dst to copy operation
Andrew de los Reyesef017552010-10-06 17:57:52 -0700959 EdgeProperties write_before_edge_properties;
960 write_before_edge_properties.write_extents = cuts.back().tmp_extents;
961 (*graph)[it->second].out_edges.insert(
962 make_pair(graph->size() - 1, write_before_edge_properties));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700963 }
Andrew de los Reyesef017552010-10-06 17:57:52 -0700964 out_cuts->swap(cuts);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700965 return true;
966}
967
968// Stores all Extents in 'extents' into 'out'.
969void DeltaDiffGenerator::StoreExtents(
Andrew de los Reyesef017552010-10-06 17:57:52 -0700970 const vector<Extent>& extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700971 google::protobuf::RepeatedPtrField<Extent>* out) {
972 for (vector<Extent>::const_iterator it = extents.begin();
973 it != extents.end(); ++it) {
974 Extent* new_extent = out->Add();
975 *new_extent = *it;
976 }
977}
978
979// Creates all the edges for the graph. Writers of a block point to
980// readers of the same block. This is because for an edge A->B, B
981// must complete before A executes.
982void DeltaDiffGenerator::CreateEdges(Graph* graph,
983 const vector<Block>& blocks) {
984 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
985 // Blocks with both a reader and writer get an edge
986 if (blocks[i].reader == Vertex::kInvalidIndex ||
987 blocks[i].writer == Vertex::kInvalidIndex)
988 continue;
989 // Don't have a node depend on itself
990 if (blocks[i].reader == blocks[i].writer)
991 continue;
992 // See if there's already an edge we can add onto
993 Vertex::EdgeMap::iterator edge_it =
994 (*graph)[blocks[i].writer].out_edges.find(blocks[i].reader);
995 if (edge_it == (*graph)[blocks[i].writer].out_edges.end()) {
996 // No existing edge. Create one
997 (*graph)[blocks[i].writer].out_edges.insert(
998 make_pair(blocks[i].reader, EdgeProperties()));
999 edge_it = (*graph)[blocks[i].writer].out_edges.find(blocks[i].reader);
Chris Masone790e62e2010-08-12 10:41:18 -07001000 CHECK(edge_it != (*graph)[blocks[i].writer].out_edges.end());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001001 }
1002 graph_utils::AppendBlockToExtents(&edge_it->second.extents, i);
1003 }
1004}
1005
Andrew de los Reyesef017552010-10-06 17:57:52 -07001006namespace {
1007
1008class SortCutsByTopoOrderLess {
1009 public:
1010 SortCutsByTopoOrderLess(vector<vector<Vertex::Index>::size_type>& table)
1011 : table_(table) {}
1012 bool operator()(const CutEdgeVertexes& a, const CutEdgeVertexes& b) {
1013 return table_[a.old_dst] < table_[b.old_dst];
1014 }
1015 private:
1016 vector<vector<Vertex::Index>::size_type>& table_;
1017};
1018
1019} // namespace {}
1020
1021void DeltaDiffGenerator::GenerateReverseTopoOrderMap(
1022 vector<Vertex::Index>& op_indexes,
1023 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes) {
1024 vector<vector<Vertex::Index>::size_type> table(op_indexes.size());
1025 for (vector<Vertex::Index>::size_type i = 0, e = op_indexes.size();
1026 i != e; ++i) {
1027 Vertex::Index node = op_indexes[i];
1028 if (table.size() < (node + 1)) {
1029 table.resize(node + 1);
1030 }
1031 table[node] = i;
1032 }
1033 reverse_op_indexes->swap(table);
1034}
1035
1036void DeltaDiffGenerator::SortCutsByTopoOrder(vector<Vertex::Index>& op_indexes,
1037 vector<CutEdgeVertexes>* cuts) {
1038 // first, make a reverse lookup table.
1039 vector<vector<Vertex::Index>::size_type> table;
1040 GenerateReverseTopoOrderMap(op_indexes, &table);
1041 SortCutsByTopoOrderLess less(table);
1042 sort(cuts->begin(), cuts->end(), less);
1043}
1044
1045void DeltaDiffGenerator::MoveFullOpsToBack(Graph* graph,
1046 vector<Vertex::Index>* op_indexes) {
1047 vector<Vertex::Index> ret;
1048 vector<Vertex::Index> full_ops;
1049 ret.reserve(op_indexes->size());
1050 for (vector<Vertex::Index>::size_type i = 0, e = op_indexes->size(); i != e;
1051 ++i) {
1052 DeltaArchiveManifest_InstallOperation_Type type =
1053 (*graph)[(*op_indexes)[i]].op.type();
1054 if (type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
1055 type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
1056 full_ops.push_back((*op_indexes)[i]);
1057 } else {
1058 ret.push_back((*op_indexes)[i]);
1059 }
1060 }
1061 LOG(INFO) << "Stats: " << full_ops.size() << " full ops out of "
1062 << (full_ops.size() + ret.size()) << " total ops.";
1063 ret.insert(ret.end(), full_ops.begin(), full_ops.end());
1064 op_indexes->swap(ret);
1065}
1066
1067namespace {
1068
1069template<typename T>
1070bool TempBlocksExistInExtents(const T& extents) {
1071 for (int i = 0, e = extents.size(); i < e; ++i) {
1072 Extent extent = graph_utils::GetElement(extents, i);
1073 uint64_t start = extent.start_block();
1074 uint64_t num = extent.num_blocks();
1075 if (start == kSparseHole)
1076 continue;
1077 if (start >= kTempBlockStart ||
1078 (start + num) >= kTempBlockStart) {
1079 LOG(ERROR) << "temp block!";
1080 LOG(ERROR) << "start: " << start << ", num: " << num;
1081 LOG(ERROR) << "kTempBlockStart: " << kTempBlockStart;
1082 LOG(ERROR) << "returning true";
1083 return true;
1084 }
1085 // check for wrap-around, which would be a bug:
1086 CHECK(start <= (start + num));
1087 }
1088 return false;
1089}
1090
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001091// Convertes the cuts, which must all have the same |old_dst| member,
1092// to full. It does this by converting the |old_dst| to REPLACE or
1093// REPLACE_BZ, dropping all incoming edges to |old_dst|, and marking
1094// all temp nodes invalid.
1095bool ConvertCutsToFull(
1096 Graph* graph,
1097 const string& new_root,
1098 int data_fd,
1099 off_t* data_file_size,
1100 vector<Vertex::Index>* op_indexes,
1101 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
1102 const vector<CutEdgeVertexes>& cuts) {
1103 CHECK(!cuts.empty());
1104 set<Vertex::Index> deleted_nodes;
1105 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
1106 e = cuts.end(); it != e; ++it) {
1107 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ConvertCutToFullOp(
1108 graph,
1109 *it,
1110 new_root,
1111 data_fd,
1112 data_file_size));
1113 deleted_nodes.insert(it->new_vertex);
1114 }
1115 deleted_nodes.insert(cuts[0].old_dst);
Darin Petkovbc58a7b2010-11-03 11:52:53 -07001116
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001117 vector<Vertex::Index> new_op_indexes;
1118 new_op_indexes.reserve(op_indexes->size());
1119 for (vector<Vertex::Index>::iterator it = op_indexes->begin(),
1120 e = op_indexes->end(); it != e; ++it) {
1121 if (utils::SetContainsKey(deleted_nodes, *it))
1122 continue;
1123 new_op_indexes.push_back(*it);
1124 }
1125 new_op_indexes.push_back(cuts[0].old_dst);
1126 op_indexes->swap(new_op_indexes);
1127 DeltaDiffGenerator::GenerateReverseTopoOrderMap(*op_indexes,
1128 reverse_op_indexes);
1129 return true;
1130}
1131
1132// Tries to assign temp blocks for a collection of cuts, all of which share
1133// the same old_dst member. If temp blocks can't be found, old_dst will be
1134// converted to a REPLACE or REPLACE_BZ operation. Returns true on success,
1135// which can happen even if blocks are converted to full. Returns false
1136// on exceptional error cases.
1137bool AssignBlockForAdjoiningCuts(
1138 Graph* graph,
1139 const string& new_root,
1140 int data_fd,
1141 off_t* data_file_size,
1142 vector<Vertex::Index>* op_indexes,
1143 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
1144 const vector<CutEdgeVertexes>& cuts) {
1145 CHECK(!cuts.empty());
1146 const Vertex::Index old_dst = cuts[0].old_dst;
1147 // Calculate # of blocks needed
1148 uint64_t blocks_needed = 0;
1149 map<const CutEdgeVertexes*, uint64_t> cuts_blocks_needed;
1150 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
1151 e = cuts.end(); it != e; ++it) {
1152 uint64_t cut_blocks_needed = 0;
1153 for (vector<Extent>::const_iterator jt = it->tmp_extents.begin(),
1154 je = it->tmp_extents.end(); jt != je; ++jt) {
1155 cut_blocks_needed += jt->num_blocks();
1156 }
1157 blocks_needed += cut_blocks_needed;
1158 cuts_blocks_needed[&*it] = cut_blocks_needed;
1159 }
Darin Petkovbc58a7b2010-11-03 11:52:53 -07001160
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001161 // Find enough blocks
1162 ExtentRanges scratch_ranges;
1163 // Each block that's supplying temp blocks and the corresponding blocks:
1164 typedef vector<pair<Vertex::Index, ExtentRanges> > SupplierVector;
1165 SupplierVector block_suppliers;
1166 uint64_t scratch_blocks_found = 0;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001167 for (vector<Vertex::Index>::size_type i = (*reverse_op_indexes)[old_dst] + 1,
1168 e = op_indexes->size(); i < e; ++i) {
1169 Vertex::Index test_node = (*op_indexes)[i];
1170 if (!(*graph)[test_node].valid)
1171 continue;
1172 // See if this node has sufficient blocks
1173 ExtentRanges ranges;
1174 ranges.AddRepeatedExtents((*graph)[test_node].op.dst_extents());
1175 ranges.SubtractExtent(ExtentForRange(
1176 kTempBlockStart, kSparseHole - kTempBlockStart));
1177 ranges.SubtractRepeatedExtents((*graph)[test_node].op.src_extents());
1178 // For now, for simplicity, subtract out all blocks in read-before
1179 // dependencies.
1180 for (Vertex::EdgeMap::const_iterator edge_i =
1181 (*graph)[test_node].out_edges.begin(),
1182 edge_e = (*graph)[test_node].out_edges.end();
1183 edge_i != edge_e; ++edge_i) {
1184 ranges.SubtractExtents(edge_i->second.extents);
1185 }
1186 if (ranges.blocks() == 0)
1187 continue;
1188
1189 if (ranges.blocks() + scratch_blocks_found > blocks_needed) {
1190 // trim down ranges
1191 vector<Extent> new_ranges = ranges.GetExtentsForBlockCount(
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001192 blocks_needed - scratch_blocks_found);
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001193 ranges = ExtentRanges();
1194 ranges.AddExtents(new_ranges);
1195 }
1196 scratch_ranges.AddRanges(ranges);
1197 block_suppliers.push_back(make_pair(test_node, ranges));
1198 scratch_blocks_found += ranges.blocks();
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001199 if (scratch_ranges.blocks() >= blocks_needed)
1200 break;
1201 }
1202 if (scratch_ranges.blocks() < blocks_needed) {
1203 LOG(INFO) << "Unable to find sufficient scratch";
1204 TEST_AND_RETURN_FALSE(ConvertCutsToFull(graph,
1205 new_root,
1206 data_fd,
1207 data_file_size,
1208 op_indexes,
1209 reverse_op_indexes,
1210 cuts));
1211 return true;
1212 }
1213 // Use the scratch we found
1214 TEST_AND_RETURN_FALSE(scratch_ranges.blocks() == scratch_blocks_found);
1215
1216 // Make all the suppliers depend on this node
1217 for (SupplierVector::iterator it = block_suppliers.begin(),
1218 e = block_suppliers.end(); it != e; ++it) {
1219 graph_utils::AddReadBeforeDepExtents(
1220 &(*graph)[it->first],
1221 old_dst,
1222 it->second.GetExtentsForBlockCount(it->second.blocks()));
1223 }
Darin Petkovbc58a7b2010-11-03 11:52:53 -07001224
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001225 // Replace temp blocks in each cut
1226 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
1227 e = cuts.end(); it != e; ++it) {
1228 vector<Extent> real_extents =
1229 scratch_ranges.GetExtentsForBlockCount(cuts_blocks_needed[&*it]);
1230 scratch_ranges.SubtractExtents(real_extents);
1231
1232 // Fix the old dest node w/ the real blocks
1233 DeltaDiffGenerator::SubstituteBlocks(&(*graph)[old_dst],
1234 it->tmp_extents,
1235 real_extents);
1236
1237 // Fix the new node w/ the real blocks. Since the new node is just a
1238 // copy operation, we can replace all the dest extents w/ the real
1239 // blocks.
1240 DeltaArchiveManifest_InstallOperation *op =
1241 &(*graph)[it->new_vertex].op;
1242 op->clear_dst_extents();
1243 DeltaDiffGenerator::StoreExtents(real_extents, op->mutable_dst_extents());
1244 }
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001245 return true;
1246}
1247
Andrew de los Reyesef017552010-10-06 17:57:52 -07001248} // namespace {}
1249
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001250// Returns true if |op| is a no-op operation that doesn't do any useful work
1251// (e.g., a move operation that copies blocks onto themselves).
1252bool DeltaDiffGenerator::IsNoopOperation(
1253 const DeltaArchiveManifest_InstallOperation& op) {
1254 return (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE &&
1255 ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
1256}
1257
Andrew de los Reyesef017552010-10-06 17:57:52 -07001258bool DeltaDiffGenerator::AssignTempBlocks(
1259 Graph* graph,
1260 const string& new_root,
1261 int data_fd,
1262 off_t* data_file_size,
1263 vector<Vertex::Index>* op_indexes,
1264 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001265 const vector<CutEdgeVertexes>& cuts) {
Andrew de los Reyesef017552010-10-06 17:57:52 -07001266 CHECK(!cuts.empty());
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001267
1268 // group of cuts w/ the same old_dst:
1269 vector<CutEdgeVertexes> cuts_group;
1270
Andrew de los Reyesef017552010-10-06 17:57:52 -07001271 for (vector<CutEdgeVertexes>::size_type i = cuts.size() - 1, e = 0;
1272 true ; --i) {
1273 LOG(INFO) << "Fixing temp blocks in cut " << i
1274 << ": old dst: " << cuts[i].old_dst << " new vertex: "
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001275 << cuts[i].new_vertex << " path: "
1276 << (*graph)[cuts[i].old_dst].file_name;
1277
1278 if (cuts_group.empty() || (cuts_group[0].old_dst == cuts[i].old_dst)) {
1279 cuts_group.push_back(cuts[i]);
1280 } else {
1281 CHECK(!cuts_group.empty());
1282 TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
1283 new_root,
1284 data_fd,
1285 data_file_size,
1286 op_indexes,
1287 reverse_op_indexes,
1288 cuts_group));
1289 cuts_group.clear();
1290 cuts_group.push_back(cuts[i]);
Andrew de los Reyesef017552010-10-06 17:57:52 -07001291 }
Darin Petkov36a58222010-10-07 22:00:09 -07001292
Andrew de los Reyesef017552010-10-06 17:57:52 -07001293 if (i == e) {
1294 // break out of for() loop
1295 break;
1296 }
1297 }
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001298 CHECK(!cuts_group.empty());
1299 TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
1300 new_root,
1301 data_fd,
1302 data_file_size,
1303 op_indexes,
1304 reverse_op_indexes,
1305 cuts_group));
Andrew de los Reyesef017552010-10-06 17:57:52 -07001306 return true;
1307}
1308
1309bool DeltaDiffGenerator::NoTempBlocksRemain(const Graph& graph) {
1310 size_t idx = 0;
1311 for (Graph::const_iterator it = graph.begin(), e = graph.end(); it != e;
1312 ++it, ++idx) {
1313 if (!it->valid)
1314 continue;
1315 const DeltaArchiveManifest_InstallOperation& op = it->op;
1316 if (TempBlocksExistInExtents(op.dst_extents()) ||
1317 TempBlocksExistInExtents(op.src_extents())) {
1318 LOG(INFO) << "bad extents in node " << idx;
1319 LOG(INFO) << "so yeah";
1320 return false;
1321 }
1322
1323 // Check out-edges:
1324 for (Vertex::EdgeMap::const_iterator jt = it->out_edges.begin(),
1325 je = it->out_edges.end(); jt != je; ++jt) {
1326 if (TempBlocksExistInExtents(jt->second.extents) ||
1327 TempBlocksExistInExtents(jt->second.write_extents)) {
1328 LOG(INFO) << "bad out edge in node " << idx;
1329 LOG(INFO) << "so yeah";
1330 return false;
1331 }
1332 }
1333 }
1334 return true;
1335}
1336
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001337bool DeltaDiffGenerator::ReorderDataBlobs(
1338 DeltaArchiveManifest* manifest,
1339 const std::string& data_blobs_path,
1340 const std::string& new_data_blobs_path) {
1341 int in_fd = open(data_blobs_path.c_str(), O_RDONLY, 0);
1342 TEST_AND_RETURN_FALSE_ERRNO(in_fd >= 0);
1343 ScopedFdCloser in_fd_closer(&in_fd);
Chris Masone790e62e2010-08-12 10:41:18 -07001344
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001345 DirectFileWriter writer;
1346 TEST_AND_RETURN_FALSE(
1347 writer.Open(new_data_blobs_path.c_str(),
1348 O_WRONLY | O_TRUNC | O_CREAT,
1349 0644) == 0);
1350 ScopedFileWriterCloser writer_closer(&writer);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001351 uint64_t out_file_size = 0;
Chris Masone790e62e2010-08-12 10:41:18 -07001352
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001353 for (int i = 0; i < (manifest->install_operations_size() +
1354 manifest->kernel_install_operations_size()); i++) {
1355 DeltaArchiveManifest_InstallOperation* op = NULL;
1356 if (i < manifest->install_operations_size()) {
1357 op = manifest->mutable_install_operations(i);
1358 } else {
1359 op = manifest->mutable_kernel_install_operations(
1360 i - manifest->install_operations_size());
1361 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001362 if (!op->has_data_offset())
1363 continue;
1364 CHECK(op->has_data_length());
1365 vector<char> buf(op->data_length());
1366 ssize_t rc = pread(in_fd, &buf[0], buf.size(), op->data_offset());
1367 TEST_AND_RETURN_FALSE(rc == static_cast<ssize_t>(buf.size()));
1368
Jay Srinivasan00f76b62012-09-17 18:48:36 -07001369 // Add the hash of the data blobs for this operation
1370 TEST_AND_RETURN_FALSE(AddOperationHash(op, buf));
1371
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001372 op->set_data_offset(out_file_size);
Don Garrette410e0f2011-11-10 15:39:01 -08001373 TEST_AND_RETURN_FALSE(writer.Write(&buf[0], buf.size()));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001374 out_file_size += buf.size();
1375 }
1376 return true;
1377}
1378
Jay Srinivasan00f76b62012-09-17 18:48:36 -07001379bool DeltaDiffGenerator::AddOperationHash(
1380 DeltaArchiveManifest_InstallOperation* op,
1381 const vector<char>& buf) {
1382 OmahaHashCalculator hasher;
1383
1384 TEST_AND_RETURN_FALSE(hasher.Update(&buf[0], buf.size()));
1385 TEST_AND_RETURN_FALSE(hasher.Finalize());
1386
1387 const vector<char>& hash = hasher.raw_hash();
1388 op->set_data_sha256_hash(hash.data(), hash.size());
1389 return true;
1390}
1391
Andrew de los Reyesef017552010-10-06 17:57:52 -07001392bool DeltaDiffGenerator::ConvertCutToFullOp(Graph* graph,
1393 const CutEdgeVertexes& cut,
1394 const string& new_root,
1395 int data_fd,
1396 off_t* data_file_size) {
1397 // Drop all incoming edges, keep all outgoing edges
Darin Petkov36a58222010-10-07 22:00:09 -07001398
Andrew de los Reyesef017552010-10-06 17:57:52 -07001399 // Keep all outgoing edges
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001400 if ((*graph)[cut.old_dst].op.type() !=
1401 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ &&
1402 (*graph)[cut.old_dst].op.type() !=
1403 DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
1404 Vertex::EdgeMap out_edges = (*graph)[cut.old_dst].out_edges;
1405 graph_utils::DropWriteBeforeDeps(&out_edges);
Darin Petkov36a58222010-10-07 22:00:09 -07001406
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001407 TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
1408 cut.old_dst,
1409 NULL,
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -08001410 kNonexistentPath,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001411 new_root,
1412 (*graph)[cut.old_dst].file_name,
Darin Petkov8e447e02013-04-16 16:23:50 +02001413 (*graph)[cut.old_dst].chunk_offset,
1414 (*graph)[cut.old_dst].chunk_size,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001415 data_fd,
1416 data_file_size));
Darin Petkov36a58222010-10-07 22:00:09 -07001417
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001418 (*graph)[cut.old_dst].out_edges = out_edges;
Andrew de los Reyesef017552010-10-06 17:57:52 -07001419
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001420 // Right now we don't have doubly-linked edges, so we have to scan
1421 // the whole graph.
1422 graph_utils::DropIncomingEdgesTo(graph, cut.old_dst);
1423 }
Andrew de los Reyesef017552010-10-06 17:57:52 -07001424
1425 // Delete temp node
1426 (*graph)[cut.old_src].out_edges.erase(cut.new_vertex);
1427 CHECK((*graph)[cut.old_dst].out_edges.find(cut.new_vertex) ==
1428 (*graph)[cut.old_dst].out_edges.end());
1429 (*graph)[cut.new_vertex].valid = false;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001430 LOG(INFO) << "marked node invalid: " << cut.new_vertex;
Andrew de los Reyesef017552010-10-06 17:57:52 -07001431 return true;
1432}
1433
1434bool DeltaDiffGenerator::ConvertGraphToDag(Graph* graph,
1435 const string& new_root,
1436 int fd,
1437 off_t* data_file_size,
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001438 vector<Vertex::Index>* final_order,
1439 Vertex::Index scratch_vertex) {
Andrew de los Reyesef017552010-10-06 17:57:52 -07001440 CycleBreaker cycle_breaker;
1441 LOG(INFO) << "Finding cycles...";
1442 set<Edge> cut_edges;
1443 cycle_breaker.BreakCycles(*graph, &cut_edges);
1444 LOG(INFO) << "done finding cycles";
1445 CheckGraph(*graph);
1446
1447 // Calculate number of scratch blocks needed
1448
1449 LOG(INFO) << "Cutting cycles...";
1450 vector<CutEdgeVertexes> cuts;
1451 TEST_AND_RETURN_FALSE(CutEdges(graph, cut_edges, &cuts));
1452 LOG(INFO) << "done cutting cycles";
1453 LOG(INFO) << "There are " << cuts.size() << " cuts.";
1454 CheckGraph(*graph);
1455
1456 LOG(INFO) << "Creating initial topological order...";
1457 TopologicalSort(*graph, final_order);
1458 LOG(INFO) << "done with initial topo order";
1459 CheckGraph(*graph);
1460
1461 LOG(INFO) << "Moving full ops to the back";
1462 MoveFullOpsToBack(graph, final_order);
1463 LOG(INFO) << "done moving full ops to back";
1464
1465 vector<vector<Vertex::Index>::size_type> inverse_final_order;
1466 GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
1467
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001468 SortCutsByTopoOrder(*final_order, &cuts);
1469
Andrew de los Reyesef017552010-10-06 17:57:52 -07001470 if (!cuts.empty())
1471 TEST_AND_RETURN_FALSE(AssignTempBlocks(graph,
1472 new_root,
1473 fd,
1474 data_file_size,
1475 final_order,
1476 &inverse_final_order,
1477 cuts));
1478 LOG(INFO) << "Making sure all temp blocks have been allocated";
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001479
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001480 // Remove the scratch node, if any
1481 if (scratch_vertex != Vertex::kInvalidIndex) {
1482 final_order->erase(final_order->begin() +
1483 inverse_final_order[scratch_vertex]);
1484 (*graph)[scratch_vertex].valid = false;
1485 GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
1486 }
1487
Andrew de los Reyesef017552010-10-06 17:57:52 -07001488 graph_utils::DumpGraph(*graph);
1489 CHECK(NoTempBlocksRemain(*graph));
1490 LOG(INFO) << "done making sure all temp blocks are allocated";
1491 return true;
1492}
1493
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001494void DeltaDiffGenerator::CreateScratchNode(uint64_t start_block,
1495 uint64_t num_blocks,
1496 Vertex* vertex) {
1497 vertex->file_name = "<scratch>";
1498 vertex->op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
1499 vertex->op.set_data_offset(0);
1500 vertex->op.set_data_length(0);
1501 Extent* extent = vertex->op.add_dst_extents();
1502 extent->set_start_block(start_block);
1503 extent->set_num_blocks(num_blocks);
1504}
1505
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001506bool DeltaDiffGenerator::GenerateDeltaUpdateFile(
1507 const string& old_root,
1508 const string& old_image,
1509 const string& new_root,
1510 const string& new_image,
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001511 const string& old_kernel_part,
1512 const string& new_kernel_part,
1513 const string& output_path,
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001514 const string& private_key_path,
Darin Petkov8e447e02013-04-16 16:23:50 +02001515 off_t chunk_size,
Chris Sosad5ae1562013-04-23 13:20:18 -07001516 size_t rootfs_partition_size,
Don Garrett0dd39852013-04-03 16:55:42 -07001517 const ImageInfo* old_image_info,
1518 const ImageInfo* new_image_info,
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001519 uint64_t* metadata_size) {
Darin Petkov8e447e02013-04-16 16:23:50 +02001520 TEST_AND_RETURN_FALSE(chunk_size == -1 || chunk_size % kBlockSize == 0);
Darin Petkov7ea32332010-10-13 10:46:11 -07001521 int old_image_block_count = 0, old_image_block_size = 0;
1522 int new_image_block_count = 0, new_image_block_size = 0;
1523 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(new_image,
1524 &new_image_block_count,
1525 &new_image_block_size));
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001526 if (!old_image.empty()) {
Darin Petkov7ea32332010-10-13 10:46:11 -07001527 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(old_image,
1528 &old_image_block_count,
1529 &old_image_block_size));
1530 TEST_AND_RETURN_FALSE(old_image_block_size == new_image_block_size);
1531 LOG_IF(WARNING, old_image_block_count != new_image_block_count)
1532 << "Old and new images have different block counts.";
Don Garrett0dd39852013-04-03 16:55:42 -07001533
Don Garrett60fc59c2013-10-18 11:43:52 -07001534 // If new_image_info is present, old_image_info must be present.
Don Garrett0dd39852013-04-03 16:55:42 -07001535 TEST_AND_RETURN_FALSE((bool)old_image_info == (bool)new_image_info);
1536 } else {
1537 // old_image_info must not be present for a full update.
1538 TEST_AND_RETURN_FALSE(!old_image_info);
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001539 }
Chris Sosad5ae1562013-04-23 13:20:18 -07001540
1541 // Sanity checks for the partition size.
1542 TEST_AND_RETURN_FALSE(rootfs_partition_size % kBlockSize == 0);
Chris Sosae9f5f422013-05-17 16:11:10 -07001543 size_t fs_size = static_cast<size_t>(new_image_block_size) *
1544 new_image_block_count;
Chris Sosad5ae1562013-04-23 13:20:18 -07001545 LOG(INFO) << "Rootfs partition size: " << rootfs_partition_size;
1546 LOG(INFO) << "Actual filesystem size: " << fs_size;
1547 TEST_AND_RETURN_FALSE(rootfs_partition_size >= fs_size);
1548
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001549 // Sanity check kernel partition arg
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001550 TEST_AND_RETURN_FALSE(utils::FileSize(new_kernel_part) >= 0);
1551
Darin Petkov7ea32332010-10-13 10:46:11 -07001552 vector<Block> blocks(max(old_image_block_count, new_image_block_count));
1553 LOG(INFO) << "Invalid block index: " << Vertex::kInvalidIndex;
1554 LOG(INFO) << "Block count: " << blocks.size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001555 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
1556 CHECK(blocks[i].reader == Vertex::kInvalidIndex);
1557 CHECK(blocks[i].writer == Vertex::kInvalidIndex);
1558 }
1559 Graph graph;
1560 CheckGraph(graph);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001561
Gilad Arnolda6742b32014-01-11 00:18:34 -08001562 const string kTempFileTemplate("CrAU_temp_data.XXXXXX");
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001563 string temp_file_path;
Darin Petkov7438a5c2011-08-29 11:56:44 -07001564 scoped_ptr<ScopedPathUnlinker> temp_file_unlinker;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001565 off_t data_file_size = 0;
1566
1567 LOG(INFO) << "Reading files...";
1568
Don Garrettb8dd1d92013-11-22 17:40:02 -08001569 // Create empty protobuf Manifest object
1570 DeltaArchiveManifest manifest;
1571
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001572 vector<DeltaArchiveManifest_InstallOperation> kernel_ops;
1573
Andrew de los Reyesef017552010-10-06 17:57:52 -07001574 vector<Vertex::Index> final_order;
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001575 Vertex::Index scratch_vertex = Vertex::kInvalidIndex;
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001576 {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001577 int fd;
1578 TEST_AND_RETURN_FALSE(
1579 utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &fd));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001580 temp_file_unlinker.reset(new ScopedPathUnlinker(temp_file_path));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001581 TEST_AND_RETURN_FALSE(fd >= 0);
1582 ScopedFdCloser fd_closer(&fd);
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001583 if (!old_image.empty()) {
1584 // Delta update
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001585
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001586 TEST_AND_RETURN_FALSE(DeltaReadFiles(&graph,
1587 &blocks,
1588 old_root,
1589 new_root,
Darin Petkov8e447e02013-04-16 16:23:50 +02001590 chunk_size,
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001591 fd,
1592 &data_file_size));
1593 LOG(INFO) << "done reading normal files";
1594 CheckGraph(graph);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001595
Thieu Le5c7d9752010-12-15 16:09:28 -08001596 LOG(INFO) << "Starting metadata processing";
1597 TEST_AND_RETURN_FALSE(Metadata::DeltaReadMetadata(&graph,
1598 &blocks,
1599 old_image,
1600 new_image,
1601 fd,
1602 &data_file_size));
1603 LOG(INFO) << "Done metadata processing";
1604 CheckGraph(graph);
1605
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001606 graph.resize(graph.size() + 1);
1607 TEST_AND_RETURN_FALSE(ReadUnwrittenBlocks(blocks,
1608 fd,
1609 &data_file_size,
1610 new_image,
1611 &graph.back()));
1612
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001613 // Final scratch block (if there's space)
Chris Sosad5ae1562013-04-23 13:20:18 -07001614 if (blocks.size() < (rootfs_partition_size / kBlockSize)) {
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001615 scratch_vertex = graph.size();
1616 graph.resize(graph.size() + 1);
1617 CreateScratchNode(blocks.size(),
Chris Sosad5ae1562013-04-23 13:20:18 -07001618 (rootfs_partition_size / kBlockSize) - blocks.size(),
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001619 &graph.back());
1620 }
1621
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001622 // Read kernel partition
1623 TEST_AND_RETURN_FALSE(DeltaCompressKernelPartition(old_kernel_part,
1624 new_kernel_part,
1625 &kernel_ops,
1626 fd,
1627 &data_file_size));
1628
1629 LOG(INFO) << "done reading kernel";
1630 CheckGraph(graph);
1631
1632 LOG(INFO) << "Creating edges...";
1633 CreateEdges(&graph, blocks);
1634 LOG(INFO) << "Done creating edges";
1635 CheckGraph(graph);
1636
1637 TEST_AND_RETURN_FALSE(ConvertGraphToDag(&graph,
1638 new_root,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001639 fd,
1640 &data_file_size,
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001641 &final_order,
1642 scratch_vertex));
Don Garrettb8dd1d92013-11-22 17:40:02 -08001643
1644 // Set the minor version for this payload.
1645 LOG(INFO) << "Adding Delta Minor Version.";
1646 manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001647 } else {
1648 // Full update
Darin Petkov7ea32332010-10-13 10:46:11 -07001649 off_t new_image_size =
1650 static_cast<off_t>(new_image_block_count) * new_image_block_size;
Darin Petkov7a22d792010-11-08 14:10:00 -08001651 TEST_AND_RETURN_FALSE(FullUpdateGenerator::Run(&graph,
1652 new_kernel_part,
1653 new_image,
1654 new_image_size,
1655 fd,
1656 &data_file_size,
1657 kFullUpdateChunkSize,
1658 kBlockSize,
1659 &kernel_ops,
1660 &final_order));
Don Garrettb8dd1d92013-11-22 17:40:02 -08001661
1662 // Set the minor version for this payload.
1663 LOG(INFO) << "Adding Full Minor Version.";
1664 manifest.set_minor_version(DeltaPerformer::kFullPayloadMinorVersion);
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001665 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001666 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001667
Don Garrett0dd39852013-04-03 16:55:42 -07001668 if (old_image_info)
1669 *(manifest.mutable_old_image_info()) = *old_image_info;
1670
1671 if (new_image_info)
1672 *(manifest.mutable_new_image_info()) = *new_image_info;
1673
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001674 OperationNameMap op_name_map;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001675 CheckGraph(graph);
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001676 InstallOperationsToManifest(graph,
1677 final_order,
1678 kernel_ops,
1679 &manifest,
1680 &op_name_map);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001681 CheckGraph(graph);
1682 manifest.set_block_size(kBlockSize);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001683
1684 // Reorder the data blobs with the newly ordered manifest
1685 string ordered_blobs_path;
1686 TEST_AND_RETURN_FALSE(utils::MakeTempFile(
Gilad Arnolda6742b32014-01-11 00:18:34 -08001687 "CrAU_temp_data.ordered.XXXXXX",
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001688 &ordered_blobs_path,
Andrew de los Reyese05fc282011-06-02 09:50:08 -07001689 NULL));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001690 ScopedPathUnlinker ordered_blobs_unlinker(ordered_blobs_path);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001691 TEST_AND_RETURN_FALSE(ReorderDataBlobs(&manifest,
1692 temp_file_path,
1693 ordered_blobs_path));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001694 temp_file_unlinker.reset();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001695
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001696 // Check that install op blobs are in order.
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001697 uint64_t next_blob_offset = 0;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001698 {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001699 for (int i = 0; i < (manifest.install_operations_size() +
1700 manifest.kernel_install_operations_size()); i++) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001701 DeltaArchiveManifest_InstallOperation* op =
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001702 i < manifest.install_operations_size() ?
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001703 manifest.mutable_install_operations(i) :
1704 manifest.mutable_kernel_install_operations(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001705 i - manifest.install_operations_size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001706 if (op->has_data_offset()) {
1707 if (op->data_offset() != next_blob_offset) {
1708 LOG(FATAL) << "bad blob offset! " << op->data_offset() << " != "
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001709 << next_blob_offset;
1710 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001711 next_blob_offset += op->data_length();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001712 }
1713 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001714 }
1715
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001716 // Signatures appear at the end of the blobs. Note the offset in the
1717 // manifest
1718 if (!private_key_path.empty()) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001719 uint64_t signature_blob_length = 0;
1720 TEST_AND_RETURN_FALSE(
Andrew de los Reyesc24e3f32011-08-30 15:45:20 -07001721 PayloadSigner::SignatureBlobLength(vector<string>(1, private_key_path),
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001722 &signature_blob_length));
Darin Petkov9574f7e2011-01-13 10:48:12 -08001723 AddSignatureOp(next_blob_offset, signature_blob_length, &manifest);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001724 }
1725
Darin Petkov36a58222010-10-07 22:00:09 -07001726 TEST_AND_RETURN_FALSE(InitializePartitionInfos(old_kernel_part,
1727 new_kernel_part,
1728 old_image,
1729 new_image,
1730 &manifest));
1731
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001732 // Serialize protobuf
1733 string serialized_manifest;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001734
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001735 CheckGraph(graph);
1736 TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest));
1737 CheckGraph(graph);
1738
1739 LOG(INFO) << "Writing final delta file header...";
1740 DirectFileWriter writer;
1741 TEST_AND_RETURN_FALSE_ERRNO(writer.Open(output_path.c_str(),
1742 O_WRONLY | O_CREAT | O_TRUNC,
1743 0644) == 0);
1744 ScopedFileWriterCloser writer_closer(&writer);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001745
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001746 // Write header
Don Garrette410e0f2011-11-10 15:39:01 -08001747 TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, strlen(kDeltaMagic)));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001748
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001749 // Write version number
1750 TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, kVersionNumber));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001751
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001752 // Write protobuf length
1753 TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer,
1754 serialized_manifest.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001755
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001756 // Write protobuf
1757 LOG(INFO) << "Writing final delta file protobuf... "
1758 << serialized_manifest.size();
1759 TEST_AND_RETURN_FALSE(writer.Write(serialized_manifest.data(),
Don Garrette410e0f2011-11-10 15:39:01 -08001760 serialized_manifest.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001761
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001762 // Append the data blobs
1763 LOG(INFO) << "Writing final delta file data blobs...";
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001764 int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001765 ScopedFdCloser blobs_fd_closer(&blobs_fd);
1766 TEST_AND_RETURN_FALSE(blobs_fd >= 0);
1767 for (;;) {
1768 char buf[kBlockSize];
1769 ssize_t rc = read(blobs_fd, buf, sizeof(buf));
1770 if (0 == rc) {
1771 // EOF
1772 break;
1773 }
1774 TEST_AND_RETURN_FALSE_ERRNO(rc > 0);
Don Garrette410e0f2011-11-10 15:39:01 -08001775 TEST_AND_RETURN_FALSE(writer.Write(buf, rc));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001776 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001777
1778 // Write signature blob.
1779 if (!private_key_path.empty()) {
1780 LOG(INFO) << "Signing the update...";
1781 vector<char> signature_blob;
Andrew de los Reyesc24e3f32011-08-30 15:45:20 -07001782 TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(
1783 output_path,
1784 vector<string>(1, private_key_path),
1785 &signature_blob));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001786 TEST_AND_RETURN_FALSE(writer.Write(&signature_blob[0],
Don Garrette410e0f2011-11-10 15:39:01 -08001787 signature_blob.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001788 }
1789
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001790 *metadata_size =
Darin Petkov95cf01f2010-10-12 14:59:13 -07001791 strlen(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001792 ReportPayloadUsage(manifest, *metadata_size, op_name_map);
Darin Petkov880335c2010-10-01 15:52:53 -07001793
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001794 LOG(INFO) << "All done. Successfully created delta file with "
1795 << "metadata size = " << *metadata_size;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001796 return true;
1797}
1798
Thieu Le5c7d9752010-12-15 16:09:28 -08001799// Runs the bsdiff tool on two files and returns the resulting delta in
1800// 'out'. Returns true on success.
1801bool DeltaDiffGenerator::BsdiffFiles(const string& old_file,
1802 const string& new_file,
1803 vector<char>* out) {
Gilad Arnolda6742b32014-01-11 00:18:34 -08001804 const string kPatchFile = "delta.patchXXXXXX";
Thieu Le5c7d9752010-12-15 16:09:28 -08001805 string patch_file_path;
1806
1807 TEST_AND_RETURN_FALSE(
1808 utils::MakeTempFile(kPatchFile, &patch_file_path, NULL));
1809
1810 vector<string> cmd;
1811 cmd.push_back(kBsdiffPath);
1812 cmd.push_back(old_file);
1813 cmd.push_back(new_file);
1814 cmd.push_back(patch_file_path);
1815
1816 int rc = 1;
1817 vector<char> patch_file;
Darin Petkov85d02b72011-05-17 13:25:51 -07001818 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc, NULL));
Thieu Le5c7d9752010-12-15 16:09:28 -08001819 TEST_AND_RETURN_FALSE(rc == 0);
1820 TEST_AND_RETURN_FALSE(utils::ReadFile(patch_file_path, out));
1821 unlink(patch_file_path.c_str());
1822 return true;
1823}
1824
1825// The |blocks| vector contains a reader and writer for each block on the
1826// filesystem that's being in-place updated. We populate the reader/writer
1827// fields of |blocks| by calling this function.
1828// For each block in |operation| that is read or written, find that block
1829// in |blocks| and set the reader/writer field to the vertex passed.
1830// |graph| is not strictly necessary, but useful for printing out
1831// error messages.
1832bool DeltaDiffGenerator::AddInstallOpToBlocksVector(
1833 const DeltaArchiveManifest_InstallOperation& operation,
1834 const Graph& graph,
1835 Vertex::Index vertex,
1836 vector<Block>* blocks) {
1837 // See if this is already present.
1838 TEST_AND_RETURN_FALSE(operation.dst_extents_size() > 0);
1839
1840 enum BlockField { READER = 0, WRITER, BLOCK_FIELD_COUNT };
1841 for (int field = READER; field < BLOCK_FIELD_COUNT; field++) {
1842 const int extents_size =
1843 (field == READER) ? operation.src_extents_size() :
1844 operation.dst_extents_size();
1845 const char* past_participle = (field == READER) ? "read" : "written";
1846 const google::protobuf::RepeatedPtrField<Extent>& extents =
1847 (field == READER) ? operation.src_extents() : operation.dst_extents();
1848 Vertex::Index Block::*access_type =
1849 (field == READER) ? &Block::reader : &Block::writer;
1850
1851 for (int i = 0; i < extents_size; i++) {
1852 const Extent& extent = extents.Get(i);
1853 if (extent.start_block() == kSparseHole) {
1854 // Hole in sparse file. skip
1855 continue;
1856 }
1857 for (uint64_t block = extent.start_block();
1858 block < (extent.start_block() + extent.num_blocks()); block++) {
1859 if ((*blocks)[block].*access_type != Vertex::kInvalidIndex) {
1860 LOG(FATAL) << "Block " << block << " is already "
1861 << past_participle << " by "
1862 << (*blocks)[block].*access_type << "("
1863 << graph[(*blocks)[block].*access_type].file_name
1864 << ") and also " << vertex << "("
1865 << graph[vertex].file_name << ")";
1866 }
1867 (*blocks)[block].*access_type = vertex;
1868 }
1869 }
1870 }
1871 return true;
1872}
1873
Darin Petkov9574f7e2011-01-13 10:48:12 -08001874void DeltaDiffGenerator::AddSignatureOp(uint64_t signature_blob_offset,
1875 uint64_t signature_blob_length,
1876 DeltaArchiveManifest* manifest) {
1877 LOG(INFO) << "Making room for signature in file";
1878 manifest->set_signatures_offset(signature_blob_offset);
1879 LOG(INFO) << "set? " << manifest->has_signatures_offset();
1880 // Add a dummy op at the end to appease older clients
1881 DeltaArchiveManifest_InstallOperation* dummy_op =
1882 manifest->add_kernel_install_operations();
1883 dummy_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
1884 dummy_op->set_data_offset(signature_blob_offset);
1885 manifest->set_signatures_offset(signature_blob_offset);
1886 dummy_op->set_data_length(signature_blob_length);
1887 manifest->set_signatures_size(signature_blob_length);
1888 Extent* dummy_extent = dummy_op->add_dst_extents();
1889 // Tell the dummy op to write this data to a big sparse hole
1890 dummy_extent->set_start_block(kSparseHole);
1891 dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) /
1892 kBlockSize);
1893}
1894
Andrew de los Reyes50f36492010-11-01 13:57:12 -07001895const char* const kBsdiffPath = "bsdiff";
1896const char* const kBspatchPath = "bspatch";
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001897const char* const kDeltaMagic = "CrAU";
1898
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001899}; // namespace chromeos_update_engine