blob: ee16454484ed767a9371c5d22804d5960b7ef60a [file] [log] [blame]
Don Garrettf4b28742012-03-27 20:48:06 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/delta_diff_generator.h"
Darin Petkov880335c2010-10-01 15:52:53 -07006
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07007#include <errno.h>
8#include <fcntl.h>
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07009#include <inttypes.h>
Darin Petkov880335c2010-10-01 15:52:53 -070010#include <sys/stat.h>
11#include <sys/types.h>
12
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070013#include <algorithm>
Andrew de los Reyesef017552010-10-06 17:57:52 -070014#include <map>
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070015#include <set>
16#include <string>
17#include <utility>
18#include <vector>
Darin Petkov880335c2010-10-01 15:52:53 -070019
Darin Petkov8e447e02013-04-16 16:23:50 +020020#include <base/file_path.h>
21#include <base/file_util.h>
Darin Petkov880335c2010-10-01 15:52:53 -070022#include <base/logging.h>
Darin Petkov7438a5c2011-08-29 11:56:44 -070023#include <base/memory/scoped_ptr.h>
Darin Petkov8e447e02013-04-16 16:23:50 +020024#include <base/string_number_conversions.h>
Darin Petkov880335c2010-10-01 15:52:53 -070025#include <base/string_util.h>
Mike Frysinger8155d082012-04-06 15:23:18 -040026#include <base/stringprintf.h>
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070027#include <bzlib.h>
Darin Petkov880335c2010-10-01 15:52:53 -070028
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070029#include "update_engine/bzip.h"
30#include "update_engine/cycle_breaker.h"
31#include "update_engine/extent_mapper.h"
Andrew de los Reyesef017552010-10-06 17:57:52 -070032#include "update_engine/extent_ranges.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070033#include "update_engine/file_writer.h"
34#include "update_engine/filesystem_iterator.h"
Darin Petkov7a22d792010-11-08 14:10:00 -080035#include "update_engine/full_update_generator.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070036#include "update_engine/graph_types.h"
37#include "update_engine/graph_utils.h"
Thieu Le5c7d9752010-12-15 16:09:28 -080038#include "update_engine/metadata.h"
Darin Petkov36a58222010-10-07 22:00:09 -070039#include "update_engine/omaha_hash_calculator.h"
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070040#include "update_engine/payload_signer.h"
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070041#include "update_engine/subprocess.h"
42#include "update_engine/topological_sort.h"
43#include "update_engine/update_metadata.pb.h"
44#include "update_engine/utils.h"
45
46using std::make_pair;
Andrew de los Reyesef017552010-10-06 17:57:52 -070047using std::map;
Andrew de los Reyes3270f742010-07-15 22:28:14 -070048using std::max;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070049using std::min;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -070050using std::pair;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070051using std::set;
52using std::string;
53using std::vector;
54
55namespace chromeos_update_engine {
56
57typedef DeltaDiffGenerator::Block Block;
Darin Petkov9fa7ec52010-10-18 11:45:23 -070058typedef map<const DeltaArchiveManifest_InstallOperation*,
Darin Petkov8e447e02013-04-16 16:23:50 +020059 string> OperationNameMap;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070060
Chris Sosad5ae1562013-04-23 13:20:18 -070061const size_t kRootFSPartitionSize = 1024 * 1024 * 1024; // bytes
62const uint64_t kVersionNumber = 1;
63const uint64_t kFullUpdateChunkSize = 1024 * 1024; // bytes
64
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070065namespace {
Andrew de los Reyes27f7d372010-10-07 11:26:07 -070066const size_t kBlockSize = 4096; // bytes
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -080067const string kNonexistentPath = "";
Andrew de los Reyes927179d2010-12-02 11:26:48 -080068
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070069
Darin Petkov68c10d12010-10-14 09:24:37 -070070static const char* kInstallOperationTypes[] = {
71 "REPLACE",
72 "REPLACE_BZ",
73 "MOVE",
74 "BSDIFF"
75};
76
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070077// Stores all Extents for a file into 'out'. Returns true on success.
78bool GatherExtents(const string& path,
Darin Petkov8e447e02013-04-16 16:23:50 +020079 off_t chunk_offset,
80 off_t chunk_size,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070081 google::protobuf::RepeatedPtrField<Extent>* out) {
82 vector<Extent> extents;
Darin Petkov8e447e02013-04-16 16:23:50 +020083 TEST_AND_RETURN_FALSE(
84 extent_mapper::ExtentsForFileChunkFibmap(
85 path, chunk_offset, chunk_size, &extents));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070086 DeltaDiffGenerator::StoreExtents(extents, out);
87 return true;
88}
89
Andrew de los Reyesef017552010-10-06 17:57:52 -070090// For a given regular file which must exist at new_root + path, and
91// may exist at old_root + path, creates a new InstallOperation and
92// adds it to the graph. Also, populates the |blocks| array as
93// necessary, if |blocks| is non-NULL. Also, writes the data
94// necessary to send the file down to the client into data_fd, which
95// has length *data_file_size. *data_file_size is updated
96// appropriately. If |existing_vertex| is no kInvalidIndex, use that
97// rather than allocating a new vertex. Returns true on success.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070098bool DeltaReadFile(Graph* graph,
Andrew de los Reyesef017552010-10-06 17:57:52 -070099 Vertex::Index existing_vertex,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700100 vector<Block>* blocks,
101 const string& old_root,
102 const string& new_root,
103 const string& path, // within new_root
Darin Petkov8e447e02013-04-16 16:23:50 +0200104 off_t chunk_offset,
105 off_t chunk_size,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700106 int data_fd,
107 off_t* data_file_size) {
108 vector<char> data;
109 DeltaArchiveManifest_InstallOperation operation;
110
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800111 string old_path = (old_root == kNonexistentPath) ? kNonexistentPath :
112 old_root + path;
113
Don Garrett1d787092013-03-11 18:07:28 -0700114 // If bsdiff breaks again, blacklist the problem file by using:
115 // bsdiff_allowed = (path != "/foo/bar")
Don Garrett36e60772012-03-29 10:31:20 -0700116 //
Don Garrett1d787092013-03-11 18:07:28 -0700117 // TODO(dgarrett): chromium-os:15274 connect this test to the command line.
Don Garrett36e60772012-03-29 10:31:20 -0700118 bool bsdiff_allowed = true;
Don Garrettf4b28742012-03-27 20:48:06 -0700119
Don Garrett36e60772012-03-29 10:31:20 -0700120 if (!bsdiff_allowed)
121 LOG(INFO) << "bsdiff blacklisting: " << path;
Don Garrettf4b28742012-03-27 20:48:06 -0700122
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800123 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ReadFileToDiff(old_path,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700124 new_root + path,
Darin Petkov8e447e02013-04-16 16:23:50 +0200125 chunk_offset,
126 chunk_size,
Don Garrett36e60772012-03-29 10:31:20 -0700127 bsdiff_allowed,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700128 &data,
Darin Petkov68c10d12010-10-14 09:24:37 -0700129 &operation,
130 true));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700131
132 // Write the data
133 if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) {
134 operation.set_data_offset(*data_file_size);
135 operation.set_data_length(data.size());
136 }
137
138 TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, &data[0], data.size()));
139 *data_file_size += data.size();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700140
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700141 // Now, insert into graph and blocks vector
Andrew de los Reyesef017552010-10-06 17:57:52 -0700142 Vertex::Index vertex = existing_vertex;
143 if (vertex == Vertex::kInvalidIndex) {
144 graph->resize(graph->size() + 1);
145 vertex = graph->size() - 1;
146 }
147 (*graph)[vertex].op = operation;
148 CHECK((*graph)[vertex].op.has_type());
149 (*graph)[vertex].file_name = path;
Darin Petkov8e447e02013-04-16 16:23:50 +0200150 (*graph)[vertex].chunk_offset = chunk_offset;
151 (*graph)[vertex].chunk_size = chunk_size;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700152
Andrew de los Reyesef017552010-10-06 17:57:52 -0700153 if (blocks)
Thieu Le5c7d9752010-12-15 16:09:28 -0800154 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::AddInstallOpToBlocksVector(
155 (*graph)[vertex].op,
156 *graph,
157 vertex,
158 blocks));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700159 return true;
160}
161
162// For each regular file within new_root, creates a node in the graph,
163// determines the best way to compress it (REPLACE, REPLACE_BZ, COPY, BSDIFF),
164// and writes any necessary data to the end of data_fd.
165bool DeltaReadFiles(Graph* graph,
166 vector<Block>* blocks,
167 const string& old_root,
168 const string& new_root,
Darin Petkov8e447e02013-04-16 16:23:50 +0200169 off_t chunk_size,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700170 int data_fd,
171 off_t* data_file_size) {
172 set<ino_t> visited_inodes;
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800173 set<ino_t> visited_src_inodes;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700174 for (FilesystemIterator fs_iter(new_root,
175 utils::SetWithValue<string>("/lost+found"));
176 !fs_iter.IsEnd(); fs_iter.Increment()) {
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800177 // We never diff symlinks (here, we check that dst file is not a symlink).
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700178 if (!S_ISREG(fs_iter.GetStat().st_mode))
179 continue;
180
181 // Make sure we visit each inode only once.
182 if (utils::SetContainsKey(visited_inodes, fs_iter.GetStat().st_ino))
183 continue;
184 visited_inodes.insert(fs_iter.GetStat().st_ino);
Darin Petkov8e447e02013-04-16 16:23:50 +0200185 off_t dst_size = fs_iter.GetStat().st_size;
186 if (dst_size == 0)
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700187 continue;
188
189 LOG(INFO) << "Encoding file " << fs_iter.GetPartialPath();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700190
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800191 // We can't visit each dst image inode more than once, as that would
192 // duplicate work. Here, we avoid visiting each source image inode
193 // more than once. Technically, we could have multiple operations
194 // that read the same blocks from the source image for diffing, but
195 // we choose not to to avoid complexity. Eventually we will move away
196 // from using a graph/cycle detection/etc to generate diffs, and at that
197 // time, it will be easy (non-complex) to have many operations read
198 // from the same source blocks. At that time, this code can die. -adlr
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800199 bool should_diff_from_source = false;
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800200 string src_path = old_root + fs_iter.GetPartialPath();
Andrew de los Reyes48a0a482011-02-22 15:32:11 -0800201 struct stat src_stbuf;
202 // We never diff symlinks (here, we check that src file is not a symlink).
203 if (0 == lstat(src_path.c_str(), &src_stbuf) &&
204 S_ISREG(src_stbuf.st_mode)) {
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -0800205 should_diff_from_source = !utils::SetContainsKey(visited_src_inodes,
206 src_stbuf.st_ino);
207 visited_src_inodes.insert(src_stbuf.st_ino);
208 }
209
Darin Petkov8e447e02013-04-16 16:23:50 +0200210 off_t size = chunk_size == -1 ? dst_size : chunk_size;
211 off_t step = size;
212 for (off_t offset = 0; offset < dst_size; offset += step) {
213 if (offset + size >= dst_size) {
214 size = -1; // Read through the end of the file.
215 }
216 TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
217 Vertex::kInvalidIndex,
218 blocks,
219 (should_diff_from_source ?
220 old_root :
221 kNonexistentPath),
222 new_root,
223 fs_iter.GetPartialPath(),
224 offset,
225 size,
226 data_fd,
227 data_file_size));
228 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700229 }
230 return true;
231}
232
Andrew de los Reyesef017552010-10-06 17:57:52 -0700233// This class allocates non-existent temp blocks, starting from
234// kTempBlockStart. Other code is responsible for converting these
235// temp blocks into real blocks, as the client can't read or write to
236// these blocks.
237class DummyExtentAllocator {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700238 public:
Andrew de los Reyesef017552010-10-06 17:57:52 -0700239 explicit DummyExtentAllocator()
240 : next_block_(kTempBlockStart) {}
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700241 vector<Extent> Allocate(const uint64_t block_count) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700242 vector<Extent> ret(1);
243 ret[0].set_start_block(next_block_);
244 ret[0].set_num_blocks(block_count);
245 next_block_ += block_count;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700246 return ret;
247 }
248 private:
Andrew de los Reyesef017552010-10-06 17:57:52 -0700249 uint64_t next_block_;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700250};
251
252// Reads blocks from image_path that are not yet marked as being written
253// in the blocks array. These blocks that remain are non-file-data blocks.
254// In the future we might consider intelligent diffing between this data
255// and data in the previous image, but for now we just bzip2 compress it
256// and include it in the update.
257// Creates a new node in the graph to write these blocks and writes the
258// appropriate blob to blobs_fd. Reads and updates blobs_length;
259bool ReadUnwrittenBlocks(const vector<Block>& blocks,
260 int blobs_fd,
261 off_t* blobs_length,
262 const string& image_path,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700263 Vertex* vertex) {
Darin Petkovabe7cc92010-10-08 12:29:32 -0700264 vertex->file_name = "<rootfs-non-file-data>";
265
Andrew de los Reyesef017552010-10-06 17:57:52 -0700266 DeltaArchiveManifest_InstallOperation* out_op = &vertex->op;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700267 int image_fd = open(image_path.c_str(), O_RDONLY, 000);
268 TEST_AND_RETURN_FALSE_ERRNO(image_fd >= 0);
269 ScopedFdCloser image_fd_closer(&image_fd);
270
271 string temp_file_path;
272 TEST_AND_RETURN_FALSE(utils::MakeTempFile("/tmp/CrAU_temp_data.XXXXXX",
273 &temp_file_path,
274 NULL));
275
276 FILE* file = fopen(temp_file_path.c_str(), "w");
277 TEST_AND_RETURN_FALSE(file);
278 int err = BZ_OK;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700279
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700280 BZFILE* bz_file = BZ2_bzWriteOpen(&err,
281 file,
282 9, // max compression
283 0, // verbosity
284 0); // default work factor
285 TEST_AND_RETURN_FALSE(err == BZ_OK);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700286
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700287 vector<Extent> extents;
288 vector<Block>::size_type block_count = 0;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700289
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700290 LOG(INFO) << "Appending left over blocks to extents";
291 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
292 if (blocks[i].writer != Vertex::kInvalidIndex)
293 continue;
Andrew de los Reyesef017552010-10-06 17:57:52 -0700294 if (blocks[i].reader != Vertex::kInvalidIndex) {
295 graph_utils::AddReadBeforeDep(vertex, blocks[i].reader, i);
296 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700297 graph_utils::AppendBlockToExtents(&extents, i);
298 block_count++;
299 }
300
301 // Code will handle 'buf' at any size that's a multiple of kBlockSize,
302 // so we arbitrarily set it to 1024 * kBlockSize.
303 vector<char> buf(1024 * kBlockSize);
304
305 LOG(INFO) << "Reading left over blocks";
306 vector<Block>::size_type blocks_copied_count = 0;
307
308 // For each extent in extents, write the data into BZ2_bzWrite which
309 // sends it to an output file.
310 // We use the temporary buffer 'buf' to hold the data, which may be
311 // smaller than the extent, so in that case we have to loop to get
312 // the extent's data (that's the inner while loop).
313 for (vector<Extent>::const_iterator it = extents.begin();
314 it != extents.end(); ++it) {
315 vector<Block>::size_type blocks_read = 0;
Andrew de los Reyes4b8740f2010-11-08 17:09:11 -0800316 float printed_progress = -1;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700317 while (blocks_read < it->num_blocks()) {
318 const int copy_block_cnt =
319 min(buf.size() / kBlockSize,
320 static_cast<vector<char>::size_type>(
321 it->num_blocks() - blocks_read));
322 ssize_t rc = pread(image_fd,
323 &buf[0],
324 copy_block_cnt * kBlockSize,
325 (it->start_block() + blocks_read) * kBlockSize);
326 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
327 TEST_AND_RETURN_FALSE(static_cast<size_t>(rc) ==
328 copy_block_cnt * kBlockSize);
329 BZ2_bzWrite(&err, bz_file, &buf[0], copy_block_cnt * kBlockSize);
330 TEST_AND_RETURN_FALSE(err == BZ_OK);
331 blocks_read += copy_block_cnt;
332 blocks_copied_count += copy_block_cnt;
Andrew de los Reyes4b8740f2010-11-08 17:09:11 -0800333 float current_progress =
334 static_cast<float>(blocks_copied_count) / block_count;
335 if (printed_progress + 0.1 < current_progress ||
336 blocks_copied_count == block_count) {
337 LOG(INFO) << "progress: " << current_progress;
338 printed_progress = current_progress;
339 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700340 }
341 }
342 BZ2_bzWriteClose(&err, bz_file, 0, NULL, NULL);
343 TEST_AND_RETURN_FALSE(err == BZ_OK);
344 bz_file = NULL;
345 TEST_AND_RETURN_FALSE_ERRNO(0 == fclose(file));
346 file = NULL;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700347
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700348 vector<char> compressed_data;
349 LOG(INFO) << "Reading compressed data off disk";
350 TEST_AND_RETURN_FALSE(utils::ReadFile(temp_file_path, &compressed_data));
351 TEST_AND_RETURN_FALSE(unlink(temp_file_path.c_str()) == 0);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700352
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700353 // Add node to graph to write these blocks
354 out_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
355 out_op->set_data_offset(*blobs_length);
356 out_op->set_data_length(compressed_data.size());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700357 LOG(INFO) << "Rootfs non-data blocks compressed take up "
358 << compressed_data.size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700359 *blobs_length += compressed_data.size();
360 out_op->set_dst_length(kBlockSize * block_count);
361 DeltaDiffGenerator::StoreExtents(extents, out_op->mutable_dst_extents());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700362
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700363 TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd,
364 &compressed_data[0],
365 compressed_data.size()));
366 LOG(INFO) << "done with extra blocks";
367 return true;
368}
369
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700370// Writes the uint64_t passed in in host-endian to the file as big-endian.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700371// Returns true on success.
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700372bool WriteUint64AsBigEndian(FileWriter* writer, const uint64_t value) {
373 uint64_t value_be = htobe64(value);
Don Garrette410e0f2011-11-10 15:39:01 -0800374 TEST_AND_RETURN_FALSE(writer->Write(&value_be, sizeof(value_be)));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700375 return true;
376}
377
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700378// Adds each operation from |graph| to |out_manifest| in the order specified by
379// |order| while building |out_op_name_map| with operation to name
380// mappings. Adds all |kernel_ops| to |out_manifest|. Filters out no-op
381// operations.
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700382void InstallOperationsToManifest(
383 const Graph& graph,
384 const vector<Vertex::Index>& order,
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700385 const vector<DeltaArchiveManifest_InstallOperation>& kernel_ops,
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700386 DeltaArchiveManifest* out_manifest,
387 OperationNameMap* out_op_name_map) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700388 for (vector<Vertex::Index>::const_iterator it = order.begin();
389 it != order.end(); ++it) {
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700390 const Vertex& vertex = graph[*it];
391 const DeltaArchiveManifest_InstallOperation& add_op = vertex.op;
392 if (DeltaDiffGenerator::IsNoopOperation(add_op)) {
393 continue;
394 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700395 DeltaArchiveManifest_InstallOperation* op =
396 out_manifest->add_install_operations();
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700397 *op = add_op;
Darin Petkov8e447e02013-04-16 16:23:50 +0200398 string name = vertex.file_name;
399 if (vertex.chunk_offset || vertex.chunk_size != -1) {
400 string offset = base::Int64ToString(vertex.chunk_offset);
401 if (vertex.chunk_size != -1) {
402 name += " [" + offset + ", " +
403 base::Int64ToString(vertex.chunk_offset + vertex.chunk_size - 1) +
404 "]";
405 } else {
406 name += " [" + offset + ", end]";
407 }
408 }
409 (*out_op_name_map)[op] = name;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700410 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700411 for (vector<DeltaArchiveManifest_InstallOperation>::const_iterator it =
412 kernel_ops.begin(); it != kernel_ops.end(); ++it) {
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700413 const DeltaArchiveManifest_InstallOperation& add_op = *it;
414 if (DeltaDiffGenerator::IsNoopOperation(add_op)) {
415 continue;
416 }
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700417 DeltaArchiveManifest_InstallOperation* op =
418 out_manifest->add_kernel_install_operations();
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700419 *op = add_op;
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700420 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700421}
422
423void CheckGraph(const Graph& graph) {
424 for (Graph::const_iterator it = graph.begin(); it != graph.end(); ++it) {
425 CHECK(it->op.has_type());
426 }
427}
428
Darin Petkov68c10d12010-10-14 09:24:37 -0700429// Delta compresses a kernel partition |new_kernel_part| with knowledge of the
430// old kernel partition |old_kernel_part|. If |old_kernel_part| is an empty
431// string, generates a full update of the partition.
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700432bool DeltaCompressKernelPartition(
433 const string& old_kernel_part,
434 const string& new_kernel_part,
435 vector<DeltaArchiveManifest_InstallOperation>* ops,
436 int blobs_fd,
437 off_t* blobs_length) {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700438 LOG(INFO) << "Delta compressing kernel partition...";
Darin Petkov68c10d12010-10-14 09:24:37 -0700439 LOG_IF(INFO, old_kernel_part.empty()) << "Generating full kernel update...";
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700440
441 // Add a new install operation
442 ops->resize(1);
443 DeltaArchiveManifest_InstallOperation* op = &(*ops)[0];
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700444
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700445 vector<char> data;
Don Garrett36e60772012-03-29 10:31:20 -0700446 TEST_AND_RETURN_FALSE(
447 DeltaDiffGenerator::ReadFileToDiff(old_kernel_part,
448 new_kernel_part,
Darin Petkov8e447e02013-04-16 16:23:50 +0200449 0, // chunk_offset
450 -1, // chunk_size
Don Garrett36e60772012-03-29 10:31:20 -0700451 true, // bsdiff_allowed
452 &data,
453 op,
454 false));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700455
Darin Petkov68c10d12010-10-14 09:24:37 -0700456 // Write the data
457 if (op->type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) {
458 op->set_data_offset(*blobs_length);
459 op->set_data_length(data.size());
460 }
Andrew de los Reyes36f37362010-09-03 09:20:04 -0700461
Darin Petkov68c10d12010-10-14 09:24:37 -0700462 TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd, &data[0], data.size()));
463 *blobs_length += data.size();
Andrew de los Reyes36f37362010-09-03 09:20:04 -0700464
Darin Petkov68c10d12010-10-14 09:24:37 -0700465 LOG(INFO) << "Done delta compressing kernel partition: "
466 << kInstallOperationTypes[op->type()];
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700467 return true;
468}
469
Darin Petkov880335c2010-10-01 15:52:53 -0700470struct DeltaObject {
471 DeltaObject(const string& in_name, const int in_type, const off_t in_size)
472 : name(in_name),
473 type(in_type),
474 size(in_size) {}
475 bool operator <(const DeltaObject& object) const {
Darin Petkovd43d6902010-10-14 11:17:50 -0700476 return (size != object.size) ? (size < object.size) : (name < object.name);
Darin Petkov880335c2010-10-01 15:52:53 -0700477 }
478 string name;
479 int type;
480 off_t size;
481};
482
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700483void ReportPayloadUsage(const DeltaArchiveManifest& manifest,
484 const int64_t manifest_metadata_size,
485 const OperationNameMap& op_name_map) {
Darin Petkov880335c2010-10-01 15:52:53 -0700486 vector<DeltaObject> objects;
487 off_t total_size = 0;
488
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700489 // Rootfs install operations.
490 for (int i = 0; i < manifest.install_operations_size(); ++i) {
491 const DeltaArchiveManifest_InstallOperation& op =
492 manifest.install_operations(i);
Darin Petkov8e447e02013-04-16 16:23:50 +0200493 objects.push_back(DeltaObject(op_name_map.find(&op)->second,
Darin Petkov9fa7ec52010-10-18 11:45:23 -0700494 op.type(),
495 op.data_length()));
496 total_size += op.data_length();
Darin Petkov880335c2010-10-01 15:52:53 -0700497 }
498
Darin Petkov880335c2010-10-01 15:52:53 -0700499 // Kernel install operations.
500 for (int i = 0; i < manifest.kernel_install_operations_size(); ++i) {
501 const DeltaArchiveManifest_InstallOperation& op =
502 manifest.kernel_install_operations(i);
503 objects.push_back(DeltaObject(StringPrintf("<kernel-operation-%d>", i),
504 op.type(),
505 op.data_length()));
506 total_size += op.data_length();
507 }
508
Darin Petkov95cf01f2010-10-12 14:59:13 -0700509 objects.push_back(DeltaObject("<manifest-metadata>",
510 -1,
511 manifest_metadata_size));
512 total_size += manifest_metadata_size;
513
Darin Petkov880335c2010-10-01 15:52:53 -0700514 std::sort(objects.begin(), objects.end());
515
516 static const char kFormatString[] = "%6.2f%% %10llu %-10s %s\n";
517 for (vector<DeltaObject>::const_iterator it = objects.begin();
518 it != objects.end(); ++it) {
519 const DeltaObject& object = *it;
520 fprintf(stderr, kFormatString,
521 object.size * 100.0 / total_size,
522 object.size,
Darin Petkov95cf01f2010-10-12 14:59:13 -0700523 object.type >= 0 ? kInstallOperationTypes[object.type] : "-",
Darin Petkov880335c2010-10-01 15:52:53 -0700524 object.name.c_str());
525 }
526 fprintf(stderr, kFormatString, 100.0, total_size, "", "<total>");
527}
528
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700529} // namespace {}
530
531bool DeltaDiffGenerator::ReadFileToDiff(
532 const string& old_filename,
533 const string& new_filename,
Darin Petkov8e447e02013-04-16 16:23:50 +0200534 off_t chunk_offset,
535 off_t chunk_size,
Don Garrett36e60772012-03-29 10:31:20 -0700536 bool bsdiff_allowed,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700537 vector<char>* out_data,
Darin Petkov68c10d12010-10-14 09:24:37 -0700538 DeltaArchiveManifest_InstallOperation* out_op,
539 bool gather_extents) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700540 // Read new data in
541 vector<char> new_data;
Darin Petkov8e447e02013-04-16 16:23:50 +0200542 TEST_AND_RETURN_FALSE(
543 utils::ReadFileChunk(new_filename, chunk_offset, chunk_size, &new_data));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700544
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700545 TEST_AND_RETURN_FALSE(!new_data.empty());
Darin Petkov8e447e02013-04-16 16:23:50 +0200546 TEST_AND_RETURN_FALSE(chunk_size == -1 ||
547 static_cast<off_t>(new_data.size()) <= chunk_size);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700548
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700549 vector<char> new_data_bz;
550 TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz));
551 CHECK(!new_data_bz.empty());
552
553 vector<char> data; // Data blob that will be written to delta file.
554
555 DeltaArchiveManifest_InstallOperation operation;
556 size_t current_best_size = 0;
557 if (new_data.size() <= new_data_bz.size()) {
558 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
559 current_best_size = new_data.size();
560 data = new_data;
561 } else {
562 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
563 current_best_size = new_data_bz.size();
564 data = new_data_bz;
565 }
566
567 // Do we have an original file to consider?
568 struct stat old_stbuf;
Don Garrettf4b28742012-03-27 20:48:06 -0700569 bool original = !old_filename.empty();
570 if (original && 0 != stat(old_filename.c_str(), &old_stbuf)) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700571 // If stat-ing the old file fails, it should be because it doesn't exist.
572 TEST_AND_RETURN_FALSE(errno == ENOTDIR || errno == ENOENT);
Don Garrettf4b28742012-03-27 20:48:06 -0700573 original = false;
Darin Petkov68c10d12010-10-14 09:24:37 -0700574 }
Don Garrettf4b28742012-03-27 20:48:06 -0700575
Darin Petkov8e447e02013-04-16 16:23:50 +0200576 vector<char> old_data;
Don Garrettf4b28742012-03-27 20:48:06 -0700577 if (original) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700578 // Read old data
Darin Petkov8e447e02013-04-16 16:23:50 +0200579 TEST_AND_RETURN_FALSE(
580 utils::ReadFileChunk(
581 old_filename, chunk_offset, chunk_size, &old_data));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700582 if (old_data == new_data) {
583 // No change in data.
584 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
585 current_best_size = 0;
586 data.clear();
Darin Petkov8e447e02013-04-16 16:23:50 +0200587 } else if (!old_data.empty() && bsdiff_allowed) {
Don Garrett36e60772012-03-29 10:31:20 -0700588 // If the source file is considered bsdiff safe (no bsdiff bugs
589 // triggered), see if BSDIFF encoding is smaller.
Darin Petkov8e447e02013-04-16 16:23:50 +0200590 FilePath old_chunk;
591 TEST_AND_RETURN_FALSE(file_util::CreateTemporaryFile(&old_chunk));
592 ScopedPathUnlinker old_unlinker(old_chunk.value());
593 TEST_AND_RETURN_FALSE(
594 utils::WriteFile(old_chunk.value().c_str(),
595 &old_data[0], old_data.size()));
596 FilePath new_chunk;
597 TEST_AND_RETURN_FALSE(file_util::CreateTemporaryFile(&new_chunk));
598 ScopedPathUnlinker new_unlinker(new_chunk.value());
599 TEST_AND_RETURN_FALSE(
600 utils::WriteFile(new_chunk.value().c_str(),
601 &new_data[0], new_data.size()));
602
Don Garrett36e60772012-03-29 10:31:20 -0700603 vector<char> bsdiff_delta;
604 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200605 BsdiffFiles(old_chunk.value(), new_chunk.value(), &bsdiff_delta));
Don Garrett36e60772012-03-29 10:31:20 -0700606 CHECK_GT(bsdiff_delta.size(), static_cast<vector<char>::size_type>(0));
607 if (bsdiff_delta.size() < current_best_size) {
608 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
609 current_best_size = bsdiff_delta.size();
610 data = bsdiff_delta;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700611 }
612 }
613 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700614
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700615 // Set parameters of the operations
616 CHECK_EQ(data.size(), current_best_size);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700617
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700618 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE ||
619 operation.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) {
Darin Petkov68c10d12010-10-14 09:24:37 -0700620 if (gather_extents) {
621 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200622 GatherExtents(old_filename,
623 chunk_offset,
624 chunk_size,
625 operation.mutable_src_extents()));
Darin Petkov68c10d12010-10-14 09:24:37 -0700626 } else {
627 Extent* src_extent = operation.add_src_extents();
628 src_extent->set_start_block(0);
629 src_extent->set_num_blocks(
630 (old_stbuf.st_size + kBlockSize - 1) / kBlockSize);
631 }
Darin Petkov8e447e02013-04-16 16:23:50 +0200632 operation.set_src_length(old_data.size());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700633 }
634
Darin Petkov68c10d12010-10-14 09:24:37 -0700635 if (gather_extents) {
636 TEST_AND_RETURN_FALSE(
Darin Petkov8e447e02013-04-16 16:23:50 +0200637 GatherExtents(new_filename,
638 chunk_offset,
639 chunk_size,
640 operation.mutable_dst_extents()));
Darin Petkov68c10d12010-10-14 09:24:37 -0700641 } else {
642 Extent* dst_extent = operation.add_dst_extents();
643 dst_extent->set_start_block(0);
644 dst_extent->set_num_blocks((new_data.size() + kBlockSize - 1) / kBlockSize);
645 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700646 operation.set_dst_length(new_data.size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700647
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700648 out_data->swap(data);
649 *out_op = operation;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700650
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700651 return true;
652}
653
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700654bool DeltaDiffGenerator::InitializePartitionInfo(bool is_kernel,
655 const string& partition,
656 PartitionInfo* info) {
Darin Petkov7ea32332010-10-13 10:46:11 -0700657 int64_t size = 0;
658 if (is_kernel) {
659 size = utils::FileSize(partition);
660 } else {
661 int block_count = 0, block_size = 0;
662 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(partition,
663 &block_count,
664 &block_size));
665 size = static_cast<int64_t>(block_count) * block_size;
666 }
667 TEST_AND_RETURN_FALSE(size > 0);
Darin Petkov36a58222010-10-07 22:00:09 -0700668 info->set_size(size);
669 OmahaHashCalculator hasher;
Darin Petkov7ea32332010-10-13 10:46:11 -0700670 TEST_AND_RETURN_FALSE(hasher.UpdateFile(partition, size) == size);
Darin Petkov36a58222010-10-07 22:00:09 -0700671 TEST_AND_RETURN_FALSE(hasher.Finalize());
672 const vector<char>& hash = hasher.raw_hash();
673 info->set_hash(hash.data(), hash.size());
Darin Petkovd43d6902010-10-14 11:17:50 -0700674 LOG(INFO) << partition << ": size=" << size << " hash=" << hasher.hash();
Darin Petkov36a58222010-10-07 22:00:09 -0700675 return true;
676}
677
678bool InitializePartitionInfos(const string& old_kernel,
679 const string& new_kernel,
680 const string& old_rootfs,
681 const string& new_rootfs,
682 DeltaArchiveManifest* manifest) {
Darin Petkovd43d6902010-10-14 11:17:50 -0700683 if (!old_kernel.empty()) {
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700684 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
685 true,
686 old_kernel,
687 manifest->mutable_old_kernel_info()));
Darin Petkovd43d6902010-10-14 11:17:50 -0700688 }
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700689 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
690 true,
691 new_kernel,
692 manifest->mutable_new_kernel_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700693 if (!old_rootfs.empty()) {
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700694 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
695 false,
696 old_rootfs,
697 manifest->mutable_old_rootfs_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700698 }
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700699 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
700 false,
701 new_rootfs,
702 manifest->mutable_new_rootfs_info()));
Darin Petkov36a58222010-10-07 22:00:09 -0700703 return true;
704}
705
Andrew de los Reyesef017552010-10-06 17:57:52 -0700706namespace {
707
708// Takes a collection (vector or RepeatedPtrField) of Extent and
709// returns a vector of the blocks referenced, in order.
710template<typename T>
711vector<uint64_t> ExpandExtents(const T& extents) {
712 vector<uint64_t> ret;
713 for (size_t i = 0, e = static_cast<size_t>(extents.size()); i != e; ++i) {
714 const Extent extent = graph_utils::GetElement(extents, i);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700715 if (extent.start_block() == kSparseHole) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700716 ret.resize(ret.size() + extent.num_blocks(), kSparseHole);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700717 } else {
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700718 for (uint64_t block = extent.start_block();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700719 block < (extent.start_block() + extent.num_blocks()); block++) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700720 ret.push_back(block);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700721 }
722 }
723 }
Andrew de los Reyesef017552010-10-06 17:57:52 -0700724 return ret;
725}
726
727// Takes a vector of blocks and returns an equivalent vector of Extent
728// objects.
729vector<Extent> CompressExtents(const vector<uint64_t>& blocks) {
730 vector<Extent> new_extents;
731 for (vector<uint64_t>::const_iterator it = blocks.begin(), e = blocks.end();
732 it != e; ++it) {
733 graph_utils::AppendBlockToExtents(&new_extents, *it);
734 }
735 return new_extents;
736}
737
738} // namespace {}
739
740void DeltaDiffGenerator::SubstituteBlocks(
741 Vertex* vertex,
742 const vector<Extent>& remove_extents,
743 const vector<Extent>& replace_extents) {
744 // First, expand out the blocks that op reads from
745 vector<uint64_t> read_blocks = ExpandExtents(vertex->op.src_extents());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700746 {
747 // Expand remove_extents and replace_extents
Andrew de los Reyesef017552010-10-06 17:57:52 -0700748 vector<uint64_t> remove_extents_expanded =
749 ExpandExtents(remove_extents);
750 vector<uint64_t> replace_extents_expanded =
751 ExpandExtents(replace_extents);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700752 CHECK_EQ(remove_extents_expanded.size(), replace_extents_expanded.size());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700753 map<uint64_t, uint64_t> conversion;
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700754 for (vector<uint64_t>::size_type i = 0;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700755 i < replace_extents_expanded.size(); i++) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700756 conversion[remove_extents_expanded[i]] = replace_extents_expanded[i];
757 }
758 utils::ApplyMap(&read_blocks, conversion);
759 for (Vertex::EdgeMap::iterator it = vertex->out_edges.begin(),
760 e = vertex->out_edges.end(); it != e; ++it) {
761 vector<uint64_t> write_before_deps_expanded =
762 ExpandExtents(it->second.write_extents);
763 utils::ApplyMap(&write_before_deps_expanded, conversion);
764 it->second.write_extents = CompressExtents(write_before_deps_expanded);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700765 }
766 }
767 // Convert read_blocks back to extents
Andrew de los Reyesef017552010-10-06 17:57:52 -0700768 vertex->op.clear_src_extents();
769 vector<Extent> new_extents = CompressExtents(read_blocks);
770 DeltaDiffGenerator::StoreExtents(new_extents,
771 vertex->op.mutable_src_extents());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700772}
773
774bool DeltaDiffGenerator::CutEdges(Graph* graph,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700775 const set<Edge>& edges,
776 vector<CutEdgeVertexes>* out_cuts) {
777 DummyExtentAllocator scratch_allocator;
778 vector<CutEdgeVertexes> cuts;
779 cuts.reserve(edges.size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700780
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700781 uint64_t scratch_blocks_used = 0;
782 for (set<Edge>::const_iterator it = edges.begin();
783 it != edges.end(); ++it) {
Andrew de los Reyesef017552010-10-06 17:57:52 -0700784 cuts.resize(cuts.size() + 1);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700785 vector<Extent> old_extents =
786 (*graph)[it->first].out_edges[it->second].extents;
787 // Choose some scratch space
788 scratch_blocks_used += graph_utils::EdgeWeight(*graph, *it);
Andrew de los Reyesef017552010-10-06 17:57:52 -0700789 cuts.back().tmp_extents =
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700790 scratch_allocator.Allocate(graph_utils::EdgeWeight(*graph, *it));
791 // create vertex to copy original->scratch
Andrew de los Reyesef017552010-10-06 17:57:52 -0700792 cuts.back().new_vertex = graph->size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700793 graph->resize(graph->size() + 1);
Andrew de los Reyesef017552010-10-06 17:57:52 -0700794 cuts.back().old_src = it->first;
795 cuts.back().old_dst = it->second;
Darin Petkov36a58222010-10-07 22:00:09 -0700796
Andrew de los Reyesef017552010-10-06 17:57:52 -0700797 EdgeProperties& cut_edge_properties =
798 (*graph)[it->first].out_edges.find(it->second)->second;
799
800 // This should never happen, as we should only be cutting edges between
801 // real file nodes, and write-before relationships are created from
802 // a real file node to a temp copy node:
803 CHECK(cut_edge_properties.write_extents.empty())
804 << "Can't cut edge that has write-before relationship.";
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700805
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700806 // make node depend on the copy operation
807 (*graph)[it->first].out_edges.insert(make_pair(graph->size() - 1,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700808 cut_edge_properties));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700809
810 // Set src/dst extents and other proto variables for copy operation
811 graph->back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
812 DeltaDiffGenerator::StoreExtents(
Andrew de los Reyesef017552010-10-06 17:57:52 -0700813 cut_edge_properties.extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700814 graph->back().op.mutable_src_extents());
Andrew de los Reyesef017552010-10-06 17:57:52 -0700815 DeltaDiffGenerator::StoreExtents(cuts.back().tmp_extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700816 graph->back().op.mutable_dst_extents());
817 graph->back().op.set_src_length(
818 graph_utils::EdgeWeight(*graph, *it) * kBlockSize);
819 graph->back().op.set_dst_length(graph->back().op.src_length());
820
821 // make the dest node read from the scratch space
822 DeltaDiffGenerator::SubstituteBlocks(
Andrew de los Reyesef017552010-10-06 17:57:52 -0700823 &((*graph)[it->second]),
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700824 (*graph)[it->first].out_edges[it->second].extents,
Andrew de los Reyesef017552010-10-06 17:57:52 -0700825 cuts.back().tmp_extents);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700826
827 // delete the old edge
Mike Frysinger0f9547d2012-02-16 12:11:37 -0500828 CHECK_EQ(static_cast<Graph::size_type>(1),
829 (*graph)[it->first].out_edges.erase(it->second));
Chris Masone790e62e2010-08-12 10:41:18 -0700830
Andrew de los Reyesd12784c2010-07-26 13:55:14 -0700831 // Add an edge from dst to copy operation
Andrew de los Reyesef017552010-10-06 17:57:52 -0700832 EdgeProperties write_before_edge_properties;
833 write_before_edge_properties.write_extents = cuts.back().tmp_extents;
834 (*graph)[it->second].out_edges.insert(
835 make_pair(graph->size() - 1, write_before_edge_properties));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700836 }
Andrew de los Reyesef017552010-10-06 17:57:52 -0700837 out_cuts->swap(cuts);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700838 return true;
839}
840
841// Stores all Extents in 'extents' into 'out'.
842void DeltaDiffGenerator::StoreExtents(
Andrew de los Reyesef017552010-10-06 17:57:52 -0700843 const vector<Extent>& extents,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700844 google::protobuf::RepeatedPtrField<Extent>* out) {
845 for (vector<Extent>::const_iterator it = extents.begin();
846 it != extents.end(); ++it) {
847 Extent* new_extent = out->Add();
848 *new_extent = *it;
849 }
850}
851
852// Creates all the edges for the graph. Writers of a block point to
853// readers of the same block. This is because for an edge A->B, B
854// must complete before A executes.
855void DeltaDiffGenerator::CreateEdges(Graph* graph,
856 const vector<Block>& blocks) {
857 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
858 // Blocks with both a reader and writer get an edge
859 if (blocks[i].reader == Vertex::kInvalidIndex ||
860 blocks[i].writer == Vertex::kInvalidIndex)
861 continue;
862 // Don't have a node depend on itself
863 if (blocks[i].reader == blocks[i].writer)
864 continue;
865 // See if there's already an edge we can add onto
866 Vertex::EdgeMap::iterator edge_it =
867 (*graph)[blocks[i].writer].out_edges.find(blocks[i].reader);
868 if (edge_it == (*graph)[blocks[i].writer].out_edges.end()) {
869 // No existing edge. Create one
870 (*graph)[blocks[i].writer].out_edges.insert(
871 make_pair(blocks[i].reader, EdgeProperties()));
872 edge_it = (*graph)[blocks[i].writer].out_edges.find(blocks[i].reader);
Chris Masone790e62e2010-08-12 10:41:18 -0700873 CHECK(edge_it != (*graph)[blocks[i].writer].out_edges.end());
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700874 }
875 graph_utils::AppendBlockToExtents(&edge_it->second.extents, i);
876 }
877}
878
Andrew de los Reyesef017552010-10-06 17:57:52 -0700879namespace {
880
881class SortCutsByTopoOrderLess {
882 public:
883 SortCutsByTopoOrderLess(vector<vector<Vertex::Index>::size_type>& table)
884 : table_(table) {}
885 bool operator()(const CutEdgeVertexes& a, const CutEdgeVertexes& b) {
886 return table_[a.old_dst] < table_[b.old_dst];
887 }
888 private:
889 vector<vector<Vertex::Index>::size_type>& table_;
890};
891
892} // namespace {}
893
894void DeltaDiffGenerator::GenerateReverseTopoOrderMap(
895 vector<Vertex::Index>& op_indexes,
896 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes) {
897 vector<vector<Vertex::Index>::size_type> table(op_indexes.size());
898 for (vector<Vertex::Index>::size_type i = 0, e = op_indexes.size();
899 i != e; ++i) {
900 Vertex::Index node = op_indexes[i];
901 if (table.size() < (node + 1)) {
902 table.resize(node + 1);
903 }
904 table[node] = i;
905 }
906 reverse_op_indexes->swap(table);
907}
908
909void DeltaDiffGenerator::SortCutsByTopoOrder(vector<Vertex::Index>& op_indexes,
910 vector<CutEdgeVertexes>* cuts) {
911 // first, make a reverse lookup table.
912 vector<vector<Vertex::Index>::size_type> table;
913 GenerateReverseTopoOrderMap(op_indexes, &table);
914 SortCutsByTopoOrderLess less(table);
915 sort(cuts->begin(), cuts->end(), less);
916}
917
918void DeltaDiffGenerator::MoveFullOpsToBack(Graph* graph,
919 vector<Vertex::Index>* op_indexes) {
920 vector<Vertex::Index> ret;
921 vector<Vertex::Index> full_ops;
922 ret.reserve(op_indexes->size());
923 for (vector<Vertex::Index>::size_type i = 0, e = op_indexes->size(); i != e;
924 ++i) {
925 DeltaArchiveManifest_InstallOperation_Type type =
926 (*graph)[(*op_indexes)[i]].op.type();
927 if (type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
928 type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
929 full_ops.push_back((*op_indexes)[i]);
930 } else {
931 ret.push_back((*op_indexes)[i]);
932 }
933 }
934 LOG(INFO) << "Stats: " << full_ops.size() << " full ops out of "
935 << (full_ops.size() + ret.size()) << " total ops.";
936 ret.insert(ret.end(), full_ops.begin(), full_ops.end());
937 op_indexes->swap(ret);
938}
939
940namespace {
941
942template<typename T>
943bool TempBlocksExistInExtents(const T& extents) {
944 for (int i = 0, e = extents.size(); i < e; ++i) {
945 Extent extent = graph_utils::GetElement(extents, i);
946 uint64_t start = extent.start_block();
947 uint64_t num = extent.num_blocks();
948 if (start == kSparseHole)
949 continue;
950 if (start >= kTempBlockStart ||
951 (start + num) >= kTempBlockStart) {
952 LOG(ERROR) << "temp block!";
953 LOG(ERROR) << "start: " << start << ", num: " << num;
954 LOG(ERROR) << "kTempBlockStart: " << kTempBlockStart;
955 LOG(ERROR) << "returning true";
956 return true;
957 }
958 // check for wrap-around, which would be a bug:
959 CHECK(start <= (start + num));
960 }
961 return false;
962}
963
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -0700964// Convertes the cuts, which must all have the same |old_dst| member,
965// to full. It does this by converting the |old_dst| to REPLACE or
966// REPLACE_BZ, dropping all incoming edges to |old_dst|, and marking
967// all temp nodes invalid.
968bool ConvertCutsToFull(
969 Graph* graph,
970 const string& new_root,
971 int data_fd,
972 off_t* data_file_size,
973 vector<Vertex::Index>* op_indexes,
974 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
975 const vector<CutEdgeVertexes>& cuts) {
976 CHECK(!cuts.empty());
977 set<Vertex::Index> deleted_nodes;
978 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
979 e = cuts.end(); it != e; ++it) {
980 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ConvertCutToFullOp(
981 graph,
982 *it,
983 new_root,
984 data_fd,
985 data_file_size));
986 deleted_nodes.insert(it->new_vertex);
987 }
988 deleted_nodes.insert(cuts[0].old_dst);
Darin Petkovbc58a7b2010-11-03 11:52:53 -0700989
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -0700990 vector<Vertex::Index> new_op_indexes;
991 new_op_indexes.reserve(op_indexes->size());
992 for (vector<Vertex::Index>::iterator it = op_indexes->begin(),
993 e = op_indexes->end(); it != e; ++it) {
994 if (utils::SetContainsKey(deleted_nodes, *it))
995 continue;
996 new_op_indexes.push_back(*it);
997 }
998 new_op_indexes.push_back(cuts[0].old_dst);
999 op_indexes->swap(new_op_indexes);
1000 DeltaDiffGenerator::GenerateReverseTopoOrderMap(*op_indexes,
1001 reverse_op_indexes);
1002 return true;
1003}
1004
1005// Tries to assign temp blocks for a collection of cuts, all of which share
1006// the same old_dst member. If temp blocks can't be found, old_dst will be
1007// converted to a REPLACE or REPLACE_BZ operation. Returns true on success,
1008// which can happen even if blocks are converted to full. Returns false
1009// on exceptional error cases.
1010bool AssignBlockForAdjoiningCuts(
1011 Graph* graph,
1012 const string& new_root,
1013 int data_fd,
1014 off_t* data_file_size,
1015 vector<Vertex::Index>* op_indexes,
1016 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
1017 const vector<CutEdgeVertexes>& cuts) {
1018 CHECK(!cuts.empty());
1019 const Vertex::Index old_dst = cuts[0].old_dst;
1020 // Calculate # of blocks needed
1021 uint64_t blocks_needed = 0;
1022 map<const CutEdgeVertexes*, uint64_t> cuts_blocks_needed;
1023 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
1024 e = cuts.end(); it != e; ++it) {
1025 uint64_t cut_blocks_needed = 0;
1026 for (vector<Extent>::const_iterator jt = it->tmp_extents.begin(),
1027 je = it->tmp_extents.end(); jt != je; ++jt) {
1028 cut_blocks_needed += jt->num_blocks();
1029 }
1030 blocks_needed += cut_blocks_needed;
1031 cuts_blocks_needed[&*it] = cut_blocks_needed;
1032 }
Darin Petkovbc58a7b2010-11-03 11:52:53 -07001033
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001034 // Find enough blocks
1035 ExtentRanges scratch_ranges;
1036 // Each block that's supplying temp blocks and the corresponding blocks:
1037 typedef vector<pair<Vertex::Index, ExtentRanges> > SupplierVector;
1038 SupplierVector block_suppliers;
1039 uint64_t scratch_blocks_found = 0;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001040 for (vector<Vertex::Index>::size_type i = (*reverse_op_indexes)[old_dst] + 1,
1041 e = op_indexes->size(); i < e; ++i) {
1042 Vertex::Index test_node = (*op_indexes)[i];
1043 if (!(*graph)[test_node].valid)
1044 continue;
1045 // See if this node has sufficient blocks
1046 ExtentRanges ranges;
1047 ranges.AddRepeatedExtents((*graph)[test_node].op.dst_extents());
1048 ranges.SubtractExtent(ExtentForRange(
1049 kTempBlockStart, kSparseHole - kTempBlockStart));
1050 ranges.SubtractRepeatedExtents((*graph)[test_node].op.src_extents());
1051 // For now, for simplicity, subtract out all blocks in read-before
1052 // dependencies.
1053 for (Vertex::EdgeMap::const_iterator edge_i =
1054 (*graph)[test_node].out_edges.begin(),
1055 edge_e = (*graph)[test_node].out_edges.end();
1056 edge_i != edge_e; ++edge_i) {
1057 ranges.SubtractExtents(edge_i->second.extents);
1058 }
1059 if (ranges.blocks() == 0)
1060 continue;
1061
1062 if (ranges.blocks() + scratch_blocks_found > blocks_needed) {
1063 // trim down ranges
1064 vector<Extent> new_ranges = ranges.GetExtentsForBlockCount(
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001065 blocks_needed - scratch_blocks_found);
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001066 ranges = ExtentRanges();
1067 ranges.AddExtents(new_ranges);
1068 }
1069 scratch_ranges.AddRanges(ranges);
1070 block_suppliers.push_back(make_pair(test_node, ranges));
1071 scratch_blocks_found += ranges.blocks();
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001072 if (scratch_ranges.blocks() >= blocks_needed)
1073 break;
1074 }
1075 if (scratch_ranges.blocks() < blocks_needed) {
1076 LOG(INFO) << "Unable to find sufficient scratch";
1077 TEST_AND_RETURN_FALSE(ConvertCutsToFull(graph,
1078 new_root,
1079 data_fd,
1080 data_file_size,
1081 op_indexes,
1082 reverse_op_indexes,
1083 cuts));
1084 return true;
1085 }
1086 // Use the scratch we found
1087 TEST_AND_RETURN_FALSE(scratch_ranges.blocks() == scratch_blocks_found);
1088
1089 // Make all the suppliers depend on this node
1090 for (SupplierVector::iterator it = block_suppliers.begin(),
1091 e = block_suppliers.end(); it != e; ++it) {
1092 graph_utils::AddReadBeforeDepExtents(
1093 &(*graph)[it->first],
1094 old_dst,
1095 it->second.GetExtentsForBlockCount(it->second.blocks()));
1096 }
Darin Petkovbc58a7b2010-11-03 11:52:53 -07001097
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001098 // Replace temp blocks in each cut
1099 for (vector<CutEdgeVertexes>::const_iterator it = cuts.begin(),
1100 e = cuts.end(); it != e; ++it) {
1101 vector<Extent> real_extents =
1102 scratch_ranges.GetExtentsForBlockCount(cuts_blocks_needed[&*it]);
1103 scratch_ranges.SubtractExtents(real_extents);
1104
1105 // Fix the old dest node w/ the real blocks
1106 DeltaDiffGenerator::SubstituteBlocks(&(*graph)[old_dst],
1107 it->tmp_extents,
1108 real_extents);
1109
1110 // Fix the new node w/ the real blocks. Since the new node is just a
1111 // copy operation, we can replace all the dest extents w/ the real
1112 // blocks.
1113 DeltaArchiveManifest_InstallOperation *op =
1114 &(*graph)[it->new_vertex].op;
1115 op->clear_dst_extents();
1116 DeltaDiffGenerator::StoreExtents(real_extents, op->mutable_dst_extents());
1117 }
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001118 return true;
1119}
1120
Andrew de los Reyesef017552010-10-06 17:57:52 -07001121} // namespace {}
1122
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001123// Returns true if |op| is a no-op operation that doesn't do any useful work
1124// (e.g., a move operation that copies blocks onto themselves).
1125bool DeltaDiffGenerator::IsNoopOperation(
1126 const DeltaArchiveManifest_InstallOperation& op) {
1127 return (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE &&
1128 ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
1129}
1130
Andrew de los Reyesef017552010-10-06 17:57:52 -07001131bool DeltaDiffGenerator::AssignTempBlocks(
1132 Graph* graph,
1133 const string& new_root,
1134 int data_fd,
1135 off_t* data_file_size,
1136 vector<Vertex::Index>* op_indexes,
1137 vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001138 const vector<CutEdgeVertexes>& cuts) {
Andrew de los Reyesef017552010-10-06 17:57:52 -07001139 CHECK(!cuts.empty());
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001140
1141 // group of cuts w/ the same old_dst:
1142 vector<CutEdgeVertexes> cuts_group;
1143
Andrew de los Reyesef017552010-10-06 17:57:52 -07001144 for (vector<CutEdgeVertexes>::size_type i = cuts.size() - 1, e = 0;
1145 true ; --i) {
1146 LOG(INFO) << "Fixing temp blocks in cut " << i
1147 << ": old dst: " << cuts[i].old_dst << " new vertex: "
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001148 << cuts[i].new_vertex << " path: "
1149 << (*graph)[cuts[i].old_dst].file_name;
1150
1151 if (cuts_group.empty() || (cuts_group[0].old_dst == cuts[i].old_dst)) {
1152 cuts_group.push_back(cuts[i]);
1153 } else {
1154 CHECK(!cuts_group.empty());
1155 TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
1156 new_root,
1157 data_fd,
1158 data_file_size,
1159 op_indexes,
1160 reverse_op_indexes,
1161 cuts_group));
1162 cuts_group.clear();
1163 cuts_group.push_back(cuts[i]);
Andrew de los Reyesef017552010-10-06 17:57:52 -07001164 }
Darin Petkov36a58222010-10-07 22:00:09 -07001165
Andrew de los Reyesef017552010-10-06 17:57:52 -07001166 if (i == e) {
1167 // break out of for() loop
1168 break;
1169 }
1170 }
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001171 CHECK(!cuts_group.empty());
1172 TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
1173 new_root,
1174 data_fd,
1175 data_file_size,
1176 op_indexes,
1177 reverse_op_indexes,
1178 cuts_group));
Andrew de los Reyesef017552010-10-06 17:57:52 -07001179 return true;
1180}
1181
1182bool DeltaDiffGenerator::NoTempBlocksRemain(const Graph& graph) {
1183 size_t idx = 0;
1184 for (Graph::const_iterator it = graph.begin(), e = graph.end(); it != e;
1185 ++it, ++idx) {
1186 if (!it->valid)
1187 continue;
1188 const DeltaArchiveManifest_InstallOperation& op = it->op;
1189 if (TempBlocksExistInExtents(op.dst_extents()) ||
1190 TempBlocksExistInExtents(op.src_extents())) {
1191 LOG(INFO) << "bad extents in node " << idx;
1192 LOG(INFO) << "so yeah";
1193 return false;
1194 }
1195
1196 // Check out-edges:
1197 for (Vertex::EdgeMap::const_iterator jt = it->out_edges.begin(),
1198 je = it->out_edges.end(); jt != je; ++jt) {
1199 if (TempBlocksExistInExtents(jt->second.extents) ||
1200 TempBlocksExistInExtents(jt->second.write_extents)) {
1201 LOG(INFO) << "bad out edge in node " << idx;
1202 LOG(INFO) << "so yeah";
1203 return false;
1204 }
1205 }
1206 }
1207 return true;
1208}
1209
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001210bool DeltaDiffGenerator::ReorderDataBlobs(
1211 DeltaArchiveManifest* manifest,
1212 const std::string& data_blobs_path,
1213 const std::string& new_data_blobs_path) {
1214 int in_fd = open(data_blobs_path.c_str(), O_RDONLY, 0);
1215 TEST_AND_RETURN_FALSE_ERRNO(in_fd >= 0);
1216 ScopedFdCloser in_fd_closer(&in_fd);
Chris Masone790e62e2010-08-12 10:41:18 -07001217
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001218 DirectFileWriter writer;
1219 TEST_AND_RETURN_FALSE(
1220 writer.Open(new_data_blobs_path.c_str(),
1221 O_WRONLY | O_TRUNC | O_CREAT,
1222 0644) == 0);
1223 ScopedFileWriterCloser writer_closer(&writer);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001224 uint64_t out_file_size = 0;
Chris Masone790e62e2010-08-12 10:41:18 -07001225
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001226 for (int i = 0; i < (manifest->install_operations_size() +
1227 manifest->kernel_install_operations_size()); i++) {
1228 DeltaArchiveManifest_InstallOperation* op = NULL;
1229 if (i < manifest->install_operations_size()) {
1230 op = manifest->mutable_install_operations(i);
1231 } else {
1232 op = manifest->mutable_kernel_install_operations(
1233 i - manifest->install_operations_size());
1234 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001235 if (!op->has_data_offset())
1236 continue;
1237 CHECK(op->has_data_length());
1238 vector<char> buf(op->data_length());
1239 ssize_t rc = pread(in_fd, &buf[0], buf.size(), op->data_offset());
1240 TEST_AND_RETURN_FALSE(rc == static_cast<ssize_t>(buf.size()));
1241
Jay Srinivasan00f76b62012-09-17 18:48:36 -07001242 // Add the hash of the data blobs for this operation
1243 TEST_AND_RETURN_FALSE(AddOperationHash(op, buf));
1244
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001245 op->set_data_offset(out_file_size);
Don Garrette410e0f2011-11-10 15:39:01 -08001246 TEST_AND_RETURN_FALSE(writer.Write(&buf[0], buf.size()));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001247 out_file_size += buf.size();
1248 }
1249 return true;
1250}
1251
Jay Srinivasan00f76b62012-09-17 18:48:36 -07001252bool DeltaDiffGenerator::AddOperationHash(
1253 DeltaArchiveManifest_InstallOperation* op,
1254 const vector<char>& buf) {
1255 OmahaHashCalculator hasher;
1256
1257 TEST_AND_RETURN_FALSE(hasher.Update(&buf[0], buf.size()));
1258 TEST_AND_RETURN_FALSE(hasher.Finalize());
1259
1260 const vector<char>& hash = hasher.raw_hash();
1261 op->set_data_sha256_hash(hash.data(), hash.size());
1262 return true;
1263}
1264
Andrew de los Reyesef017552010-10-06 17:57:52 -07001265bool DeltaDiffGenerator::ConvertCutToFullOp(Graph* graph,
1266 const CutEdgeVertexes& cut,
1267 const string& new_root,
1268 int data_fd,
1269 off_t* data_file_size) {
1270 // Drop all incoming edges, keep all outgoing edges
Darin Petkov36a58222010-10-07 22:00:09 -07001271
Andrew de los Reyesef017552010-10-06 17:57:52 -07001272 // Keep all outgoing edges
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001273 if ((*graph)[cut.old_dst].op.type() !=
1274 DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ &&
1275 (*graph)[cut.old_dst].op.type() !=
1276 DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
1277 Vertex::EdgeMap out_edges = (*graph)[cut.old_dst].out_edges;
1278 graph_utils::DropWriteBeforeDeps(&out_edges);
Darin Petkov36a58222010-10-07 22:00:09 -07001279
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001280 TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
1281 cut.old_dst,
1282 NULL,
Andrew de los Reyes29da8aa2011-02-15 13:34:57 -08001283 kNonexistentPath,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001284 new_root,
1285 (*graph)[cut.old_dst].file_name,
Darin Petkov8e447e02013-04-16 16:23:50 +02001286 (*graph)[cut.old_dst].chunk_offset,
1287 (*graph)[cut.old_dst].chunk_size,
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001288 data_fd,
1289 data_file_size));
Darin Petkov36a58222010-10-07 22:00:09 -07001290
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001291 (*graph)[cut.old_dst].out_edges = out_edges;
Andrew de los Reyesef017552010-10-06 17:57:52 -07001292
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001293 // Right now we don't have doubly-linked edges, so we have to scan
1294 // the whole graph.
1295 graph_utils::DropIncomingEdgesTo(graph, cut.old_dst);
1296 }
Andrew de los Reyesef017552010-10-06 17:57:52 -07001297
1298 // Delete temp node
1299 (*graph)[cut.old_src].out_edges.erase(cut.new_vertex);
1300 CHECK((*graph)[cut.old_dst].out_edges.find(cut.new_vertex) ==
1301 (*graph)[cut.old_dst].out_edges.end());
1302 (*graph)[cut.new_vertex].valid = false;
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001303 LOG(INFO) << "marked node invalid: " << cut.new_vertex;
Andrew de los Reyesef017552010-10-06 17:57:52 -07001304 return true;
1305}
1306
1307bool DeltaDiffGenerator::ConvertGraphToDag(Graph* graph,
1308 const string& new_root,
1309 int fd,
1310 off_t* data_file_size,
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001311 vector<Vertex::Index>* final_order,
1312 Vertex::Index scratch_vertex) {
Andrew de los Reyesef017552010-10-06 17:57:52 -07001313 CycleBreaker cycle_breaker;
1314 LOG(INFO) << "Finding cycles...";
1315 set<Edge> cut_edges;
1316 cycle_breaker.BreakCycles(*graph, &cut_edges);
1317 LOG(INFO) << "done finding cycles";
1318 CheckGraph(*graph);
1319
1320 // Calculate number of scratch blocks needed
1321
1322 LOG(INFO) << "Cutting cycles...";
1323 vector<CutEdgeVertexes> cuts;
1324 TEST_AND_RETURN_FALSE(CutEdges(graph, cut_edges, &cuts));
1325 LOG(INFO) << "done cutting cycles";
1326 LOG(INFO) << "There are " << cuts.size() << " cuts.";
1327 CheckGraph(*graph);
1328
1329 LOG(INFO) << "Creating initial topological order...";
1330 TopologicalSort(*graph, final_order);
1331 LOG(INFO) << "done with initial topo order";
1332 CheckGraph(*graph);
1333
1334 LOG(INFO) << "Moving full ops to the back";
1335 MoveFullOpsToBack(graph, final_order);
1336 LOG(INFO) << "done moving full ops to back";
1337
1338 vector<vector<Vertex::Index>::size_type> inverse_final_order;
1339 GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
1340
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001341 SortCutsByTopoOrder(*final_order, &cuts);
1342
Andrew de los Reyesef017552010-10-06 17:57:52 -07001343 if (!cuts.empty())
1344 TEST_AND_RETURN_FALSE(AssignTempBlocks(graph,
1345 new_root,
1346 fd,
1347 data_file_size,
1348 final_order,
1349 &inverse_final_order,
1350 cuts));
1351 LOG(INFO) << "Making sure all temp blocks have been allocated";
Andrew de los Reyes4ba850d2010-10-25 12:12:40 -07001352
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001353 // Remove the scratch node, if any
1354 if (scratch_vertex != Vertex::kInvalidIndex) {
1355 final_order->erase(final_order->begin() +
1356 inverse_final_order[scratch_vertex]);
1357 (*graph)[scratch_vertex].valid = false;
1358 GenerateReverseTopoOrderMap(*final_order, &inverse_final_order);
1359 }
1360
Andrew de los Reyesef017552010-10-06 17:57:52 -07001361 graph_utils::DumpGraph(*graph);
1362 CHECK(NoTempBlocksRemain(*graph));
1363 LOG(INFO) << "done making sure all temp blocks are allocated";
1364 return true;
1365}
1366
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001367void DeltaDiffGenerator::CreateScratchNode(uint64_t start_block,
1368 uint64_t num_blocks,
1369 Vertex* vertex) {
1370 vertex->file_name = "<scratch>";
1371 vertex->op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
1372 vertex->op.set_data_offset(0);
1373 vertex->op.set_data_length(0);
1374 Extent* extent = vertex->op.add_dst_extents();
1375 extent->set_start_block(start_block);
1376 extent->set_num_blocks(num_blocks);
1377}
1378
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001379bool DeltaDiffGenerator::GenerateDeltaUpdateFile(
1380 const string& old_root,
1381 const string& old_image,
1382 const string& new_root,
1383 const string& new_image,
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001384 const string& old_kernel_part,
1385 const string& new_kernel_part,
1386 const string& output_path,
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001387 const string& private_key_path,
Darin Petkov8e447e02013-04-16 16:23:50 +02001388 off_t chunk_size,
Chris Sosad5ae1562013-04-23 13:20:18 -07001389 size_t rootfs_partition_size,
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001390 uint64_t* metadata_size) {
Darin Petkov8e447e02013-04-16 16:23:50 +02001391 TEST_AND_RETURN_FALSE(chunk_size == -1 || chunk_size % kBlockSize == 0);
Darin Petkov7ea32332010-10-13 10:46:11 -07001392 int old_image_block_count = 0, old_image_block_size = 0;
1393 int new_image_block_count = 0, new_image_block_size = 0;
1394 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(new_image,
1395 &new_image_block_count,
1396 &new_image_block_size));
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001397 if (!old_image.empty()) {
Darin Petkov7ea32332010-10-13 10:46:11 -07001398 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(old_image,
1399 &old_image_block_count,
1400 &old_image_block_size));
1401 TEST_AND_RETURN_FALSE(old_image_block_size == new_image_block_size);
1402 LOG_IF(WARNING, old_image_block_count != new_image_block_count)
1403 << "Old and new images have different block counts.";
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001404 }
Chris Sosad5ae1562013-04-23 13:20:18 -07001405
1406 // Sanity checks for the partition size.
1407 TEST_AND_RETURN_FALSE(rootfs_partition_size % kBlockSize == 0);
1408 size_t fs_size = static_cast<size_t>(new_image_block_size *
1409 new_image_block_count);
1410 LOG(INFO) << "Rootfs partition size: " << rootfs_partition_size;
1411 LOG(INFO) << "Actual filesystem size: " << fs_size;
1412 TEST_AND_RETURN_FALSE(rootfs_partition_size >= fs_size);
1413
Andrew de los Reyes27f7d372010-10-07 11:26:07 -07001414 // Sanity check kernel partition arg
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001415 TEST_AND_RETURN_FALSE(utils::FileSize(new_kernel_part) >= 0);
1416
Darin Petkov7ea32332010-10-13 10:46:11 -07001417 vector<Block> blocks(max(old_image_block_count, new_image_block_count));
1418 LOG(INFO) << "Invalid block index: " << Vertex::kInvalidIndex;
1419 LOG(INFO) << "Block count: " << blocks.size();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001420 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
1421 CHECK(blocks[i].reader == Vertex::kInvalidIndex);
1422 CHECK(blocks[i].writer == Vertex::kInvalidIndex);
1423 }
1424 Graph graph;
1425 CheckGraph(graph);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001426
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001427 const string kTempFileTemplate("/tmp/CrAU_temp_data.XXXXXX");
1428 string temp_file_path;
Darin Petkov7438a5c2011-08-29 11:56:44 -07001429 scoped_ptr<ScopedPathUnlinker> temp_file_unlinker;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001430 off_t data_file_size = 0;
1431
1432 LOG(INFO) << "Reading files...";
1433
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001434 vector<DeltaArchiveManifest_InstallOperation> kernel_ops;
1435
Andrew de los Reyesef017552010-10-06 17:57:52 -07001436 vector<Vertex::Index> final_order;
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001437 Vertex::Index scratch_vertex = Vertex::kInvalidIndex;
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001438 {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001439 int fd;
1440 TEST_AND_RETURN_FALSE(
1441 utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &fd));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001442 temp_file_unlinker.reset(new ScopedPathUnlinker(temp_file_path));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001443 TEST_AND_RETURN_FALSE(fd >= 0);
1444 ScopedFdCloser fd_closer(&fd);
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001445 if (!old_image.empty()) {
1446 // Delta update
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001447
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001448 TEST_AND_RETURN_FALSE(DeltaReadFiles(&graph,
1449 &blocks,
1450 old_root,
1451 new_root,
Darin Petkov8e447e02013-04-16 16:23:50 +02001452 chunk_size,
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001453 fd,
1454 &data_file_size));
1455 LOG(INFO) << "done reading normal files";
1456 CheckGraph(graph);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001457
Thieu Le5c7d9752010-12-15 16:09:28 -08001458 LOG(INFO) << "Starting metadata processing";
1459 TEST_AND_RETURN_FALSE(Metadata::DeltaReadMetadata(&graph,
1460 &blocks,
1461 old_image,
1462 new_image,
1463 fd,
1464 &data_file_size));
1465 LOG(INFO) << "Done metadata processing";
1466 CheckGraph(graph);
1467
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001468 graph.resize(graph.size() + 1);
1469 TEST_AND_RETURN_FALSE(ReadUnwrittenBlocks(blocks,
1470 fd,
1471 &data_file_size,
1472 new_image,
1473 &graph.back()));
1474
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001475 // Final scratch block (if there's space)
Chris Sosad5ae1562013-04-23 13:20:18 -07001476 if (blocks.size() < (rootfs_partition_size / kBlockSize)) {
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001477 scratch_vertex = graph.size();
1478 graph.resize(graph.size() + 1);
1479 CreateScratchNode(blocks.size(),
Chris Sosad5ae1562013-04-23 13:20:18 -07001480 (rootfs_partition_size / kBlockSize) - blocks.size(),
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001481 &graph.back());
1482 }
1483
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001484 // Read kernel partition
1485 TEST_AND_RETURN_FALSE(DeltaCompressKernelPartition(old_kernel_part,
1486 new_kernel_part,
1487 &kernel_ops,
1488 fd,
1489 &data_file_size));
1490
1491 LOG(INFO) << "done reading kernel";
1492 CheckGraph(graph);
1493
1494 LOG(INFO) << "Creating edges...";
1495 CreateEdges(&graph, blocks);
1496 LOG(INFO) << "Done creating edges";
1497 CheckGraph(graph);
1498
1499 TEST_AND_RETURN_FALSE(ConvertGraphToDag(&graph,
1500 new_root,
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001501 fd,
1502 &data_file_size,
Andrew de los Reyes927179d2010-12-02 11:26:48 -08001503 &final_order,
1504 scratch_vertex));
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001505 } else {
1506 // Full update
Darin Petkov7ea32332010-10-13 10:46:11 -07001507 off_t new_image_size =
1508 static_cast<off_t>(new_image_block_count) * new_image_block_size;
Darin Petkov7a22d792010-11-08 14:10:00 -08001509 TEST_AND_RETURN_FALSE(FullUpdateGenerator::Run(&graph,
1510 new_kernel_part,
1511 new_image,
1512 new_image_size,
1513 fd,
1514 &data_file_size,
1515 kFullUpdateChunkSize,
1516 kBlockSize,
1517 &kernel_ops,
1518 &final_order));
Andrew de los Reyesf88144f2010-10-11 10:32:59 -07001519 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001520 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001521
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001522 // Convert to protobuf Manifest object
1523 DeltaArchiveManifest manifest;
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001524 OperationNameMap op_name_map;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001525 CheckGraph(graph);
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001526 InstallOperationsToManifest(graph,
1527 final_order,
1528 kernel_ops,
1529 &manifest,
1530 &op_name_map);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001531 CheckGraph(graph);
1532 manifest.set_block_size(kBlockSize);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001533
1534 // Reorder the data blobs with the newly ordered manifest
1535 string ordered_blobs_path;
1536 TEST_AND_RETURN_FALSE(utils::MakeTempFile(
1537 "/tmp/CrAU_temp_data.ordered.XXXXXX",
1538 &ordered_blobs_path,
Andrew de los Reyese05fc282011-06-02 09:50:08 -07001539 NULL));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001540 ScopedPathUnlinker ordered_blobs_unlinker(ordered_blobs_path);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001541 TEST_AND_RETURN_FALSE(ReorderDataBlobs(&manifest,
1542 temp_file_path,
1543 ordered_blobs_path));
Darin Petkov7438a5c2011-08-29 11:56:44 -07001544 temp_file_unlinker.reset();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001545
Darin Petkov9fa7ec52010-10-18 11:45:23 -07001546 // Check that install op blobs are in order.
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001547 uint64_t next_blob_offset = 0;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001548 {
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001549 for (int i = 0; i < (manifest.install_operations_size() +
1550 manifest.kernel_install_operations_size()); i++) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001551 DeltaArchiveManifest_InstallOperation* op =
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001552 i < manifest.install_operations_size() ?
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001553 manifest.mutable_install_operations(i) :
1554 manifest.mutable_kernel_install_operations(
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -07001555 i - manifest.install_operations_size());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001556 if (op->has_data_offset()) {
1557 if (op->data_offset() != next_blob_offset) {
1558 LOG(FATAL) << "bad blob offset! " << op->data_offset() << " != "
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001559 << next_blob_offset;
1560 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001561 next_blob_offset += op->data_length();
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001562 }
1563 }
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001564 }
1565
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001566 // Signatures appear at the end of the blobs. Note the offset in the
1567 // manifest
1568 if (!private_key_path.empty()) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001569 uint64_t signature_blob_length = 0;
1570 TEST_AND_RETURN_FALSE(
Andrew de los Reyesc24e3f32011-08-30 15:45:20 -07001571 PayloadSigner::SignatureBlobLength(vector<string>(1, private_key_path),
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001572 &signature_blob_length));
Darin Petkov9574f7e2011-01-13 10:48:12 -08001573 AddSignatureOp(next_blob_offset, signature_blob_length, &manifest);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001574 }
1575
Darin Petkov36a58222010-10-07 22:00:09 -07001576 TEST_AND_RETURN_FALSE(InitializePartitionInfos(old_kernel_part,
1577 new_kernel_part,
1578 old_image,
1579 new_image,
1580 &manifest));
1581
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001582 // Serialize protobuf
1583 string serialized_manifest;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001584
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001585 CheckGraph(graph);
1586 TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest));
1587 CheckGraph(graph);
1588
1589 LOG(INFO) << "Writing final delta file header...";
1590 DirectFileWriter writer;
1591 TEST_AND_RETURN_FALSE_ERRNO(writer.Open(output_path.c_str(),
1592 O_WRONLY | O_CREAT | O_TRUNC,
1593 0644) == 0);
1594 ScopedFileWriterCloser writer_closer(&writer);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001595
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001596 // Write header
Don Garrette410e0f2011-11-10 15:39:01 -08001597 TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, strlen(kDeltaMagic)));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001598
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001599 // Write version number
1600 TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, kVersionNumber));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001601
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001602 // Write protobuf length
1603 TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer,
1604 serialized_manifest.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001605
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001606 // Write protobuf
1607 LOG(INFO) << "Writing final delta file protobuf... "
1608 << serialized_manifest.size();
1609 TEST_AND_RETURN_FALSE(writer.Write(serialized_manifest.data(),
Don Garrette410e0f2011-11-10 15:39:01 -08001610 serialized_manifest.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001611
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001612 // Append the data blobs
1613 LOG(INFO) << "Writing final delta file data blobs...";
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001614 int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001615 ScopedFdCloser blobs_fd_closer(&blobs_fd);
1616 TEST_AND_RETURN_FALSE(blobs_fd >= 0);
1617 for (;;) {
1618 char buf[kBlockSize];
1619 ssize_t rc = read(blobs_fd, buf, sizeof(buf));
1620 if (0 == rc) {
1621 // EOF
1622 break;
1623 }
1624 TEST_AND_RETURN_FALSE_ERRNO(rc > 0);
Don Garrette410e0f2011-11-10 15:39:01 -08001625 TEST_AND_RETURN_FALSE(writer.Write(buf, rc));
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001626 }
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001627
1628 // Write signature blob.
1629 if (!private_key_path.empty()) {
1630 LOG(INFO) << "Signing the update...";
1631 vector<char> signature_blob;
Andrew de los Reyesc24e3f32011-08-30 15:45:20 -07001632 TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(
1633 output_path,
1634 vector<string>(1, private_key_path),
1635 &signature_blob));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001636 TEST_AND_RETURN_FALSE(writer.Write(&signature_blob[0],
Don Garrette410e0f2011-11-10 15:39:01 -08001637 signature_blob.size()));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07001638 }
1639
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001640 *metadata_size =
Darin Petkov95cf01f2010-10-12 14:59:13 -07001641 strlen(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001642 ReportPayloadUsage(manifest, *metadata_size, op_name_map);
Darin Petkov880335c2010-10-01 15:52:53 -07001643
Jay Srinivasan738fdf32012-12-07 17:40:54 -08001644 LOG(INFO) << "All done. Successfully created delta file with "
1645 << "metadata size = " << *metadata_size;
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001646 return true;
1647}
1648
Thieu Le5c7d9752010-12-15 16:09:28 -08001649// Runs the bsdiff tool on two files and returns the resulting delta in
1650// 'out'. Returns true on success.
1651bool DeltaDiffGenerator::BsdiffFiles(const string& old_file,
1652 const string& new_file,
1653 vector<char>* out) {
1654 const string kPatchFile = "/tmp/delta.patchXXXXXX";
1655 string patch_file_path;
1656
1657 TEST_AND_RETURN_FALSE(
1658 utils::MakeTempFile(kPatchFile, &patch_file_path, NULL));
1659
1660 vector<string> cmd;
1661 cmd.push_back(kBsdiffPath);
1662 cmd.push_back(old_file);
1663 cmd.push_back(new_file);
1664 cmd.push_back(patch_file_path);
1665
1666 int rc = 1;
1667 vector<char> patch_file;
Darin Petkov85d02b72011-05-17 13:25:51 -07001668 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc, NULL));
Thieu Le5c7d9752010-12-15 16:09:28 -08001669 TEST_AND_RETURN_FALSE(rc == 0);
1670 TEST_AND_RETURN_FALSE(utils::ReadFile(patch_file_path, out));
1671 unlink(patch_file_path.c_str());
1672 return true;
1673}
1674
1675// The |blocks| vector contains a reader and writer for each block on the
1676// filesystem that's being in-place updated. We populate the reader/writer
1677// fields of |blocks| by calling this function.
1678// For each block in |operation| that is read or written, find that block
1679// in |blocks| and set the reader/writer field to the vertex passed.
1680// |graph| is not strictly necessary, but useful for printing out
1681// error messages.
1682bool DeltaDiffGenerator::AddInstallOpToBlocksVector(
1683 const DeltaArchiveManifest_InstallOperation& operation,
1684 const Graph& graph,
1685 Vertex::Index vertex,
1686 vector<Block>* blocks) {
1687 // See if this is already present.
1688 TEST_AND_RETURN_FALSE(operation.dst_extents_size() > 0);
1689
1690 enum BlockField { READER = 0, WRITER, BLOCK_FIELD_COUNT };
1691 for (int field = READER; field < BLOCK_FIELD_COUNT; field++) {
1692 const int extents_size =
1693 (field == READER) ? operation.src_extents_size() :
1694 operation.dst_extents_size();
1695 const char* past_participle = (field == READER) ? "read" : "written";
1696 const google::protobuf::RepeatedPtrField<Extent>& extents =
1697 (field == READER) ? operation.src_extents() : operation.dst_extents();
1698 Vertex::Index Block::*access_type =
1699 (field == READER) ? &Block::reader : &Block::writer;
1700
1701 for (int i = 0; i < extents_size; i++) {
1702 const Extent& extent = extents.Get(i);
1703 if (extent.start_block() == kSparseHole) {
1704 // Hole in sparse file. skip
1705 continue;
1706 }
1707 for (uint64_t block = extent.start_block();
1708 block < (extent.start_block() + extent.num_blocks()); block++) {
1709 if ((*blocks)[block].*access_type != Vertex::kInvalidIndex) {
1710 LOG(FATAL) << "Block " << block << " is already "
1711 << past_participle << " by "
1712 << (*blocks)[block].*access_type << "("
1713 << graph[(*blocks)[block].*access_type].file_name
1714 << ") and also " << vertex << "("
1715 << graph[vertex].file_name << ")";
1716 }
1717 (*blocks)[block].*access_type = vertex;
1718 }
1719 }
1720 }
1721 return true;
1722}
1723
Darin Petkov9574f7e2011-01-13 10:48:12 -08001724void DeltaDiffGenerator::AddSignatureOp(uint64_t signature_blob_offset,
1725 uint64_t signature_blob_length,
1726 DeltaArchiveManifest* manifest) {
1727 LOG(INFO) << "Making room for signature in file";
1728 manifest->set_signatures_offset(signature_blob_offset);
1729 LOG(INFO) << "set? " << manifest->has_signatures_offset();
1730 // Add a dummy op at the end to appease older clients
1731 DeltaArchiveManifest_InstallOperation* dummy_op =
1732 manifest->add_kernel_install_operations();
1733 dummy_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
1734 dummy_op->set_data_offset(signature_blob_offset);
1735 manifest->set_signatures_offset(signature_blob_offset);
1736 dummy_op->set_data_length(signature_blob_length);
1737 manifest->set_signatures_size(signature_blob_length);
1738 Extent* dummy_extent = dummy_op->add_dst_extents();
1739 // Tell the dummy op to write this data to a big sparse hole
1740 dummy_extent->set_start_block(kSparseHole);
1741 dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) /
1742 kBlockSize);
1743}
1744
Andrew de los Reyes50f36492010-11-01 13:57:12 -07001745const char* const kBsdiffPath = "bsdiff";
1746const char* const kBspatchPath = "bspatch";
Andrew de los Reyes09e56d62010-04-23 13:45:53 -07001747const char* const kDeltaMagic = "CrAU";
1748
Andrew de los Reyesb10320d2010-03-31 16:44:44 -07001749}; // namespace chromeos_update_engine