blob: f96037714d7bd77001de46dc6f18c8ce68e11c6f [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
Alex Deymo161c4a12014-05-16 15:56:21 -07005#include "update_engine/payload_generator/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
Ben Chan06c76a42014-09-05 08:21:06 -070020#include <base/files/file_util.h>
Alex Deymo161c4a12014-05-16 15:56:21 -070021#include <base/files/file_path.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>
Alex Vakulenko75039d72014-03-25 12:36:28 -070024#include <base/strings/string_number_conversions.h>
25#include <base/strings/string_util.h>
26#include <base/strings/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"
Don Garrettb8dd1d92013-11-22 17:40:02 -080030#include "update_engine/delta_performer.h"
Andrew de los Reyesef017552010-10-06 17:57:52 -070031#include "update_engine/extent_ranges.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070032#include "update_engine/file_writer.h"
Darin Petkov36a58222010-10-07 22:00:09 -070033#include "update_engine/omaha_hash_calculator.h"
Alex Deymo161c4a12014-05-16 15:56:21 -070034#include "update_engine/payload_constants.h"
35#include "update_engine/payload_generator/cycle_breaker.h"
36#include "update_engine/payload_generator/extent_mapper.h"
37#include "update_engine/payload_generator/filesystem_iterator.h"
38#include "update_engine/payload_generator/full_update_generator.h"
39#include "update_engine/payload_generator/graph_types.h"
40#include "update_engine/payload_generator/graph_utils.h"
41#include "update_engine/payload_generator/metadata.h"
Alex Deymo923d8fa2014-07-15 17:58:51 -070042#include "update_engine/payload_generator/payload_signer.h"
Alex Deymo161c4a12014-05-16 15:56:21 -070043#include "update_engine/payload_generator/topological_sort.h"
Alex Deymo923d8fa2014-07-15 17:58:51 -070044#include "update_engine/payload_verifier.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070045#include "update_engine/subprocess.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070046#include "update_engine/update_metadata.pb.h"
47#include "update_engine/utils.h"
48
49using std::make_pair;
Andrew de los Reyesef017552010-10-06 17:57:52 -070050using std::map;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070051using std::max;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070052using std::min;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -070053using std::pair;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070054using std::set;
55using std::string;
56using std::vector;
57
Alex Deymo161c4a12014-05-16 15:56:21 -070058namespace {
59
60const uint64_t kVersionNumber = 1;
61const uint64_t kFullUpdateChunkSize = 1024 * 1024; // bytes
62
63const size_t kBlockSize = 4096; // bytes
Alex Deymo923d8fa2014-07-15 17:58:51 -070064const char kEmptyPath[] = "";
Alex Deymo161c4a12014-05-16 15:56:21 -070065
Alex Deymo4a444ae2014-09-19 19:13:31 -070066// The maximum destination size allowed for bsdiff. In general, bsdiff should
67// work for arbitrary big files, but the payload generation and payload
68// application requires a significant amount of RAM. We put a hard-limit of
69// 200 MiB that should not affect any released board, but will limit the
70// Chrome binary in ASan builders.
71const off_t kMaxBsdiffDestinationSize = 200 * 1024 * 1024; // bytes
72
Alex Deymo161c4a12014-05-16 15:56:21 -070073static const char* kInstallOperationTypes[] = {
74 "REPLACE",
75 "REPLACE_BZ",
76 "MOVE",
77 "BSDIFF"
78};
79
80} // namespace
81
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070082namespace chromeos_update_engine {
83
84typedef DeltaDiffGenerator::Block Block;
Darin Petkov9fa7ec52010-10-18 11:45:23 -070085typedef map<const DeltaArchiveManifest_InstallOperation*,
Darin Petkov8e447e02013-04-16 16:23:50 +020086 string> OperationNameMap;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070087
Chris Sosaf586b012013-05-21 13:33:42 -070088// bytes
89const size_t kRootFSPartitionSize = static_cast<size_t>(2) * 1024 * 1024 * 1024;
Chris Sosad5ae1562013-04-23 13:20:18 -070090
Gilad Arnoldfa404502014-01-01 23:36:12 -080091// Needed for testing purposes, in case we can't use actual filesystem objects.
Alex Deymo923d8fa2014-07-15 17:58:51 -070092// TODO(garnold) (chromium:331965) Replace this hack with a properly injected
Gilad Arnoldfa404502014-01-01 23:36:12 -080093// parameter in form of a mockable abstract class.
94bool (*get_extents_with_chunk_func)(const std::string&, off_t, off_t,
95 std::vector<Extent>*) =
96 extent_mapper::ExtentsForFileChunkFibmap;
97
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070098namespace {
Darin Petkov68c10d12010-10-14 09:24:37 -070099
Gilad Arnoldfa404502014-01-01 23:36:12 -0800100// Stores all the extents of |path| into |extents|. Returns true on success.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700101bool GatherExtents(const string& path,
Darin Petkov8e447e02013-04-16 16:23:50 +0200102 off_t chunk_offset,
103 off_t chunk_size,
Gilad Arnoldfa404502014-01-01 23:36:12 -0800104 vector<Extent>* extents) {
105 extents->clear();
Darin Petkov8e447e02013-04-16 16:23:50 +0200106 TEST_AND_RETURN_FALSE(
Gilad Arnoldfa404502014-01-01 23:36:12 -0800107 get_extents_with_chunk_func(
108 path, chunk_offset, chunk_size, extents));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700109 return true;
110}
111
Andrew de los Reyesef017552010-10-06 17:57:52 -0700112// For a given regular file which must exist at new_root + path, and
113// may exist at old_root + path, creates a new InstallOperation and
114// adds it to the graph. Also, populates the |blocks| array as
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700115// necessary, if |blocks| is non-null. Also, writes the data
Andrew de los Reyesef017552010-10-06 17:57:52 -0700116// necessary to send the file down to the client into data_fd, which
117// has length *data_file_size. *data_file_size is updated
118// appropriately. If |existing_vertex| is no kInvalidIndex, use that
119// rather than allocating a new vertex. Returns true on success.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700120bool DeltaReadFile(Graph* graph,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700121 Vertex::Index existing_vertex,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700122 vector<Block>* blocks,
123 const string& old_root,
124 const string& new_root,
125 const string& path, // within new_root
Darin Petkov8e447e02013-04-16 16:23:50 +0200126 off_t chunk_offset,
127 off_t chunk_size,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700128 int data_fd,
129 off_t* data_file_size) {
130 vector<char> data;
131 DeltaArchiveManifest_InstallOperation operation;
132
Alex Deymo923d8fa2014-07-15 17:58:51 -0700133 string old_path = (old_root == kEmptyPath) ? kEmptyPath :
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800134 old_root + path;
135
Don Garrett1d787092013-03-11 18:07:28 -0700136 // If bsdiff breaks again, blacklist the problem file by using:
137 // bsdiff_allowed = (path != "/foo/bar")
Don Garrett36e60772012-03-29 10:31:20 -0700138 //
Don Garrett1d787092013-03-11 18:07:28 -0700139 // TODO(dgarrett): chromium-os:15274 connect this test to the command line.
Don Garrett36e60772012-03-29 10:31:20 -0700140 bool bsdiff_allowed = true;
Don Garrettf4b28742012-03-27 20:48:06 -0700141
Alex Deymo4a444ae2014-09-19 19:13:31 -0700142 if (utils::FileSize(new_root + path) > kMaxBsdiffDestinationSize)
143 bsdiff_allowed = false;
144
Don Garrett36e60772012-03-29 10:31:20 -0700145 if (!bsdiff_allowed)
146 LOG(INFO) << "bsdiff blacklisting: " << path;
Don Garrettf4b28742012-03-27 20:48:06 -0700147
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800148 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ReadFileToDiff(old_path,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700149 new_root + path,
Darin Petkov8e447e02013-04-16 16:23:50 +0200150 chunk_offset,
151 chunk_size,
Don Garrett36e60772012-03-29 10:31:20 -0700152 bsdiff_allowed,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700153 &data,
Darin Petkov68c10d12010-10-14 09:24:37 -0700154 &operation,
155 true));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700156
Gilad Arnoldfa404502014-01-01 23:36:12 -0800157 // Check if the operation writes nothing.
158 if (operation.dst_extents_size() == 0) {
159 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
160 LOG(INFO) << "Empty MOVE operation (" << new_root + path << "), skipping";
161 return true;
162 } else {
163 LOG(ERROR) << "Empty non-MOVE operation";
164 return false;
165 }
166 }
167
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700168 // Write the data
169 if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) {
170 operation.set_data_offset(*data_file_size);
171 operation.set_data_length(data.size());
172 }
173
174 TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, &data[0], data.size()));
175 *data_file_size += data.size();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700176
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700177 // Now, insert into graph and blocks vector
Andrew de los Reyesef017552010-10-06 17:57:52 -0700178 Vertex::Index vertex = existing_vertex;
179 if (vertex == Vertex::kInvalidIndex) {
180 graph->resize(graph->size() + 1);
181 vertex = graph->size() - 1;
182 }
183 (*graph)[vertex].op = operation;
184 CHECK((*graph)[vertex].op.has_type());
185 (*graph)[vertex].file_name = path;
Darin Petkov8e447e02013-04-16 16:23:50 +0200186 (*graph)[vertex].chunk_offset = chunk_offset;
187 (*graph)[vertex].chunk_size = chunk_size;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700188
Andrew de los Reyesef017552010-10-06 17:57:52 -0700189 if (blocks)
Thieu Le5c7d9752010-12-15 16:09:28 -0800190 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::AddInstallOpToBlocksVector(
191 (*graph)[vertex].op,
192 *graph,
193 vertex,
194 blocks));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700195 return true;
196}
197
198// For each regular file within new_root, creates a node in the graph,
199// determines the best way to compress it (REPLACE, REPLACE_BZ, COPY, BSDIFF),
200// and writes any necessary data to the end of data_fd.
201bool DeltaReadFiles(Graph* graph,
202 vector<Block>* blocks,
203 const string& old_root,
204 const string& new_root,
Darin Petkov8e447e02013-04-16 16:23:50 +0200205 off_t chunk_size,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700206 int data_fd,
207 off_t* data_file_size) {
208 set<ino_t> visited_inodes;
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800209 set<ino_t> visited_src_inodes;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700210 for (FilesystemIterator fs_iter(new_root,
211 utils::SetWithValue<string>("/lost+found"));
212 !fs_iter.IsEnd(); fs_iter.Increment()) {
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800213 // We never diff symlinks (here, we check that dst file is not a symlink).
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700214 if (!S_ISREG(fs_iter.GetStat().st_mode))
215 continue;
216
217 // Make sure we visit each inode only once.
218 if (utils::SetContainsKey(visited_inodes, fs_iter.GetStat().st_ino))
219 continue;
220 visited_inodes.insert(fs_iter.GetStat().st_ino);
Gabe Blacka77939e2014-09-09 23:35:08 -0700221 off_t dst_size = fs_iter.GetFileSize();
Darin Petkov8e447e02013-04-16 16:23:50 +0200222 if (dst_size == 0)
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700223 continue;
224
225 LOG(INFO) << "Encoding file " << fs_iter.GetPartialPath();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700226
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800227 // We can't visit each dst image inode more than once, as that would
228 // duplicate work. Here, we avoid visiting each source image inode
229 // more than once. Technically, we could have multiple operations
230 // that read the same blocks from the source image for diffing, but
Alex Vakulenko072359c2014-07-18 11:41:07 -0700231 // we choose not to avoid complexity. Eventually we will move away
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800232 // from using a graph/cycle detection/etc to generate diffs, and at that
233 // time, it will be easy (non-complex) to have many operations read
234 // from the same source blocks. At that time, this code can die. -adlr
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800235 bool should_diff_from_source = false;
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800236 string src_path = old_root + fs_iter.GetPartialPath();
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800237 struct stat src_stbuf;
238 // We never diff symlinks (here, we check that src file is not a symlink).
239 if (0 == lstat(src_path.c_str(), &src_stbuf) &&
240 S_ISREG(src_stbuf.st_mode)) {
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800241 should_diff_from_source = !utils::SetContainsKey(visited_src_inodes,
242 src_stbuf.st_ino);
243 visited_src_inodes.insert(src_stbuf.st_ino);
244 }
245
Darin Petkov8e447e02013-04-16 16:23:50 +0200246 off_t size = chunk_size == -1 ? dst_size : chunk_size;
247 off_t step = size;
248 for (off_t offset = 0; offset < dst_size; offset += step) {
249 if (offset + size >= dst_size) {
250 size = -1; // Read through the end of the file.
251 }
252 TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
253 Vertex::kInvalidIndex,
254 blocks,
255 (should_diff_from_source ?
256 old_root :
Alex Deymo923d8fa2014-07-15 17:58:51 -0700257 kEmptyPath),
Darin Petkov8e447e02013-04-16 16:23:50 +0200258 new_root,
259 fs_iter.GetPartialPath(),
260 offset,
261 size,
262 data_fd,
263 data_file_size));
264 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700265 }
266 return true;
267}
268
Andrew de los Reyesef017552010-10-06 17:57:52 -0700269// This class allocates non-existent temp blocks, starting from
270// kTempBlockStart. Other code is responsible for converting these
271// temp blocks into real blocks, as the client can't read or write to
272// these blocks.
273class DummyExtentAllocator {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700274 public:
Alex Vakulenko157fe302014-08-11 15:59:58 -0700275 DummyExtentAllocator() : next_block_(kTempBlockStart) {}
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700276 vector<Extent> Allocate(const uint64_t block_count) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700277 vector<Extent> ret(1);
278 ret[0].set_start_block(next_block_);
279 ret[0].set_num_blocks(block_count);
280 next_block_ += block_count;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700281 return ret;
282 }
283 private:
Andrew de los Reyesef017552010-10-06 17:57:52 -0700284 uint64_t next_block_;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700285};
286
287// Reads blocks from image_path that are not yet marked as being written
288// in the blocks array. These blocks that remain are non-file-data blocks.
289// In the future we might consider intelligent diffing between this data
290// and data in the previous image, but for now we just bzip2 compress it
291// and include it in the update.
292// Creates a new node in the graph to write these blocks and writes the
293// appropriate blob to blobs_fd. Reads and updates blobs_length;
294bool ReadUnwrittenBlocks(const vector<Block>& blocks,
295 int blobs_fd,
296 off_t* blobs_length,
297 const string& image_path,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700298 Vertex* vertex) {
Darin Petkovabe7cc92010-10-08 12:29:32 -0700299 vertex->file_name = "<rootfs-non-file-data>";
300
Andrew de los Reyesef017552010-10-06 17:57:52 -0700301 DeltaArchiveManifest_InstallOperation* out_op = &vertex->op;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700302 int image_fd = open(image_path.c_str(), O_RDONLY, 000);
303 TEST_AND_RETURN_FALSE_ERRNO(image_fd >= 0);
304 ScopedFdCloser image_fd_closer(&image_fd);
305
306 string temp_file_path;
Gilad Arnolda6742b32014-01-11 00:18:34 -0800307 TEST_AND_RETURN_FALSE(utils::MakeTempFile("CrAU_temp_data.XXXXXX",
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700308 &temp_file_path,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700309 nullptr));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700310
311 FILE* file = fopen(temp_file_path.c_str(), "w");
312 TEST_AND_RETURN_FALSE(file);
313 int err = BZ_OK;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700314
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700315 BZFILE* bz_file = BZ2_bzWriteOpen(&err,
316 file,
317 9, // max compression
318 0, // verbosity
319 0); // default work factor
320 TEST_AND_RETURN_FALSE(err == BZ_OK);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700321
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700322 vector<Extent> extents;
323 vector<Block>::size_type block_count = 0;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700324
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700325 LOG(INFO) << "Appending left over blocks to extents";
326 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
327 if (blocks[i].writer != Vertex::kInvalidIndex)
328 continue;
Andrew de los Reyesef017552010-10-06 17:57:52 -0700329 if (blocks[i].reader != Vertex::kInvalidIndex) {
330 graph_utils::AddReadBeforeDep(vertex, blocks[i].reader, i);
331 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700332 graph_utils::AppendBlockToExtents(&extents, i);
333 block_count++;
334 }
335
336 // Code will handle 'buf' at any size that's a multiple of kBlockSize,
337 // so we arbitrarily set it to 1024 * kBlockSize.
338 vector<char> buf(1024 * kBlockSize);
339
340 LOG(INFO) << "Reading left over blocks";
341 vector<Block>::size_type blocks_copied_count = 0;
342
343 // For each extent in extents, write the data into BZ2_bzWrite which
344 // sends it to an output file.
345 // We use the temporary buffer 'buf' to hold the data, which may be
346 // smaller than the extent, so in that case we have to loop to get
347 // the extent's data (that's the inner while loop).
348 for (vector<Extent>::const_iterator it = extents.begin();
349 it != extents.end(); ++it) {
350 vector<Block>::size_type blocks_read = 0;
Andrew de los Reyes4b8740f2010-11-08 17:09:11 -0800351 float printed_progress = -1;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700352 while (blocks_read < it->num_blocks()) {
353 const int copy_block_cnt =
354 min(buf.size() / kBlockSize,
355 static_cast<vector<char>::size_type>(
356 it->num_blocks() - blocks_read));
357 ssize_t rc = pread(image_fd,
358 &buf[0],
359 copy_block_cnt * kBlockSize,
360 (it->start_block() + blocks_read) * kBlockSize);
361 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
362 TEST_AND_RETURN_FALSE(static_cast<size_t>(rc) ==
363 copy_block_cnt * kBlockSize);
364 BZ2_bzWrite(&err, bz_file, &buf[0], copy_block_cnt * kBlockSize);
365 TEST_AND_RETURN_FALSE(err == BZ_OK);
366 blocks_read += copy_block_cnt;
367 blocks_copied_count += copy_block_cnt;
Andrew de los Reyes4b8740f2010-11-08 17:09:11 -0800368 float current_progress =
369 static_cast<float>(blocks_copied_count) / block_count;
370 if (printed_progress + 0.1 < current_progress ||
371 blocks_copied_count == block_count) {
372 LOG(INFO) << "progress: " << current_progress;
373 printed_progress = current_progress;
374 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700375 }
376 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700377 BZ2_bzWriteClose(&err, bz_file, 0, nullptr, nullptr);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700378 TEST_AND_RETURN_FALSE(err == BZ_OK);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700379 bz_file = nullptr;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700380 TEST_AND_RETURN_FALSE_ERRNO(0 == fclose(file));
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700381 file = nullptr;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700382
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700383 vector<char> compressed_data;
384 LOG(INFO) << "Reading compressed data off disk";
385 TEST_AND_RETURN_FALSE(utils::ReadFile(temp_file_path, &compressed_data));
386 TEST_AND_RETURN_FALSE(unlink(temp_file_path.c_str()) == 0);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700387
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700388 // Add node to graph to write these blocks
389 out_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
390 out_op->set_data_offset(*blobs_length);
391 out_op->set_data_length(compressed_data.size());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700392 LOG(INFO) << "Rootfs non-data blocks compressed take up "
393 << compressed_data.size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700394 *blobs_length += compressed_data.size();
395 out_op->set_dst_length(kBlockSize * block_count);
396 DeltaDiffGenerator::StoreExtents(extents, out_op->mutable_dst_extents());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700397
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700398 TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd,
399 &compressed_data[0],
400 compressed_data.size()));
401 LOG(INFO) << "done with extra blocks";
402 return true;
403}
404
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700405// Writes the uint64_t passed in in host-endian to the file as big-endian.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700406// Returns true on success.
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700407bool WriteUint64AsBigEndian(FileWriter* writer, const uint64_t value) {
408 uint64_t value_be = htobe64(value);
Don Garrette410e0f2011-11-10 15:39:01 -0800409 TEST_AND_RETURN_FALSE(writer->Write(&value_be, sizeof(value_be)));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700410 return true;
411}
412
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700413// Adds each operation from |graph| to |out_manifest| in the order specified by
414// |order| while building |out_op_name_map| with operation to name
415// mappings. Adds all |kernel_ops| to |out_manifest|. Filters out no-op
416// operations.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700417void InstallOperationsToManifest(
418 const Graph& graph,
419 const vector<Vertex::Index>& order,
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700420 const vector<DeltaArchiveManifest_InstallOperation>& kernel_ops,
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700421 DeltaArchiveManifest* out_manifest,
422 OperationNameMap* out_op_name_map) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700423 for (vector<Vertex::Index>::const_iterator it = order.begin();
424 it != order.end(); ++it) {
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700425 const Vertex& vertex = graph[*it];
426 const DeltaArchiveManifest_InstallOperation& add_op = vertex.op;
427 if (DeltaDiffGenerator::IsNoopOperation(add_op)) {
428 continue;
429 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700430 DeltaArchiveManifest_InstallOperation* op =
431 out_manifest->add_install_operations();
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700432 *op = add_op;
Darin Petkov8e447e02013-04-16 16:23:50 +0200433 string name = vertex.file_name;
434 if (vertex.chunk_offset || vertex.chunk_size != -1) {
435 string offset = base::Int64ToString(vertex.chunk_offset);
436 if (vertex.chunk_size != -1) {
437 name += " [" + offset + ", " +
438 base::Int64ToString(vertex.chunk_offset + vertex.chunk_size - 1) +
439 "]";
440 } else {
441 name += " [" + offset + ", end]";
442 }
443 }
444 (*out_op_name_map)[op] = name;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700445 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700446 for (vector<DeltaArchiveManifest_InstallOperation>::const_iterator it =
447 kernel_ops.begin(); it != kernel_ops.end(); ++it) {
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700448 const DeltaArchiveManifest_InstallOperation& add_op = *it;
449 if (DeltaDiffGenerator::IsNoopOperation(add_op)) {
450 continue;
451 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700452 DeltaArchiveManifest_InstallOperation* op =
453 out_manifest->add_kernel_install_operations();
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700454 *op = add_op;
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700455 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700456}
457
458void CheckGraph(const Graph& graph) {
459 for (Graph::const_iterator it = graph.begin(); it != graph.end(); ++it) {
460 CHECK(it->op.has_type());
461 }
462}
463
Darin Petkov68c10d12010-10-14 09:24:37 -0700464// Delta compresses a kernel partition |new_kernel_part| with knowledge of the
465// old kernel partition |old_kernel_part|. If |old_kernel_part| is an empty
466// string, generates a full update of the partition.
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700467bool DeltaCompressKernelPartition(
468 const string& old_kernel_part,
469 const string& new_kernel_part,
470 vector<DeltaArchiveManifest_InstallOperation>* ops,
471 int blobs_fd,
472 off_t* blobs_length) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700473 LOG(INFO) << "Delta compressing kernel partition...";
Darin Petkov68c10d12010-10-14 09:24:37 -0700474 LOG_IF(INFO, old_kernel_part.empty()) << "Generating full kernel update...";
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700475
Gilad Arnoldfa404502014-01-01 23:36:12 -0800476 DeltaArchiveManifest_InstallOperation op;
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700477 vector<char> data;
Don Garrett36e60772012-03-29 10:31:20 -0700478 TEST_AND_RETURN_FALSE(
479 DeltaDiffGenerator::ReadFileToDiff(old_kernel_part,
480 new_kernel_part,
Darin Petkov8e447e02013-04-16 16:23:50 +0200481 0, // chunk_offset
482 -1, // chunk_size
Alex Deymo923d8fa2014-07-15 17:58:51 -0700483 true, // bsdiff_allowed
Don Garrett36e60772012-03-29 10:31:20 -0700484 &data,
Gilad Arnoldfa404502014-01-01 23:36:12 -0800485 &op,
Don Garrett36e60772012-03-29 10:31:20 -0700486 false));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700487
Gilad Arnoldfa404502014-01-01 23:36:12 -0800488 // Check if the operation writes nothing.
489 if (op.dst_extents_size() == 0) {
490 if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
491 LOG(INFO) << "Empty MOVE operation, nothing to do.";
492 return true;
493 } else {
494 LOG(ERROR) << "Empty non-MOVE operation";
495 return false;
496 }
Darin Petkov68c10d12010-10-14 09:24:37 -0700497 }
Andrew de los Reyes36f37362010-09-03 09:20:04 -0700498
Gilad Arnoldfa404502014-01-01 23:36:12 -0800499 // Write the data.
500 if (op.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) {
501 op.set_data_offset(*blobs_length);
502 op.set_data_length(data.size());
503 }
504
505 // Add the new install operation.
506 ops->clear();
507 ops->push_back(op);
508
Darin Petkov68c10d12010-10-14 09:24:37 -0700509 TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd, &data[0], data.size()));
510 *blobs_length += data.size();
Andrew de los Reyes36f37362010-09-03 09:20:04 -0700511
Darin Petkov68c10d12010-10-14 09:24:37 -0700512 LOG(INFO) << "Done delta compressing kernel partition: "
Gilad Arnoldfa404502014-01-01 23:36:12 -0800513 << kInstallOperationTypes[op.type()];
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700514 return true;
515}
516
Darin Petkov880335c2010-10-01 15:52:53 -0700517struct DeltaObject {
518 DeltaObject(const string& in_name, const int in_type, const off_t in_size)
519 : name(in_name),
520 type(in_type),
521 size(in_size) {}
522 bool operator <(const DeltaObject& object) const {
Darin Petkovd43d6902010-10-14 11:17:50 -0700523 return (size != object.size) ? (size < object.size) : (name < object.name);
Darin Petkov880335c2010-10-01 15:52:53 -0700524 }
525 string name;
526 int type;
527 off_t size;
528};
529
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700530void ReportPayloadUsage(const DeltaArchiveManifest& manifest,
531 const int64_t manifest_metadata_size,
532 const OperationNameMap& op_name_map) {
Darin Petkov880335c2010-10-01 15:52:53 -0700533 vector<DeltaObject> objects;
534 off_t total_size = 0;
535
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700536 // Rootfs install operations.
537 for (int i = 0; i < manifest.install_operations_size(); ++i) {
538 const DeltaArchiveManifest_InstallOperation& op =
539 manifest.install_operations(i);
Darin Petkov8e447e02013-04-16 16:23:50 +0200540 objects.push_back(DeltaObject(op_name_map.find(&op)->second,
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700541 op.type(),
542 op.data_length()));
543 total_size += op.data_length();
Darin Petkov880335c2010-10-01 15:52:53 -0700544 }
545
Darin Petkov880335c2010-10-01 15:52:53 -0700546 // Kernel install operations.
547 for (int i = 0; i < manifest.kernel_install_operations_size(); ++i) {
548 const DeltaArchiveManifest_InstallOperation& op =
549 manifest.kernel_install_operations(i);
Alex Vakulenko75039d72014-03-25 12:36:28 -0700550 objects.push_back(DeltaObject(base::StringPrintf("<kernel-operation-%d>",
551 i),
Darin Petkov880335c2010-10-01 15:52:53 -0700552 op.type(),
553 op.data_length()));
554 total_size += op.data_length();
555 }
556
Darin Petkov95cf01f2010-10-12 14:59:13 -0700557 objects.push_back(DeltaObject("<manifest-metadata>",
558 -1,
559 manifest_metadata_size));
560 total_size += manifest_metadata_size;
561
Darin Petkov880335c2010-10-01 15:52:53 -0700562 std::sort(objects.begin(), objects.end());
563
Alex Vakulenko75039d72014-03-25 12:36:28 -0700564 static const char kFormatString[] = "%6.2f%% %10jd %-10s %s\n";
Darin Petkov880335c2010-10-01 15:52:53 -0700565 for (vector<DeltaObject>::const_iterator it = objects.begin();
566 it != objects.end(); ++it) {
567 const DeltaObject& object = *it;
568 fprintf(stderr, kFormatString,
569 object.size * 100.0 / total_size,
Alex Vakulenko75039d72014-03-25 12:36:28 -0700570 static_cast<intmax_t>(object.size),
Darin Petkov95cf01f2010-10-12 14:59:13 -0700571 object.type >= 0 ? kInstallOperationTypes[object.type] : "-",
Darin Petkov880335c2010-10-01 15:52:53 -0700572 object.name.c_str());
573 }
Alex Vakulenko75039d72014-03-25 12:36:28 -0700574 fprintf(stderr, kFormatString,
575 100.0, static_cast<intmax_t>(total_size), "", "<total>");
Darin Petkov880335c2010-10-01 15:52:53 -0700576}
577
Gilad Arnoldfa404502014-01-01 23:36:12 -0800578// Process a range of blocks from |range_start| to |range_end| in the extent at
579// position |*idx_p| of |extents|. If |do_remove| is true, this range will be
580// removed, which may cause the extent to be trimmed, split or removed entirely.
581// The value of |*idx_p| is updated to point to the next extent to be processed.
582// Returns true iff the next extent to process is a new or updated one.
583bool ProcessExtentBlockRange(vector<Extent>* extents, size_t* idx_p,
584 const bool do_remove, uint64_t range_start,
585 uint64_t range_end) {
586 size_t idx = *idx_p;
587 uint64_t start_block = (*extents)[idx].start_block();
588 uint64_t num_blocks = (*extents)[idx].num_blocks();
589 uint64_t range_size = range_end - range_start;
590
591 if (do_remove) {
592 if (range_size == num_blocks) {
593 // Remove the entire extent.
594 extents->erase(extents->begin() + idx);
595 } else if (range_end == num_blocks) {
596 // Trim the end of the extent.
597 (*extents)[idx].set_num_blocks(num_blocks - range_size);
598 idx++;
599 } else if (range_start == 0) {
600 // Trim the head of the extent.
601 (*extents)[idx].set_start_block(start_block + range_size);
602 (*extents)[idx].set_num_blocks(num_blocks - range_size);
603 } else {
604 // Trim the middle, splitting the remainder into two parts.
605 (*extents)[idx].set_num_blocks(range_start);
606 Extent e;
607 e.set_start_block(start_block + range_end);
608 e.set_num_blocks(num_blocks - range_end);
609 idx++;
610 extents->insert(extents->begin() + idx, e);
611 }
612 } else if (range_end == num_blocks) {
613 // Done with this extent.
614 idx++;
615 } else {
616 return false;
617 }
618
619 *idx_p = idx;
620 return true;
621}
622
623// Remove identical corresponding block ranges in |src_extents| and
624// |dst_extents|. Used for preventing moving of blocks onto themselves during
Gilad Arnoldebca5712014-01-10 14:26:37 -0800625// MOVE operations. The value of |total_bytes| indicates the actual length of
626// content; this may be slightly less than the total size of blocks, in which
627// case the last block is only partly occupied with data. Returns the total
628// number of bytes removed.
629size_t RemoveIdenticalBlockRanges(vector<Extent>* src_extents,
630 vector<Extent>* dst_extents,
631 const size_t total_bytes) {
Gilad Arnoldfa404502014-01-01 23:36:12 -0800632 size_t src_idx = 0;
633 size_t dst_idx = 0;
634 uint64_t src_offset = 0, dst_offset = 0;
635 bool new_src = true, new_dst = true;
Gilad Arnoldebca5712014-01-10 14:26:37 -0800636 size_t removed_bytes = 0, nonfull_block_bytes;
637 bool do_remove = false;
Gilad Arnoldfa404502014-01-01 23:36:12 -0800638 while (src_idx < src_extents->size() && dst_idx < dst_extents->size()) {
639 if (new_src) {
640 src_offset = 0;
641 new_src = false;
642 }
643 if (new_dst) {
644 dst_offset = 0;
645 new_dst = false;
646 }
647
Gilad Arnoldebca5712014-01-10 14:26:37 -0800648 do_remove = ((*src_extents)[src_idx].start_block() + src_offset ==
649 (*dst_extents)[dst_idx].start_block() + dst_offset);
Gilad Arnoldfa404502014-01-01 23:36:12 -0800650
651 uint64_t src_num_blocks = (*src_extents)[src_idx].num_blocks();
652 uint64_t dst_num_blocks = (*dst_extents)[dst_idx].num_blocks();
653 uint64_t min_num_blocks = min(src_num_blocks - src_offset,
654 dst_num_blocks - dst_offset);
655 uint64_t prev_src_offset = src_offset;
656 uint64_t prev_dst_offset = dst_offset;
657 src_offset += min_num_blocks;
658 dst_offset += min_num_blocks;
659
660 new_src = ProcessExtentBlockRange(src_extents, &src_idx, do_remove,
661 prev_src_offset, src_offset);
662 new_dst = ProcessExtentBlockRange(dst_extents, &dst_idx, do_remove,
663 prev_dst_offset, dst_offset);
Gilad Arnoldebca5712014-01-10 14:26:37 -0800664 if (do_remove)
665 removed_bytes += min_num_blocks * kBlockSize;
Gilad Arnoldfa404502014-01-01 23:36:12 -0800666 }
Gilad Arnoldebca5712014-01-10 14:26:37 -0800667
668 // If we removed the last block and this block is only partly used by file
669 // content, deduct the unused portion from the total removed byte count.
670 if (do_remove && (nonfull_block_bytes = total_bytes % kBlockSize))
671 removed_bytes -= kBlockSize - nonfull_block_bytes;
672
673 return removed_bytes;
Gilad Arnoldfa404502014-01-01 23:36:12 -0800674}
675
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700676} // namespace
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700677
678bool DeltaDiffGenerator::ReadFileToDiff(
679 const string& old_filename,
680 const string& new_filename,
Darin Petkov8e447e02013-04-16 16:23:50 +0200681 off_t chunk_offset,
682 off_t chunk_size,
Don Garrett36e60772012-03-29 10:31:20 -0700683 bool bsdiff_allowed,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700684 vector<char>* out_data,
Darin Petkov68c10d12010-10-14 09:24:37 -0700685 DeltaArchiveManifest_InstallOperation* out_op,
686 bool gather_extents) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700687 // Read new data in
688 vector<char> new_data;
Darin Petkov8e447e02013-04-16 16:23:50 +0200689 TEST_AND_RETURN_FALSE(
690 utils::ReadFileChunk(new_filename, chunk_offset, chunk_size, &new_data));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700691
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700692 TEST_AND_RETURN_FALSE(!new_data.empty());
Darin Petkov8e447e02013-04-16 16:23:50 +0200693 TEST_AND_RETURN_FALSE(chunk_size == -1 ||
694 static_cast<off_t>(new_data.size()) <= chunk_size);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700695
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700696 vector<char> new_data_bz;
697 TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz));
698 CHECK(!new_data_bz.empty());
699
700 vector<char> data; // Data blob that will be written to delta file.
701
702 DeltaArchiveManifest_InstallOperation operation;
703 size_t current_best_size = 0;
704 if (new_data.size() <= new_data_bz.size()) {
705 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
706 current_best_size = new_data.size();
707 data = new_data;
708 } else {
709 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
710 current_best_size = new_data_bz.size();
711 data = new_data_bz;
712 }
713
714 // Do we have an original file to consider?
Gabe Blacka77939e2014-09-09 23:35:08 -0700715 off_t old_size = 0;
Don Garrettf4b28742012-03-27 20:48:06 -0700716 bool original = !old_filename.empty();
Gabe Blacka77939e2014-09-09 23:35:08 -0700717 if (original && (old_size = utils::FileSize(old_filename)) < 0) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700718 // If stat-ing the old file fails, it should be because it doesn't exist.
Gabe Blacka77939e2014-09-09 23:35:08 -0700719 TEST_AND_RETURN_FALSE(!utils::FileExists(old_filename.c_str()));
Don Garrettf4b28742012-03-27 20:48:06 -0700720 original = false;
Darin Petkov68c10d12010-10-14 09:24:37 -0700721 }
Don Garrettf4b28742012-03-27 20:48:06 -0700722
Darin Petkov8e447e02013-04-16 16:23:50 +0200723 vector<char> old_data;
Don Garrettf4b28742012-03-27 20:48:06 -0700724 if (original) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700725 // Read old data
Darin Petkov8e447e02013-04-16 16:23:50 +0200726 TEST_AND_RETURN_FALSE(
727 utils::ReadFileChunk(
728 old_filename, chunk_offset, chunk_size, &old_data));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700729 if (old_data == new_data) {
730 // No change in data.
731 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
732 current_best_size = 0;
733 data.clear();
Darin Petkov8e447e02013-04-16 16:23:50 +0200734 } else if (!old_data.empty() && bsdiff_allowed) {
Don Garrett36e60772012-03-29 10:31:20 -0700735 // If the source file is considered bsdiff safe (no bsdiff bugs
736 // triggered), see if BSDIFF encoding is smaller.
Alex Vakulenko75039d72014-03-25 12:36:28 -0700737 base::FilePath old_chunk;
738 TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&old_chunk));
Darin Petkov8e447e02013-04-16 16:23:50 +0200739 ScopedPathUnlinker old_unlinker(old_chunk.value());
740 TEST_AND_RETURN_FALSE(
741 utils::WriteFile(old_chunk.value().c_str(),
742 &old_data[0], old_data.size()));
Alex Vakulenko75039d72014-03-25 12:36:28 -0700743 base::FilePath new_chunk;
744 TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&new_chunk));
Darin Petkov8e447e02013-04-16 16:23:50 +0200745 ScopedPathUnlinker new_unlinker(new_chunk.value());
746 TEST_AND_RETURN_FALSE(
747 utils::WriteFile(new_chunk.value().c_str(),
748 &new_data[0], new_data.size()));
749
Don Garrett36e60772012-03-29 10:31:20 -0700750 vector<char> bsdiff_delta;
751 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200752 BsdiffFiles(old_chunk.value(), new_chunk.value(), &bsdiff_delta));
Don Garrett36e60772012-03-29 10:31:20 -0700753 CHECK_GT(bsdiff_delta.size(), static_cast<vector<char>::size_type>(0));
754 if (bsdiff_delta.size() < current_best_size) {
755 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
756 current_best_size = bsdiff_delta.size();
757 data = bsdiff_delta;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700758 }
759 }
760 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700761
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700762 // Set parameters of the operations
763 CHECK_EQ(data.size(), current_best_size);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700764
Gilad Arnoldfa404502014-01-01 23:36:12 -0800765 vector<Extent> src_extents, dst_extents;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700766 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE ||
767 operation.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) {
Darin Petkov68c10d12010-10-14 09:24:37 -0700768 if (gather_extents) {
769 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200770 GatherExtents(old_filename,
771 chunk_offset,
772 chunk_size,
Gilad Arnoldfa404502014-01-01 23:36:12 -0800773 &src_extents));
Darin Petkov68c10d12010-10-14 09:24:37 -0700774 } else {
775 Extent* src_extent = operation.add_src_extents();
776 src_extent->set_start_block(0);
Gabe Blacka77939e2014-09-09 23:35:08 -0700777 src_extent->set_num_blocks((old_size + kBlockSize - 1) / kBlockSize);
Darin Petkov68c10d12010-10-14 09:24:37 -0700778 }
Darin Petkov8e447e02013-04-16 16:23:50 +0200779 operation.set_src_length(old_data.size());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700780 }
781
Darin Petkov68c10d12010-10-14 09:24:37 -0700782 if (gather_extents) {
783 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200784 GatherExtents(new_filename,
785 chunk_offset,
786 chunk_size,
Gilad Arnoldfa404502014-01-01 23:36:12 -0800787 &dst_extents));
Darin Petkov68c10d12010-10-14 09:24:37 -0700788 } else {
789 Extent* dst_extent = operation.add_dst_extents();
790 dst_extent->set_start_block(0);
791 dst_extent->set_num_blocks((new_data.size() + kBlockSize - 1) / kBlockSize);
792 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700793 operation.set_dst_length(new_data.size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700794
Gilad Arnoldfa404502014-01-01 23:36:12 -0800795 if (gather_extents) {
796 // Remove identical src/dst block ranges in MOVE operations.
Gilad Arnoldebca5712014-01-10 14:26:37 -0800797 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
798 size_t removed_bytes = RemoveIdenticalBlockRanges(
799 &src_extents, &dst_extents, new_data.size());
800
801 // Adjust the file length field accordingly.
802 if (removed_bytes) {
803 operation.set_src_length(old_data.size() - removed_bytes);
804 operation.set_dst_length(new_data.size() - removed_bytes);
805 }
806 }
Gilad Arnoldfa404502014-01-01 23:36:12 -0800807
808 // Embed extents in the operation.
809 DeltaDiffGenerator::StoreExtents(src_extents,
810 operation.mutable_src_extents());
811 DeltaDiffGenerator::StoreExtents(dst_extents,
812 operation.mutable_dst_extents());
813 }
814
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700815 out_data->swap(data);
816 *out_op = operation;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700817
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700818 return true;
819}
820
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700821bool DeltaDiffGenerator::InitializePartitionInfo(bool is_kernel,
822 const string& partition,
823 PartitionInfo* info) {
Darin Petkov7ea32332010-10-13 10:46:11 -0700824 int64_t size = 0;
825 if (is_kernel) {
826 size = utils::FileSize(partition);
827 } else {
828 int block_count = 0, block_size = 0;
829 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(partition,
830 &block_count,
831 &block_size));
832 size = static_cast<int64_t>(block_count) * block_size;
833 }
834 TEST_AND_RETURN_FALSE(size > 0);
Darin Petkov36a58222010-10-07 22:00:09 -0700835 info->set_size(size);
836 OmahaHashCalculator hasher;
Darin Petkov7ea32332010-10-13 10:46:11 -0700837 TEST_AND_RETURN_FALSE(hasher.UpdateFile(partition, size) == size);
Darin Petkov36a58222010-10-07 22:00:09 -0700838 TEST_AND_RETURN_FALSE(hasher.Finalize());
839 const vector<char>& hash = hasher.raw_hash();
840 info->set_hash(hash.data(), hash.size());
Darin Petkovd43d6902010-10-14 11:17:50 -0700841 LOG(INFO) << partition << ": size=" << size << " hash=" << hasher.hash();
Darin Petkov36a58222010-10-07 22:00:09 -0700842 return true;
843}
844
845bool InitializePartitionInfos(const string& old_kernel,
846 const string& new_kernel,
847 const string& old_rootfs,
848 const string& new_rootfs,
849 DeltaArchiveManifest* manifest) {
Darin Petkovd43d6902010-10-14 11:17:50 -0700850 if (!old_kernel.empty()) {
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700851 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
852 true,
853 old_kernel,
854 manifest->mutable_old_kernel_info()));
Darin Petkovd43d6902010-10-14 11:17:50 -0700855 }
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700856 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
857 true,
858 new_kernel,
859 manifest->mutable_new_kernel_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700860 if (!old_rootfs.empty()) {
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700861 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
862 false,
863 old_rootfs,
864 manifest->mutable_old_rootfs_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700865 }
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700866 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
867 false,
868 new_rootfs,
869 manifest->mutable_new_rootfs_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700870 return true;
871}
872
Andrew de los Reyesef017552010-10-06 17:57:52 -0700873namespace {
874
875// Takes a collection (vector or RepeatedPtrField) of Extent and
876// returns a vector of the blocks referenced, in order.
877template<typename T>
878vector<uint64_t> ExpandExtents(const T& extents) {
879 vector<uint64_t> ret;
880 for (size_t i = 0, e = static_cast<size_t>(extents.size()); i != e; ++i) {
881 const Extent extent = graph_utils::GetElement(extents, i);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700882 if (extent.start_block() == kSparseHole) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700883 ret.resize(ret.size() + extent.num_blocks(), kSparseHole);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700884 } else {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700885 for (uint64_t block = extent.start_block();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700886 block < (extent.start_block() + extent.num_blocks()); block++) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700887 ret.push_back(block);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700888 }
889 }
890 }
Andrew de los Reyesef017552010-10-06 17:57:52 -0700891 return ret;
892}
893
894// Takes a vector of blocks and returns an equivalent vector of Extent
895// objects.
896vector<Extent> CompressExtents(const vector<uint64_t>& blocks) {
897 vector<Extent> new_extents;
898 for (vector<uint64_t>::const_iterator it = blocks.begin(), e = blocks.end();
899 it != e; ++it) {
900 graph_utils::AppendBlockToExtents(&new_extents, *it);
901 }
902 return new_extents;
903}
904
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700905} // namespace
Andrew de los Reyesef017552010-10-06 17:57:52 -0700906
907void DeltaDiffGenerator::SubstituteBlocks(
908 Vertex* vertex,
909 const vector<Extent>& remove_extents,
910 const vector<Extent>& replace_extents) {
911 // First, expand out the blocks that op reads from
912 vector<uint64_t> read_blocks = ExpandExtents(vertex->op.src_extents());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700913 {
914 // Expand remove_extents and replace_extents
Andrew de los Reyesef017552010-10-06 17:57:52 -0700915 vector<uint64_t> remove_extents_expanded =
916 ExpandExtents(remove_extents);
917 vector<uint64_t> replace_extents_expanded =
918 ExpandExtents(replace_extents);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700919 CHECK_EQ(remove_extents_expanded.size(), replace_extents_expanded.size());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700920 map<uint64_t, uint64_t> conversion;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700921 for (vector<uint64_t>::size_type i = 0;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700922 i < replace_extents_expanded.size(); i++) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700923 conversion[remove_extents_expanded[i]] = replace_extents_expanded[i];
924 }
925 utils::ApplyMap(&read_blocks, conversion);
926 for (Vertex::EdgeMap::iterator it = vertex->out_edges.begin(),
927 e = vertex->out_edges.end(); it != e; ++it) {
928 vector<uint64_t> write_before_deps_expanded =
929 ExpandExtents(it->second.write_extents);
930 utils::ApplyMap(&write_before_deps_expanded, conversion);
931 it->second.write_extents = CompressExtents(write_before_deps_expanded);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700932 }
933 }
934 // Convert read_blocks back to extents
Andrew de los Reyesef017552010-10-06 17:57:52 -0700935 vertex->op.clear_src_extents();
936 vector<Extent> new_extents = CompressExtents(read_blocks);
937 DeltaDiffGenerator::StoreExtents(new_extents,
938 vertex->op.mutable_src_extents());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700939}
940
941bool DeltaDiffGenerator::CutEdges(Graph* graph,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700942 const set<Edge>& edges,
943 vector<CutEdgeVertexes>* out_cuts) {
944 DummyExtentAllocator scratch_allocator;
945 vector<CutEdgeVertexes> cuts;
946 cuts.reserve(edges.size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700947
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700948 uint64_t scratch_blocks_used = 0;
949 for (set<Edge>::const_iterator it = edges.begin();
950 it != edges.end(); ++it) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700951 cuts.resize(cuts.size() + 1);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700952 vector<Extent> old_extents =
953 (*graph)[it->first].out_edges[it->second].extents;
954 // Choose some scratch space
955 scratch_blocks_used += graph_utils::EdgeWeight(*graph, *it);
Andrew de los Reyesef017552010-10-06 17:57:52 -0700956 cuts.back().tmp_extents =
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700957 scratch_allocator.Allocate(graph_utils::EdgeWeight(*graph, *it));
958 // create vertex to copy original->scratch
Andrew de los Reyesef017552010-10-06 17:57:52 -0700959 cuts.back().new_vertex = graph->size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700960 graph->resize(graph->size() + 1);
Andrew de los Reyesef017552010-10-06 17:57:52 -0700961 cuts.back().old_src = it->first;
962 cuts.back().old_dst = it->second;
Darin Petkov36a58222010-10-07 22:00:09 -0700963
Andrew de los Reyesef017552010-10-06 17:57:52 -0700964 EdgeProperties& cut_edge_properties =
965 (*graph)[it->first].out_edges.find(it->second)->second;
966
967 // This should never happen, as we should only be cutting edges between
968 // real file nodes, and write-before relationships are created from
969 // a real file node to a temp copy node:
970 CHECK(cut_edge_properties.write_extents.empty())
971 << "Can't cut edge that has write-before relationship.";
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700972
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700973 // make node depend on the copy operation
974 (*graph)[it->first].out_edges.insert(make_pair(graph->size() - 1,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700975 cut_edge_properties));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700976
977 // Set src/dst extents and other proto variables for copy operation
978 graph->back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
979 DeltaDiffGenerator::StoreExtents(
Andrew de los Reyesef017552010-10-06 17:57:52 -0700980 cut_edge_properties.extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700981 graph->back().op.mutable_src_extents());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700982 DeltaDiffGenerator::StoreExtents(cuts.back().tmp_extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700983 graph->back().op.mutable_dst_extents());
984 graph->back().op.set_src_length(
985 graph_utils::EdgeWeight(*graph, *it) * kBlockSize);
986 graph->back().op.set_dst_length(graph->back().op.src_length());
987
988 // make the dest node read from the scratch space
989 DeltaDiffGenerator::SubstituteBlocks(
Andrew de los Reyesef017552010-10-06 17:57:52 -0700990 &((*graph)[it->second]),
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700991 (*graph)[it->first].out_edges[it->second].extents,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700992 cuts.back().tmp_extents);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700993
994 // delete the old edge
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500995 CHECK_EQ(static_cast<Graph::size_type>(1),
996 (*graph)[it->first].out_edges.erase(it->second));
Chris Masone790e62e2010-08-12 10:41:18 -0700997
Andrew de los Reyesd12784c2010-07-26 13:55:14 -0700998 // Add an edge from dst to copy operation
Andrew de los Reyesef017552010-10-06 17:57:52 -0700999 EdgeProperties write_before_edge_properties;
1000 write_before_edge_properties.write_extents = cuts.back().tmp_extents;
1001 (*graph)[it->second].out_edges.insert(
1002 make_pair(graph->size() - 1, write_before_edge_properties));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001003 }
Andrew de los Reyesef017552010-10-06 17:57:52 -07001004 out_cuts->swap(cuts);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001005 return true;
1006}
1007
1008// Stores all Extents in 'extents' into 'out'.
1009void DeltaDiffGenerator::StoreExtents(
Andrew de los Reyesef017552010-10-06 17:57:52 -07001010 const vector<Extent>& extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001011 google::protobuf::RepeatedPtrField<Extent>* out) {
1012 for (vector<Extent>::const_iterator it = extents.begin();
1013 it != extents.end(); ++it) {
1014 Extent* new_extent = out->Add();
1015 *new_extent = *it;
1016 }
1017}
1018
1019// Creates all the edges for the graph. Writers of a block point to
1020// readers of the same block. This is because for an edge A->B, B
1021// must complete before A executes.
1022void DeltaDiffGenerator::CreateEdges(Graph* graph,
1023 const vector<Block>& blocks) {
1024 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
1025 // Blocks with both a reader and writer get an edge
1026 if (blocks[i].reader == Vertex::kInvalidIndex ||
1027 blocks[i].writer == Vertex::kInvalidIndex)
1028 continue;
1029 // Don't have a node depend on itself
1030 if (blocks[i].reader == blocks[i].writer)
1031 continue;
1032 // See if there's already an edge we can add onto
1033 Vertex::EdgeMap::iterator edge_it =
1034 (*graph)[blocks[i].writer].out_edges.find(blocks[i].reader);
1035 if (edge_it == (*graph)[blocks[i].writer].out_edges.end()) {
1036 // No existing edge. Create one
1037 (*graph)[blocks[i].writer].out_edges.insert(
1038 make_pair(blocks[i].reader, EdgeProperties()));
1039 edge_it = (*graph)[blocks[i].writer].out_edges.find(blocks[i].reader);
Chris Masone790e62e2010-08-12 10:41:18 -07001040 CHECK(edge_it != (*graph)[blocks[i].writer].out_edges.end());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001041 }
1042 graph_utils::AppendBlockToExtents(&edge_it->second.extents, i);
1043 }
1044}
1045
Andrew de los Reyesef017552010-10-06 17:57:52 -07001046namespace {
1047
1048class SortCutsByTopoOrderLess {
1049 public:
Alex Deymo923d8fa2014-07-15 17:58:51 -07001050 explicit SortCutsByTopoOrderLess(
1051 const vector<vector<Vertex::Index>::size_type>& table)
Andrew de los Reyesef017552010-10-06 17:57:52 -07001052 : table_(table) {}
1053 bool operator()(const CutEdgeVertexes& a, const CutEdgeVertexes& b) {
1054 return table_[a.old_dst] < table_[b.old_dst];
1055 }
1056 private:
Alex Deymo923d8fa2014-07-15 17:58:51 -07001057 const vector<vector<Vertex::Index>::size_type>& table_;
Andrew de los Reyesef017552010-10-06 17:57:52 -07001058};
1059
Alex Vakulenkod2779df2014-06-16 13:19:00 -07001060} // namespace
Andrew de los Reyesef017552010-10-06 17:57:52 -07001061
1062void DeltaDiffGenerator::GenerateReverseTopoOrderMap(
Alex Deymo923d8fa2014-07-15 17:58:51 -07001063 const vector<Vertex::Index>& op_indexes,
Andrew de los Reyesef017552010-10-06 17:57:52 -07001064 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes) {
1065 vector<vector<Vertex::Index>::size_type> table(op_indexes.size());
1066 for (vector<Vertex::Index>::size_type i = 0, e = op_indexes.size();
1067 i != e; ++i) {
1068 Vertex::Index node = op_indexes[i];
1069 if (table.size() < (node + 1)) {
1070 table.resize(node + 1);
1071 }
1072 table[node] = i;
1073 }
1074 reverse_op_indexes->swap(table);
1075}
1076
Alex Deymo923d8fa2014-07-15 17:58:51 -07001077void DeltaDiffGenerator::SortCutsByTopoOrder(
1078 const vector<Vertex::Index>& op_indexes,
1079 vector<CutEdgeVertexes>* cuts) {
Andrew de los Reyesef017552010-10-06 17:57:52 -07001080 // first, make a reverse lookup table.
1081 vector<vector<Vertex::Index>::size_type> table;
1082 GenerateReverseTopoOrderMap(op_indexes, &table);
1083 SortCutsByTopoOrderLess less(table);
1084 sort(cuts->begin(), cuts->end(), less);
1085}
1086
1087void DeltaDiffGenerator::MoveFullOpsToBack(Graph* graph,
1088 vector<Vertex::Index>* op_indexes) {
1089 vector<Vertex::Index> ret;
1090 vector<Vertex::Index> full_ops;
1091 ret.reserve(op_indexes->size());
1092 for (vector<Vertex::Index>::size_type i = 0, e = op_indexes->size(); i != e;
1093 ++i) {
1094 DeltaArchiveManifest_InstallOperation_Type type =
1095 (*graph)[(*op_indexes)[i]].op.type();
1096 if (type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
1097 type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
1098 full_ops.push_back((*op_indexes)[i]);
1099 } else {
1100 ret.push_back((*op_indexes)[i]);
1101 }
1102 }
1103 LOG(INFO) << "Stats: " << full_ops.size() << " full ops out of "
1104 << (full_ops.size() + ret.size()) << " total ops.";
1105 ret.insert(ret.end(), full_ops.begin(), full_ops.end());
1106 op_indexes->swap(ret);
1107}
1108
1109namespace {
1110
1111template<typename T>
1112bool TempBlocksExistInExtents(const T& extents) {
1113 for (int i = 0, e = extents.size(); i < e; ++i) {
1114 Extent extent = graph_utils::GetElement(extents, i);
1115 uint64_t start = extent.start_block();
1116 uint64_t num = extent.num_blocks();
1117 if (start == kSparseHole)
1118 continue;
1119 if (start >= kTempBlockStart ||
1120 (start + num) >= kTempBlockStart) {
1121 LOG(ERROR) << "temp block!";
1122 LOG(ERROR) << "start: " << start << ", num: " << num;
1123 LOG(ERROR) << "kTempBlockStart: " << kTempBlockStart;
1124 LOG(ERROR) << "returning true";
1125 return true;
1126 }
1127 // check for wrap-around, which would be a bug:
1128 CHECK(start <= (start + num));
1129 }
1130 return false;
1131}
1132
Alex Vakulenko072359c2014-07-18 11:41:07 -07001133// Converts the cuts, which must all have the same |old_dst| member,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001134// to full. It does this by converting the |old_dst| to REPLACE or
1135// REPLACE_BZ, dropping all incoming edges to |old_dst|, and marking
1136// all temp nodes invalid.
1137bool ConvertCutsToFull(
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 set<Vertex::Index> deleted_nodes;
1147 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
1148 e = cuts.end(); it != e; ++it) {
1149 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ConvertCutToFullOp(
1150 graph,
1151 *it,
1152 new_root,
1153 data_fd,
1154 data_file_size));
1155 deleted_nodes.insert(it->new_vertex);
1156 }
1157 deleted_nodes.insert(cuts[0].old_dst);
Darin Petkovbc58a7b2010-11-03 11:52:53 -07001158
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001159 vector<Vertex::Index> new_op_indexes;
1160 new_op_indexes.reserve(op_indexes->size());
1161 for (vector<Vertex::Index>::iterator it = op_indexes->begin(),
1162 e = op_indexes->end(); it != e; ++it) {
1163 if (utils::SetContainsKey(deleted_nodes, *it))
1164 continue;
1165 new_op_indexes.push_back(*it);
1166 }
1167 new_op_indexes.push_back(cuts[0].old_dst);
1168 op_indexes->swap(new_op_indexes);
1169 DeltaDiffGenerator::GenerateReverseTopoOrderMap(*op_indexes,
1170 reverse_op_indexes);
1171 return true;
1172}
1173
1174// Tries to assign temp blocks for a collection of cuts, all of which share
1175// the same old_dst member. If temp blocks can't be found, old_dst will be
1176// converted to a REPLACE or REPLACE_BZ operation. Returns true on success,
1177// which can happen even if blocks are converted to full. Returns false
1178// on exceptional error cases.
1179bool AssignBlockForAdjoiningCuts(
1180 Graph* graph,
1181 const string& new_root,
1182 int data_fd,
1183 off_t* data_file_size,
1184 vector<Vertex::Index>* op_indexes,
1185 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
1186 const vector<CutEdgeVertexes>& cuts) {
1187 CHECK(!cuts.empty());
1188 const Vertex::Index old_dst = cuts[0].old_dst;
1189 // Calculate # of blocks needed
1190 uint64_t blocks_needed = 0;
1191 map<const CutEdgeVertexes*, uint64_t> cuts_blocks_needed;
1192 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
1193 e = cuts.end(); it != e; ++it) {
1194 uint64_t cut_blocks_needed = 0;
1195 for (vector<Extent>::const_iterator jt = it->tmp_extents.begin(),
1196 je = it->tmp_extents.end(); jt != je; ++jt) {
1197 cut_blocks_needed += jt->num_blocks();
1198 }
1199 blocks_needed += cut_blocks_needed;
1200 cuts_blocks_needed[&*it] = cut_blocks_needed;
1201 }
Darin Petkovbc58a7b2010-11-03 11:52:53 -07001202
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001203 // Find enough blocks
1204 ExtentRanges scratch_ranges;
1205 // Each block that's supplying temp blocks and the corresponding blocks:
Ben Chanf9cb98c2014-09-21 18:31:30 -07001206 typedef vector<pair<Vertex::Index, ExtentRanges>> SupplierVector;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001207 SupplierVector block_suppliers;
1208 uint64_t scratch_blocks_found = 0;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001209 for (vector<Vertex::Index>::size_type i = (*reverse_op_indexes)[old_dst] + 1,
1210 e = op_indexes->size(); i < e; ++i) {
1211 Vertex::Index test_node = (*op_indexes)[i];
1212 if (!(*graph)[test_node].valid)
1213 continue;
1214 // See if this node has sufficient blocks
1215 ExtentRanges ranges;
1216 ranges.AddRepeatedExtents((*graph)[test_node].op.dst_extents());
1217 ranges.SubtractExtent(ExtentForRange(
1218 kTempBlockStart, kSparseHole - kTempBlockStart));
1219 ranges.SubtractRepeatedExtents((*graph)[test_node].op.src_extents());
1220 // For now, for simplicity, subtract out all blocks in read-before
1221 // dependencies.
1222 for (Vertex::EdgeMap::const_iterator edge_i =
1223 (*graph)[test_node].out_edges.begin(),
1224 edge_e = (*graph)[test_node].out_edges.end();
1225 edge_i != edge_e; ++edge_i) {
1226 ranges.SubtractExtents(edge_i->second.extents);
1227 }
1228 if (ranges.blocks() == 0)
1229 continue;
1230
1231 if (ranges.blocks() + scratch_blocks_found > blocks_needed) {
1232 // trim down ranges
1233 vector<Extent> new_ranges = ranges.GetExtentsForBlockCount(
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001234 blocks_needed - scratch_blocks_found);
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001235 ranges = ExtentRanges();
1236 ranges.AddExtents(new_ranges);
1237 }
1238 scratch_ranges.AddRanges(ranges);
1239 block_suppliers.push_back(make_pair(test_node, ranges));
1240 scratch_blocks_found += ranges.blocks();
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001241 if (scratch_ranges.blocks() >= blocks_needed)
1242 break;
1243 }
1244 if (scratch_ranges.blocks() < blocks_needed) {
1245 LOG(INFO) << "Unable to find sufficient scratch";
1246 TEST_AND_RETURN_FALSE(ConvertCutsToFull(graph,
1247 new_root,
1248 data_fd,
1249 data_file_size,
1250 op_indexes,
1251 reverse_op_indexes,
1252 cuts));
1253 return true;
1254 }
1255 // Use the scratch we found
1256 TEST_AND_RETURN_FALSE(scratch_ranges.blocks() == scratch_blocks_found);
1257
1258 // Make all the suppliers depend on this node
1259 for (SupplierVector::iterator it = block_suppliers.begin(),
1260 e = block_suppliers.end(); it != e; ++it) {
1261 graph_utils::AddReadBeforeDepExtents(
1262 &(*graph)[it->first],
1263 old_dst,
1264 it->second.GetExtentsForBlockCount(it->second.blocks()));
1265 }
Darin Petkovbc58a7b2010-11-03 11:52:53 -07001266
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001267 // Replace temp blocks in each cut
1268 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
1269 e = cuts.end(); it != e; ++it) {
1270 vector<Extent> real_extents =
1271 scratch_ranges.GetExtentsForBlockCount(cuts_blocks_needed[&*it]);
1272 scratch_ranges.SubtractExtents(real_extents);
1273
1274 // Fix the old dest node w/ the real blocks
1275 DeltaDiffGenerator::SubstituteBlocks(&(*graph)[old_dst],
1276 it->tmp_extents,
1277 real_extents);
1278
1279 // Fix the new node w/ the real blocks. Since the new node is just a
1280 // copy operation, we can replace all the dest extents w/ the real
1281 // blocks.
1282 DeltaArchiveManifest_InstallOperation *op =
1283 &(*graph)[it->new_vertex].op;
1284 op->clear_dst_extents();
1285 DeltaDiffGenerator::StoreExtents(real_extents, op->mutable_dst_extents());
1286 }
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001287 return true;
1288}
1289
Alex Vakulenkod2779df2014-06-16 13:19:00 -07001290} // namespace
Andrew de los Reyesef017552010-10-06 17:57:52 -07001291
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001292// Returns true if |op| is a no-op operation that doesn't do any useful work
1293// (e.g., a move operation that copies blocks onto themselves).
1294bool DeltaDiffGenerator::IsNoopOperation(
1295 const DeltaArchiveManifest_InstallOperation& op) {
1296 return (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE &&
1297 ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
1298}
1299
Andrew de los Reyesef017552010-10-06 17:57:52 -07001300bool DeltaDiffGenerator::AssignTempBlocks(
1301 Graph* graph,
1302 const string& new_root,
1303 int data_fd,
1304 off_t* data_file_size,
1305 vector<Vertex::Index>* op_indexes,
1306 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001307 const vector<CutEdgeVertexes>& cuts) {
Andrew de los Reyesef017552010-10-06 17:57:52 -07001308 CHECK(!cuts.empty());
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001309
1310 // group of cuts w/ the same old_dst:
1311 vector<CutEdgeVertexes> cuts_group;
1312
Andrew de los Reyesef017552010-10-06 17:57:52 -07001313 for (vector<CutEdgeVertexes>::size_type i = cuts.size() - 1, e = 0;
1314 true ; --i) {
1315 LOG(INFO) << "Fixing temp blocks in cut " << i
1316 << ": old dst: " << cuts[i].old_dst << " new vertex: "
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001317 << cuts[i].new_vertex << " path: "
1318 << (*graph)[cuts[i].old_dst].file_name;
1319
1320 if (cuts_group.empty() || (cuts_group[0].old_dst == cuts[i].old_dst)) {
1321 cuts_group.push_back(cuts[i]);
1322 } else {
1323 CHECK(!cuts_group.empty());
1324 TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
1325 new_root,
1326 data_fd,
1327 data_file_size,
1328 op_indexes,
1329 reverse_op_indexes,
1330 cuts_group));
1331 cuts_group.clear();
1332 cuts_group.push_back(cuts[i]);
Andrew de los Reyesef017552010-10-06 17:57:52 -07001333 }
Darin Petkov36a58222010-10-07 22:00:09 -07001334
Andrew de los Reyesef017552010-10-06 17:57:52 -07001335 if (i == e) {
1336 // break out of for() loop
1337 break;
1338 }
1339 }
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001340 CHECK(!cuts_group.empty());
1341 TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
1342 new_root,
1343 data_fd,
1344 data_file_size,
1345 op_indexes,
1346 reverse_op_indexes,
1347 cuts_group));
Andrew de los Reyesef017552010-10-06 17:57:52 -07001348 return true;
1349}
1350
1351bool DeltaDiffGenerator::NoTempBlocksRemain(const Graph& graph) {
1352 size_t idx = 0;
1353 for (Graph::const_iterator it = graph.begin(), e = graph.end(); it != e;
1354 ++it, ++idx) {
1355 if (!it->valid)
1356 continue;
1357 const DeltaArchiveManifest_InstallOperation& op = it->op;
1358 if (TempBlocksExistInExtents(op.dst_extents()) ||
1359 TempBlocksExistInExtents(op.src_extents())) {
1360 LOG(INFO) << "bad extents in node " << idx;
1361 LOG(INFO) << "so yeah";
1362 return false;
1363 }
1364
1365 // Check out-edges:
1366 for (Vertex::EdgeMap::const_iterator jt = it->out_edges.begin(),
1367 je = it->out_edges.end(); jt != je; ++jt) {
1368 if (TempBlocksExistInExtents(jt->second.extents) ||
1369 TempBlocksExistInExtents(jt->second.write_extents)) {
1370 LOG(INFO) << "bad out edge in node " << idx;
1371 LOG(INFO) << "so yeah";
1372 return false;
1373 }
1374 }
1375 }
1376 return true;
1377}
1378
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001379bool DeltaDiffGenerator::ReorderDataBlobs(
1380 DeltaArchiveManifest* manifest,
1381 const std::string& data_blobs_path,
1382 const std::string& new_data_blobs_path) {
1383 int in_fd = open(data_blobs_path.c_str(), O_RDONLY, 0);
1384 TEST_AND_RETURN_FALSE_ERRNO(in_fd >= 0);
1385 ScopedFdCloser in_fd_closer(&in_fd);
Chris Masone790e62e2010-08-12 10:41:18 -07001386
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001387 DirectFileWriter writer;
1388 TEST_AND_RETURN_FALSE(
1389 writer.Open(new_data_blobs_path.c_str(),
1390 O_WRONLY | O_TRUNC | O_CREAT,
1391 0644) == 0);
1392 ScopedFileWriterCloser writer_closer(&writer);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001393 uint64_t out_file_size = 0;
Chris Masone790e62e2010-08-12 10:41:18 -07001394
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001395 for (int i = 0; i < (manifest->install_operations_size() +
1396 manifest->kernel_install_operations_size()); i++) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -07001397 DeltaArchiveManifest_InstallOperation* op = nullptr;
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001398 if (i < manifest->install_operations_size()) {
1399 op = manifest->mutable_install_operations(i);
1400 } else {
1401 op = manifest->mutable_kernel_install_operations(
1402 i - manifest->install_operations_size());
1403 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001404 if (!op->has_data_offset())
1405 continue;
1406 CHECK(op->has_data_length());
1407 vector<char> buf(op->data_length());
1408 ssize_t rc = pread(in_fd, &buf[0], buf.size(), op->data_offset());
1409 TEST_AND_RETURN_FALSE(rc == static_cast<ssize_t>(buf.size()));
1410
Jay Srinivasan00f76b62012-09-17 18:48:36 -07001411 // Add the hash of the data blobs for this operation
1412 TEST_AND_RETURN_FALSE(AddOperationHash(op, buf));
1413
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001414 op->set_data_offset(out_file_size);
Don Garrette410e0f2011-11-10 15:39:01 -08001415 TEST_AND_RETURN_FALSE(writer.Write(&buf[0], buf.size()));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001416 out_file_size += buf.size();
1417 }
1418 return true;
1419}
1420
Jay Srinivasan00f76b62012-09-17 18:48:36 -07001421bool DeltaDiffGenerator::AddOperationHash(
1422 DeltaArchiveManifest_InstallOperation* op,
1423 const vector<char>& buf) {
1424 OmahaHashCalculator hasher;
1425
1426 TEST_AND_RETURN_FALSE(hasher.Update(&buf[0], buf.size()));
1427 TEST_AND_RETURN_FALSE(hasher.Finalize());
1428
1429 const vector<char>& hash = hasher.raw_hash();
1430 op->set_data_sha256_hash(hash.data(), hash.size());
1431 return true;
1432}
1433
Andrew de los Reyesef017552010-10-06 17:57:52 -07001434bool DeltaDiffGenerator::ConvertCutToFullOp(Graph* graph,
1435 const CutEdgeVertexes& cut,
1436 const string& new_root,
1437 int data_fd,
1438 off_t* data_file_size) {
1439 // Drop all incoming edges, keep all outgoing edges
Darin Petkov36a58222010-10-07 22:00:09 -07001440
Andrew de los Reyesef017552010-10-06 17:57:52 -07001441 // Keep all outgoing edges
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001442 if ((*graph)[cut.old_dst].op.type() !=
1443 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ &&
1444 (*graph)[cut.old_dst].op.type() !=
1445 DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
1446 Vertex::EdgeMap out_edges = (*graph)[cut.old_dst].out_edges;
1447 graph_utils::DropWriteBeforeDeps(&out_edges);
Darin Petkov36a58222010-10-07 22:00:09 -07001448
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001449 TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
1450 cut.old_dst,
Alex Vakulenko88b591f2014-08-28 16:48:57 -07001451 nullptr,
Alex Deymo923d8fa2014-07-15 17:58:51 -07001452 kEmptyPath,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001453 new_root,
1454 (*graph)[cut.old_dst].file_name,
Darin Petkov8e447e02013-04-16 16:23:50 +02001455 (*graph)[cut.old_dst].chunk_offset,
1456 (*graph)[cut.old_dst].chunk_size,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001457 data_fd,
1458 data_file_size));
Darin Petkov36a58222010-10-07 22:00:09 -07001459
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001460 (*graph)[cut.old_dst].out_edges = out_edges;
Andrew de los Reyesef017552010-10-06 17:57:52 -07001461
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001462 // Right now we don't have doubly-linked edges, so we have to scan
1463 // the whole graph.
1464 graph_utils::DropIncomingEdgesTo(graph, cut.old_dst);
1465 }
Andrew de los Reyesef017552010-10-06 17:57:52 -07001466
1467 // Delete temp node
1468 (*graph)[cut.old_src].out_edges.erase(cut.new_vertex);
1469 CHECK((*graph)[cut.old_dst].out_edges.find(cut.new_vertex) ==
1470 (*graph)[cut.old_dst].out_edges.end());
1471 (*graph)[cut.new_vertex].valid = false;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001472 LOG(INFO) << "marked node invalid: " << cut.new_vertex;
Andrew de los Reyesef017552010-10-06 17:57:52 -07001473 return true;
1474}
1475
1476bool DeltaDiffGenerator::ConvertGraphToDag(Graph* graph,
1477 const string& new_root,
1478 int fd,
1479 off_t* data_file_size,
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001480 vector<Vertex::Index>* final_order,
1481 Vertex::Index scratch_vertex) {
Andrew de los Reyesef017552010-10-06 17:57:52 -07001482 CycleBreaker cycle_breaker;
1483 LOG(INFO) << "Finding cycles...";
1484 set<Edge> cut_edges;
1485 cycle_breaker.BreakCycles(*graph, &cut_edges);
1486 LOG(INFO) << "done finding cycles";
1487 CheckGraph(*graph);
1488
1489 // Calculate number of scratch blocks needed
1490
1491 LOG(INFO) << "Cutting cycles...";
1492 vector<CutEdgeVertexes> cuts;
1493 TEST_AND_RETURN_FALSE(CutEdges(graph, cut_edges, &cuts));
1494 LOG(INFO) << "done cutting cycles";
1495 LOG(INFO) << "There are " << cuts.size() << " cuts.";
1496 CheckGraph(*graph);
1497
1498 LOG(INFO) << "Creating initial topological order...";
1499 TopologicalSort(*graph, final_order);
1500 LOG(INFO) << "done with initial topo order";
1501 CheckGraph(*graph);
1502
1503 LOG(INFO) << "Moving full ops to the back";
1504 MoveFullOpsToBack(graph, final_order);
1505 LOG(INFO) << "done moving full ops to back";
1506
1507 vector<vector<Vertex::Index>::size_type> inverse_final_order;
1508 GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
1509
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001510 SortCutsByTopoOrder(*final_order, &cuts);
1511
Andrew de los Reyesef017552010-10-06 17:57:52 -07001512 if (!cuts.empty())
1513 TEST_AND_RETURN_FALSE(AssignTempBlocks(graph,
1514 new_root,
1515 fd,
1516 data_file_size,
1517 final_order,
1518 &inverse_final_order,
1519 cuts));
1520 LOG(INFO) << "Making sure all temp blocks have been allocated";
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001521
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001522 // Remove the scratch node, if any
1523 if (scratch_vertex != Vertex::kInvalidIndex) {
1524 final_order->erase(final_order->begin() +
1525 inverse_final_order[scratch_vertex]);
1526 (*graph)[scratch_vertex].valid = false;
1527 GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
1528 }
1529
Andrew de los Reyesef017552010-10-06 17:57:52 -07001530 graph_utils::DumpGraph(*graph);
1531 CHECK(NoTempBlocksRemain(*graph));
1532 LOG(INFO) << "done making sure all temp blocks are allocated";
1533 return true;
1534}
1535
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001536void DeltaDiffGenerator::CreateScratchNode(uint64_t start_block,
1537 uint64_t num_blocks,
1538 Vertex* vertex) {
1539 vertex->file_name = "<scratch>";
1540 vertex->op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
1541 vertex->op.set_data_offset(0);
1542 vertex->op.set_data_length(0);
1543 Extent* extent = vertex->op.add_dst_extents();
1544 extent->set_start_block(start_block);
1545 extent->set_num_blocks(num_blocks);
1546}
1547
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001548bool DeltaDiffGenerator::GenerateDeltaUpdateFile(
1549 const string& old_root,
1550 const string& old_image,
1551 const string& new_root,
1552 const string& new_image,
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001553 const string& old_kernel_part,
1554 const string& new_kernel_part,
1555 const string& output_path,
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001556 const string& private_key_path,
Darin Petkov8e447e02013-04-16 16:23:50 +02001557 off_t chunk_size,
Chris Sosad5ae1562013-04-23 13:20:18 -07001558 size_t rootfs_partition_size,
Don Garrett0dd39852013-04-03 16:55:42 -07001559 const ImageInfo* old_image_info,
1560 const ImageInfo* new_image_info,
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001561 uint64_t* metadata_size) {
Darin Petkov8e447e02013-04-16 16:23:50 +02001562 TEST_AND_RETURN_FALSE(chunk_size == -1 || chunk_size % kBlockSize == 0);
Darin Petkov7ea32332010-10-13 10:46:11 -07001563 int old_image_block_count = 0, old_image_block_size = 0;
1564 int new_image_block_count = 0, new_image_block_size = 0;
1565 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(new_image,
1566 &new_image_block_count,
1567 &new_image_block_size));
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001568 if (!old_image.empty()) {
Darin Petkov7ea32332010-10-13 10:46:11 -07001569 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(old_image,
1570 &old_image_block_count,
1571 &old_image_block_size));
1572 TEST_AND_RETURN_FALSE(old_image_block_size == new_image_block_size);
1573 LOG_IF(WARNING, old_image_block_count != new_image_block_count)
1574 << "Old and new images have different block counts.";
Don Garrett0dd39852013-04-03 16:55:42 -07001575
Don Garrett60fc59c2013-10-18 11:43:52 -07001576 // If new_image_info is present, old_image_info must be present.
Alex Deymo923d8fa2014-07-15 17:58:51 -07001577 TEST_AND_RETURN_FALSE(!old_image_info == !new_image_info);
Don Garrett0dd39852013-04-03 16:55:42 -07001578 } else {
1579 // old_image_info must not be present for a full update.
1580 TEST_AND_RETURN_FALSE(!old_image_info);
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001581 }
Chris Sosad5ae1562013-04-23 13:20:18 -07001582
1583 // Sanity checks for the partition size.
1584 TEST_AND_RETURN_FALSE(rootfs_partition_size % kBlockSize == 0);
Chris Sosae9f5f422013-05-17 16:11:10 -07001585 size_t fs_size = static_cast<size_t>(new_image_block_size) *
1586 new_image_block_count;
Chris Sosad5ae1562013-04-23 13:20:18 -07001587 LOG(INFO) << "Rootfs partition size: " << rootfs_partition_size;
1588 LOG(INFO) << "Actual filesystem size: " << fs_size;
1589 TEST_AND_RETURN_FALSE(rootfs_partition_size >= fs_size);
1590
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001591 // Sanity check kernel partition arg
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001592 TEST_AND_RETURN_FALSE(utils::FileSize(new_kernel_part) >= 0);
1593
Darin Petkov7ea32332010-10-13 10:46:11 -07001594 vector<Block> blocks(max(old_image_block_count, new_image_block_count));
1595 LOG(INFO) << "Invalid block index: " << Vertex::kInvalidIndex;
1596 LOG(INFO) << "Block count: " << blocks.size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001597 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
1598 CHECK(blocks[i].reader == Vertex::kInvalidIndex);
1599 CHECK(blocks[i].writer == Vertex::kInvalidIndex);
1600 }
1601 Graph graph;
1602 CheckGraph(graph);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001603
Gilad Arnolda6742b32014-01-11 00:18:34 -08001604 const string kTempFileTemplate("CrAU_temp_data.XXXXXX");
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001605 string temp_file_path;
Darin Petkov7438a5c2011-08-29 11:56:44 -07001606 scoped_ptr<ScopedPathUnlinker> temp_file_unlinker;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001607 off_t data_file_size = 0;
1608
1609 LOG(INFO) << "Reading files...";
1610
Don Garrettb8dd1d92013-11-22 17:40:02 -08001611 // Create empty protobuf Manifest object
1612 DeltaArchiveManifest manifest;
1613
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001614 vector<DeltaArchiveManifest_InstallOperation> kernel_ops;
1615
Andrew de los Reyesef017552010-10-06 17:57:52 -07001616 vector<Vertex::Index> final_order;
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001617 Vertex::Index scratch_vertex = Vertex::kInvalidIndex;
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001618 {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001619 int fd;
1620 TEST_AND_RETURN_FALSE(
1621 utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &fd));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001622 temp_file_unlinker.reset(new ScopedPathUnlinker(temp_file_path));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001623 TEST_AND_RETURN_FALSE(fd >= 0);
1624 ScopedFdCloser fd_closer(&fd);
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001625 if (!old_image.empty()) {
1626 // Delta update
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001627
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001628 TEST_AND_RETURN_FALSE(DeltaReadFiles(&graph,
1629 &blocks,
1630 old_root,
1631 new_root,
Darin Petkov8e447e02013-04-16 16:23:50 +02001632 chunk_size,
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001633 fd,
1634 &data_file_size));
1635 LOG(INFO) << "done reading normal files";
1636 CheckGraph(graph);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001637
Thieu Le5c7d9752010-12-15 16:09:28 -08001638 LOG(INFO) << "Starting metadata processing";
1639 TEST_AND_RETURN_FALSE(Metadata::DeltaReadMetadata(&graph,
1640 &blocks,
1641 old_image,
1642 new_image,
1643 fd,
1644 &data_file_size));
1645 LOG(INFO) << "Done metadata processing";
1646 CheckGraph(graph);
1647
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001648 graph.resize(graph.size() + 1);
1649 TEST_AND_RETURN_FALSE(ReadUnwrittenBlocks(blocks,
1650 fd,
1651 &data_file_size,
1652 new_image,
1653 &graph.back()));
1654
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001655 // Final scratch block (if there's space)
Chris Sosad5ae1562013-04-23 13:20:18 -07001656 if (blocks.size() < (rootfs_partition_size / kBlockSize)) {
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001657 scratch_vertex = graph.size();
1658 graph.resize(graph.size() + 1);
1659 CreateScratchNode(blocks.size(),
Chris Sosad5ae1562013-04-23 13:20:18 -07001660 (rootfs_partition_size / kBlockSize) - blocks.size(),
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001661 &graph.back());
1662 }
1663
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001664 // Read kernel partition
1665 TEST_AND_RETURN_FALSE(DeltaCompressKernelPartition(old_kernel_part,
1666 new_kernel_part,
1667 &kernel_ops,
1668 fd,
1669 &data_file_size));
1670
1671 LOG(INFO) << "done reading kernel";
1672 CheckGraph(graph);
1673
1674 LOG(INFO) << "Creating edges...";
1675 CreateEdges(&graph, blocks);
1676 LOG(INFO) << "Done creating edges";
1677 CheckGraph(graph);
1678
1679 TEST_AND_RETURN_FALSE(ConvertGraphToDag(&graph,
1680 new_root,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001681 fd,
1682 &data_file_size,
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001683 &final_order,
1684 scratch_vertex));
Don Garrettb8dd1d92013-11-22 17:40:02 -08001685
1686 // Set the minor version for this payload.
1687 LOG(INFO) << "Adding Delta Minor Version.";
1688 manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001689 } else {
1690 // Full update
Darin Petkov7ea32332010-10-13 10:46:11 -07001691 off_t new_image_size =
1692 static_cast<off_t>(new_image_block_count) * new_image_block_size;
Darin Petkov7a22d792010-11-08 14:10:00 -08001693 TEST_AND_RETURN_FALSE(FullUpdateGenerator::Run(&graph,
1694 new_kernel_part,
1695 new_image,
1696 new_image_size,
1697 fd,
1698 &data_file_size,
1699 kFullUpdateChunkSize,
1700 kBlockSize,
1701 &kernel_ops,
1702 &final_order));
Don Garrettb8dd1d92013-11-22 17:40:02 -08001703
1704 // Set the minor version for this payload.
1705 LOG(INFO) << "Adding Full Minor Version.";
1706 manifest.set_minor_version(DeltaPerformer::kFullPayloadMinorVersion);
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001707 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001708 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001709
Don Garrett0dd39852013-04-03 16:55:42 -07001710 if (old_image_info)
1711 *(manifest.mutable_old_image_info()) = *old_image_info;
1712
1713 if (new_image_info)
1714 *(manifest.mutable_new_image_info()) = *new_image_info;
1715
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001716 OperationNameMap op_name_map;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001717 CheckGraph(graph);
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001718 InstallOperationsToManifest(graph,
1719 final_order,
1720 kernel_ops,
1721 &manifest,
1722 &op_name_map);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001723 CheckGraph(graph);
1724 manifest.set_block_size(kBlockSize);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001725
1726 // Reorder the data blobs with the newly ordered manifest
1727 string ordered_blobs_path;
1728 TEST_AND_RETURN_FALSE(utils::MakeTempFile(
Gilad Arnolda6742b32014-01-11 00:18:34 -08001729 "CrAU_temp_data.ordered.XXXXXX",
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001730 &ordered_blobs_path,
Alex Vakulenko88b591f2014-08-28 16:48:57 -07001731 nullptr));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001732 ScopedPathUnlinker ordered_blobs_unlinker(ordered_blobs_path);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001733 TEST_AND_RETURN_FALSE(ReorderDataBlobs(&manifest,
1734 temp_file_path,
1735 ordered_blobs_path));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001736 temp_file_unlinker.reset();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001737
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001738 // Check that install op blobs are in order.
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001739 uint64_t next_blob_offset = 0;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001740 {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001741 for (int i = 0; i < (manifest.install_operations_size() +
1742 manifest.kernel_install_operations_size()); i++) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001743 DeltaArchiveManifest_InstallOperation* op =
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001744 i < manifest.install_operations_size() ?
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001745 manifest.mutable_install_operations(i) :
1746 manifest.mutable_kernel_install_operations(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001747 i - manifest.install_operations_size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001748 if (op->has_data_offset()) {
1749 if (op->data_offset() != next_blob_offset) {
1750 LOG(FATAL) << "bad blob offset! " << op->data_offset() << " != "
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001751 << next_blob_offset;
1752 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001753 next_blob_offset += op->data_length();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001754 }
1755 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001756 }
1757
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001758 // Signatures appear at the end of the blobs. Note the offset in the
1759 // manifest
1760 if (!private_key_path.empty()) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001761 uint64_t signature_blob_length = 0;
1762 TEST_AND_RETURN_FALSE(
Andrew de los Reyesc24e3f32011-08-30 15:45:20 -07001763 PayloadSigner::SignatureBlobLength(vector<string>(1, private_key_path),
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001764 &signature_blob_length));
Darin Petkov9574f7e2011-01-13 10:48:12 -08001765 AddSignatureOp(next_blob_offset, signature_blob_length, &manifest);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001766 }
1767
Darin Petkov36a58222010-10-07 22:00:09 -07001768 TEST_AND_RETURN_FALSE(InitializePartitionInfos(old_kernel_part,
1769 new_kernel_part,
1770 old_image,
1771 new_image,
1772 &manifest));
1773
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001774 // Serialize protobuf
1775 string serialized_manifest;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001776
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001777 CheckGraph(graph);
1778 TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest));
1779 CheckGraph(graph);
1780
1781 LOG(INFO) << "Writing final delta file header...";
1782 DirectFileWriter writer;
1783 TEST_AND_RETURN_FALSE_ERRNO(writer.Open(output_path.c_str(),
1784 O_WRONLY | O_CREAT | O_TRUNC,
1785 0644) == 0);
1786 ScopedFileWriterCloser writer_closer(&writer);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001787
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001788 // Write header
Don Garrette410e0f2011-11-10 15:39:01 -08001789 TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, strlen(kDeltaMagic)));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001790
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001791 // Write version number
1792 TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, kVersionNumber));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001793
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001794 // Write protobuf length
1795 TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer,
1796 serialized_manifest.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001797
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001798 // Write protobuf
1799 LOG(INFO) << "Writing final delta file protobuf... "
1800 << serialized_manifest.size();
1801 TEST_AND_RETURN_FALSE(writer.Write(serialized_manifest.data(),
Don Garrette410e0f2011-11-10 15:39:01 -08001802 serialized_manifest.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001803
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001804 // Append the data blobs
1805 LOG(INFO) << "Writing final delta file data blobs...";
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001806 int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001807 ScopedFdCloser blobs_fd_closer(&blobs_fd);
1808 TEST_AND_RETURN_FALSE(blobs_fd >= 0);
1809 for (;;) {
1810 char buf[kBlockSize];
1811 ssize_t rc = read(blobs_fd, buf, sizeof(buf));
1812 if (0 == rc) {
1813 // EOF
1814 break;
1815 }
1816 TEST_AND_RETURN_FALSE_ERRNO(rc > 0);
Don Garrette410e0f2011-11-10 15:39:01 -08001817 TEST_AND_RETURN_FALSE(writer.Write(buf, rc));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001818 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001819
1820 // Write signature blob.
1821 if (!private_key_path.empty()) {
1822 LOG(INFO) << "Signing the update...";
1823 vector<char> signature_blob;
Andrew de los Reyesc24e3f32011-08-30 15:45:20 -07001824 TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(
1825 output_path,
1826 vector<string>(1, private_key_path),
1827 &signature_blob));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001828 TEST_AND_RETURN_FALSE(writer.Write(&signature_blob[0],
Don Garrette410e0f2011-11-10 15:39:01 -08001829 signature_blob.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001830 }
1831
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001832 *metadata_size =
Darin Petkov95cf01f2010-10-12 14:59:13 -07001833 strlen(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001834 ReportPayloadUsage(manifest, *metadata_size, op_name_map);
Darin Petkov880335c2010-10-01 15:52:53 -07001835
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001836 LOG(INFO) << "All done. Successfully created delta file with "
1837 << "metadata size = " << *metadata_size;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001838 return true;
1839}
1840
Thieu Le5c7d9752010-12-15 16:09:28 -08001841// Runs the bsdiff tool on two files and returns the resulting delta in
1842// 'out'. Returns true on success.
1843bool DeltaDiffGenerator::BsdiffFiles(const string& old_file,
1844 const string& new_file,
1845 vector<char>* out) {
Gilad Arnolda6742b32014-01-11 00:18:34 -08001846 const string kPatchFile = "delta.patchXXXXXX";
Thieu Le5c7d9752010-12-15 16:09:28 -08001847 string patch_file_path;
1848
1849 TEST_AND_RETURN_FALSE(
Alex Vakulenko88b591f2014-08-28 16:48:57 -07001850 utils::MakeTempFile(kPatchFile, &patch_file_path, nullptr));
Thieu Le5c7d9752010-12-15 16:09:28 -08001851
1852 vector<string> cmd;
1853 cmd.push_back(kBsdiffPath);
1854 cmd.push_back(old_file);
1855 cmd.push_back(new_file);
1856 cmd.push_back(patch_file_path);
1857
1858 int rc = 1;
1859 vector<char> patch_file;
Alex Vakulenko88b591f2014-08-28 16:48:57 -07001860 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
Thieu Le5c7d9752010-12-15 16:09:28 -08001861 TEST_AND_RETURN_FALSE(rc == 0);
1862 TEST_AND_RETURN_FALSE(utils::ReadFile(patch_file_path, out));
1863 unlink(patch_file_path.c_str());
1864 return true;
1865}
1866
1867// The |blocks| vector contains a reader and writer for each block on the
1868// filesystem that's being in-place updated. We populate the reader/writer
1869// fields of |blocks| by calling this function.
1870// For each block in |operation| that is read or written, find that block
1871// in |blocks| and set the reader/writer field to the vertex passed.
1872// |graph| is not strictly necessary, but useful for printing out
1873// error messages.
1874bool DeltaDiffGenerator::AddInstallOpToBlocksVector(
1875 const DeltaArchiveManifest_InstallOperation& operation,
1876 const Graph& graph,
1877 Vertex::Index vertex,
1878 vector<Block>* blocks) {
1879 // See if this is already present.
1880 TEST_AND_RETURN_FALSE(operation.dst_extents_size() > 0);
1881
1882 enum BlockField { READER = 0, WRITER, BLOCK_FIELD_COUNT };
1883 for (int field = READER; field < BLOCK_FIELD_COUNT; field++) {
1884 const int extents_size =
1885 (field == READER) ? operation.src_extents_size() :
1886 operation.dst_extents_size();
1887 const char* past_participle = (field == READER) ? "read" : "written";
1888 const google::protobuf::RepeatedPtrField<Extent>& extents =
1889 (field == READER) ? operation.src_extents() : operation.dst_extents();
1890 Vertex::Index Block::*access_type =
1891 (field == READER) ? &Block::reader : &Block::writer;
1892
1893 for (int i = 0; i < extents_size; i++) {
1894 const Extent& extent = extents.Get(i);
1895 if (extent.start_block() == kSparseHole) {
1896 // Hole in sparse file. skip
1897 continue;
1898 }
1899 for (uint64_t block = extent.start_block();
1900 block < (extent.start_block() + extent.num_blocks()); block++) {
1901 if ((*blocks)[block].*access_type != Vertex::kInvalidIndex) {
1902 LOG(FATAL) << "Block " << block << " is already "
1903 << past_participle << " by "
1904 << (*blocks)[block].*access_type << "("
1905 << graph[(*blocks)[block].*access_type].file_name
1906 << ") and also " << vertex << "("
1907 << graph[vertex].file_name << ")";
1908 }
1909 (*blocks)[block].*access_type = vertex;
1910 }
1911 }
1912 }
1913 return true;
1914}
1915
Darin Petkov9574f7e2011-01-13 10:48:12 -08001916void DeltaDiffGenerator::AddSignatureOp(uint64_t signature_blob_offset,
1917 uint64_t signature_blob_length,
1918 DeltaArchiveManifest* manifest) {
1919 LOG(INFO) << "Making room for signature in file";
1920 manifest->set_signatures_offset(signature_blob_offset);
1921 LOG(INFO) << "set? " << manifest->has_signatures_offset();
1922 // Add a dummy op at the end to appease older clients
1923 DeltaArchiveManifest_InstallOperation* dummy_op =
1924 manifest->add_kernel_install_operations();
1925 dummy_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
1926 dummy_op->set_data_offset(signature_blob_offset);
1927 manifest->set_signatures_offset(signature_blob_offset);
1928 dummy_op->set_data_length(signature_blob_length);
1929 manifest->set_signatures_size(signature_blob_length);
1930 Extent* dummy_extent = dummy_op->add_dst_extents();
1931 // Tell the dummy op to write this data to a big sparse hole
1932 dummy_extent->set_start_block(kSparseHole);
1933 dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) /
1934 kBlockSize);
1935}
1936
Andrew de los Reyes50f36492010-11-01 13:57:12 -07001937const char* const kBsdiffPath = "bsdiff";
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001938
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001939}; // namespace chromeos_update_engine