delta_diff_generator.cc revision 6b9e38ef1180efe55e4a82bb18536d1b53fe493d
1// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/payload_generator/delta_diff_generator.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <inttypes.h>
10#include <sys/stat.h>
11#include <sys/types.h>
12
13#include <algorithm>
14#include <map>
15#include <memory>
16#include <string>
17#include <utility>
18#include <vector>
19
20#include <base/files/file_path.h>
21#include <base/files/file_util.h>
22#include <base/logging.h>
23#include <base/strings/stringprintf.h>
24#include <base/strings/string_util.h>
25#include <bzlib.h>
26#include <chromeos/secure_blob.h>
27
28#include "update_engine/bzip.h"
29#include "update_engine/delta_performer.h"
30#include "update_engine/extent_ranges.h"
31#include "update_engine/file_writer.h"
32#include "update_engine/omaha_hash_calculator.h"
33#include "update_engine/payload_constants.h"
34#include "update_engine/payload_generator/ext2_filesystem.h"
35#include "update_engine/payload_generator/full_update_generator.h"
36#include "update_engine/payload_generator/inplace_generator.h"
37#include "update_engine/payload_generator/payload_signer.h"
38#include "update_engine/payload_generator/raw_filesystem.h"
39#include "update_engine/payload_verifier.h"
40#include "update_engine/subprocess.h"
41#include "update_engine/update_metadata.pb.h"
42#include "update_engine/utils.h"
43
44using std::map;
45using std::set;
46using std::sort;
47using std::string;
48using std::unique_ptr;
49using std::vector;
50
51namespace {
52
53const uint64_t kMajorVersionNumber = 1;
54
55// The maximum destination size allowed for bsdiff. In general, bsdiff should
56// work for arbitrary big files, but the payload generation and payload
57// application requires a significant amount of RAM. We put a hard-limit of
58// 200 MiB that should not affect any released board, but will limit the
59// Chrome binary in ASan builders.
60const uint64_t kMaxBsdiffDestinationSize = 200 * 1024 * 1024;  // bytes
61
62static const char* kInstallOperationTypes[] = {
63  "REPLACE",
64  "REPLACE_BZ",
65  "MOVE",
66  "BSDIFF",
67  "SOURCE_COPY",
68  "SOURCE_BSDIFF"
69};
70
71}  // namespace
72
73namespace chromeos_update_engine {
74
75typedef map<const DeltaArchiveManifest_InstallOperation*,
76            string> OperationNameMap;
77
78// bytes
79const size_t kRootFSPartitionSize = static_cast<size_t>(2) * 1024 * 1024 * 1024;
80const size_t kBlockSize = 4096;  // bytes
81const char* const kEmptyPath = "";
82const char* const kBsdiffPath = "bsdiff";
83
84namespace {
85
86// Writes the uint64_t passed in in host-endian to the file as big-endian.
87// Returns true on success.
88bool WriteUint64AsBigEndian(FileWriter* writer, const uint64_t value) {
89  uint64_t value_be = htobe64(value);
90  TEST_AND_RETURN_FALSE(writer->Write(&value_be, sizeof(value_be)));
91  return true;
92}
93
94// Adds each operation from |rootfs_ops| and |kernel_ops| to |out_manifest| in
95// the order they come in those vectors. reports the operations names
96void InstallOperationsToManifest(
97    const vector<AnnotatedOperation>& rootfs_ops,
98    const vector<AnnotatedOperation>& kernel_ops,
99    DeltaArchiveManifest* out_manifest,
100    OperationNameMap* out_op_name_map) {
101  for (const AnnotatedOperation& aop : rootfs_ops) {
102    if (DeltaDiffGenerator::IsNoopOperation(aop.op))
103      continue;
104    DeltaArchiveManifest_InstallOperation* new_op =
105        out_manifest->add_install_operations();
106    (*out_op_name_map)[new_op] = aop.name;
107    *new_op = aop.op;
108  }
109  for (const AnnotatedOperation& aop : kernel_ops) {
110    if (DeltaDiffGenerator::IsNoopOperation(aop.op))
111      continue;
112    DeltaArchiveManifest_InstallOperation* new_op =
113        out_manifest->add_kernel_install_operations();
114    (*out_op_name_map)[new_op] = aop.name;
115    *new_op = aop.op;
116  }
117}
118
119struct DeltaObject {
120  DeltaObject(const string& in_name, const int in_type, const off_t in_size)
121      : name(in_name),
122        type(in_type),
123        size(in_size) {}
124  bool operator <(const DeltaObject& object) const {
125    return (size != object.size) ? (size < object.size) : (name < object.name);
126  }
127  string name;
128  int type;
129  off_t size;
130};
131
132void ReportPayloadUsage(const DeltaArchiveManifest& manifest,
133                        const int64_t manifest_metadata_size,
134                        const OperationNameMap& op_name_map) {
135  vector<DeltaObject> objects;
136  off_t total_size = 0;
137
138  // Rootfs install operations.
139  for (int i = 0; i < manifest.install_operations_size(); ++i) {
140    const DeltaArchiveManifest_InstallOperation& op =
141        manifest.install_operations(i);
142    objects.push_back(DeltaObject(op_name_map.find(&op)->second,
143                                  op.type(),
144                                  op.data_length()));
145    total_size += op.data_length();
146  }
147
148  // Kernel install operations.
149  for (int i = 0; i < manifest.kernel_install_operations_size(); ++i) {
150    const DeltaArchiveManifest_InstallOperation& op =
151        manifest.kernel_install_operations(i);
152    objects.push_back(DeltaObject(base::StringPrintf("<kernel-operation-%d>",
153                                                     i),
154                                  op.type(),
155                                  op.data_length()));
156    total_size += op.data_length();
157  }
158
159  objects.push_back(DeltaObject("<manifest-metadata>",
160                                -1,
161                                manifest_metadata_size));
162  total_size += manifest_metadata_size;
163
164  std::sort(objects.begin(), objects.end());
165
166  static const char kFormatString[] = "%6.2f%% %10jd %-10s %s\n";
167  for (const DeltaObject& object : objects) {
168    fprintf(stderr, kFormatString,
169            object.size * 100.0 / total_size,
170            static_cast<intmax_t>(object.size),
171            object.type >= 0 ? kInstallOperationTypes[object.type] : "-",
172            object.name.c_str());
173  }
174  fprintf(stderr, kFormatString,
175          100.0, static_cast<intmax_t>(total_size), "", "<total>");
176}
177
178// Process a range of blocks from |range_start| to |range_end| in the extent at
179// position |*idx_p| of |extents|. If |do_remove| is true, this range will be
180// removed, which may cause the extent to be trimmed, split or removed entirely.
181// The value of |*idx_p| is updated to point to the next extent to be processed.
182// Returns true iff the next extent to process is a new or updated one.
183bool ProcessExtentBlockRange(vector<Extent>* extents, size_t* idx_p,
184                             const bool do_remove, uint64_t range_start,
185                             uint64_t range_end) {
186  size_t idx = *idx_p;
187  uint64_t start_block = (*extents)[idx].start_block();
188  uint64_t num_blocks = (*extents)[idx].num_blocks();
189  uint64_t range_size = range_end - range_start;
190
191  if (do_remove) {
192    if (range_size == num_blocks) {
193      // Remove the entire extent.
194      extents->erase(extents->begin() + idx);
195    } else if (range_end == num_blocks) {
196      // Trim the end of the extent.
197      (*extents)[idx].set_num_blocks(num_blocks - range_size);
198      idx++;
199    } else if (range_start == 0) {
200      // Trim the head of the extent.
201      (*extents)[idx].set_start_block(start_block + range_size);
202      (*extents)[idx].set_num_blocks(num_blocks - range_size);
203    } else {
204      // Trim the middle, splitting the remainder into two parts.
205      (*extents)[idx].set_num_blocks(range_start);
206      Extent e;
207      e.set_start_block(start_block + range_end);
208      e.set_num_blocks(num_blocks - range_end);
209      idx++;
210      extents->insert(extents->begin() + idx, e);
211    }
212  } else if (range_end == num_blocks) {
213    // Done with this extent.
214    idx++;
215  } else {
216    return false;
217  }
218
219  *idx_p = idx;
220  return true;
221}
222
223// Remove identical corresponding block ranges in |src_extents| and
224// |dst_extents|. Used for preventing moving of blocks onto themselves during
225// MOVE operations. The value of |total_bytes| indicates the actual length of
226// content; this may be slightly less than the total size of blocks, in which
227// case the last block is only partly occupied with data. Returns the total
228// number of bytes removed.
229size_t RemoveIdenticalBlockRanges(vector<Extent>* src_extents,
230                                  vector<Extent>* dst_extents,
231                                  const size_t total_bytes) {
232  size_t src_idx = 0;
233  size_t dst_idx = 0;
234  uint64_t src_offset = 0, dst_offset = 0;
235  bool new_src = true, new_dst = true;
236  size_t removed_bytes = 0, nonfull_block_bytes;
237  bool do_remove = false;
238  while (src_idx < src_extents->size() && dst_idx < dst_extents->size()) {
239    if (new_src) {
240      src_offset = 0;
241      new_src = false;
242    }
243    if (new_dst) {
244      dst_offset = 0;
245      new_dst = false;
246    }
247
248    do_remove = ((*src_extents)[src_idx].start_block() + src_offset ==
249                 (*dst_extents)[dst_idx].start_block() + dst_offset);
250
251    uint64_t src_num_blocks = (*src_extents)[src_idx].num_blocks();
252    uint64_t dst_num_blocks = (*dst_extents)[dst_idx].num_blocks();
253    uint64_t min_num_blocks = std::min(src_num_blocks - src_offset,
254                                       dst_num_blocks - dst_offset);
255    uint64_t prev_src_offset = src_offset;
256    uint64_t prev_dst_offset = dst_offset;
257    src_offset += min_num_blocks;
258    dst_offset += min_num_blocks;
259
260    new_src = ProcessExtentBlockRange(src_extents, &src_idx, do_remove,
261                                      prev_src_offset, src_offset);
262    new_dst = ProcessExtentBlockRange(dst_extents, &dst_idx, do_remove,
263                                      prev_dst_offset, dst_offset);
264    if (do_remove)
265      removed_bytes += min_num_blocks * kBlockSize;
266  }
267
268  // If we removed the last block and this block is only partly used by file
269  // content, deduct the unused portion from the total removed byte count.
270  if (do_remove && (nonfull_block_bytes = total_bytes % kBlockSize))
271    removed_bytes -= kBlockSize - nonfull_block_bytes;
272
273  return removed_bytes;
274}
275
276// Compare two AnnotatedOperations by the start block of the first Extent in
277// their destination extents.
278bool CompareAopsByDestination(AnnotatedOperation first_aop,
279                              AnnotatedOperation second_aop) {
280  // We want empty operations to be at the end of the payload.
281  if (!first_aop.op.dst_extents().size() || !second_aop.op.dst_extents().size())
282    return ((!first_aop.op.dst_extents().size()) <
283            (!second_aop.op.dst_extents().size()));
284  uint32_t first_dst_start = first_aop.op.dst_extents(0).start_block();
285  uint32_t second_dst_start = second_aop.op.dst_extents(0).start_block();
286  return first_dst_start < second_dst_start;
287}
288
289}  // namespace
290
291bool DeltaDiffGenerator::DeltaReadFilesystem(
292    vector<AnnotatedOperation>* aops,
293    const string& old_part,
294    const string& new_part,
295    FilesystemInterface* old_fs,
296    FilesystemInterface* new_fs,
297    off_t chunk_blocks,
298    int data_fd,
299    off_t* data_file_size,
300    bool src_ops_allowed) {
301  ExtentRanges old_visited_blocks;
302  ExtentRanges new_visited_blocks;
303
304  map<string, vector<Extent>> old_files_map;
305  if (old_fs) {
306    vector<FilesystemInterface::File> old_files;
307    old_fs->GetFiles(&old_files);
308    for (const FilesystemInterface::File& file : old_files)
309      old_files_map[file.name] = file.extents;
310  }
311
312  vector<FilesystemInterface::File> new_files;
313  new_fs->GetFiles(&new_files);
314
315  // The processing is very straightforward here, we generate operations for
316  // every file (and pseudo-file such as the metadata) in the new filesystem
317  // based on the file with the same name in the old filesystem, if any.
318  // Files with overlapping data blocks (like hardlinks or filesystems with tail
319  // packing or compression where the blocks store more than one file) are only
320  // generated once in the new image, but are also used only once from the old
321  // image due to some simplifications (see below).
322  for (const FilesystemInterface::File& new_file : new_files) {
323    // Ignore the files in the new filesystem without blocks. Symlinks with
324    // data blocks (for example, symlinks bigger than 60 bytes in ext2) are
325    // handled as normal files. We also ignore blocks that were already
326    // processed by a previous file.
327    vector<Extent> new_file_extents = FilterExtentRanges(
328        new_file.extents, new_visited_blocks);
329    new_visited_blocks.AddExtents(new_file_extents);
330
331    if (new_file_extents.empty())
332      continue;
333
334    LOG(INFO) << "Encoding file " << new_file.name << " ("
335              << BlocksInExtents(new_file_extents) << " blocks)";
336
337    // We can't visit each dst image inode more than once, as that would
338    // duplicate work. Here, we avoid visiting each source image inode
339    // more than once. Technically, we could have multiple operations
340    // that read the same blocks from the source image for diffing, but
341    // we choose not to avoid complexity. Eventually we will move away
342    // from using a graph/cycle detection/etc to generate diffs, and at that
343    // time, it will be easy (non-complex) to have many operations read
344    // from the same source blocks. At that time, this code can die. -adlr
345    vector<Extent> old_file_extents = FilterExtentRanges(
346        old_files_map[new_file.name], old_visited_blocks);
347    old_visited_blocks.AddExtents(old_file_extents);
348
349    TEST_AND_RETURN_FALSE(DeltaReadFile(
350        aops,
351        old_part,
352        new_part,
353        old_file_extents,
354        new_file_extents,
355        new_file.name,  // operation name
356        chunk_blocks,
357        data_fd,
358        data_file_size,
359        src_ops_allowed));
360  }
361  // Process all the blocks not included in any file. We provided all the unused
362  // blocks in the old partition as available data.
363  vector<Extent> new_unvisited = { ExtentForRange(0, new_fs->GetBlockCount()) };
364  new_unvisited = FilterExtentRanges(new_unvisited, new_visited_blocks);
365  if (new_unvisited.empty())
366    return true;
367
368  vector<Extent> old_unvisited;
369  if (old_fs) {
370    old_unvisited.push_back(ExtentForRange(0, old_fs->GetBlockCount()));
371    old_unvisited = FilterExtentRanges(old_unvisited, old_visited_blocks);
372  }
373
374  LOG(INFO) << "Scanning " << BlocksInExtents(new_unvisited)
375            << " unwritten blocks";
376  TEST_AND_RETURN_FALSE(DeltaReadFile(
377      aops,
378      old_part,
379      new_part,
380      old_unvisited,
381      new_unvisited,
382      "<non-file-data>",  // operation name
383      chunk_blocks,
384      data_fd,
385      data_file_size,
386      src_ops_allowed));
387
388  return true;
389}
390
391bool DeltaDiffGenerator::DeltaReadFile(
392    vector<AnnotatedOperation>* aops,
393    const string& old_part,
394    const string& new_part,
395    const vector<Extent>& old_extents,
396    const vector<Extent>& new_extents,
397    const string& name,
398    off_t chunk_blocks,
399    int data_fd,
400    off_t* data_file_size,
401    bool src_ops_allowed) {
402  chromeos::Blob data;
403  DeltaArchiveManifest_InstallOperation operation;
404
405  uint64_t total_blocks = BlocksInExtents(new_extents);
406  if (chunk_blocks == -1)
407    chunk_blocks = total_blocks;
408
409  // If bsdiff breaks again, blacklist the problem file by using:
410  //   bsdiff_allowed = (name != "/foo/bar")
411  //
412  // TODO(dgarrett): chromium-os:15274 connect this test to the command line.
413  bool bsdiff_allowed = true;
414  if (static_cast<uint64_t>(chunk_blocks) * kBlockSize >
415      kMaxBsdiffDestinationSize) {
416    bsdiff_allowed = false;
417  }
418
419  if (!bsdiff_allowed) {
420    LOG(INFO) << "bsdiff blacklisting: " << name;
421  }
422
423  for (uint64_t block_offset = 0; block_offset < total_blocks;
424      block_offset += chunk_blocks) {
425    // Split the old/new file in the same chunks. Note that this could drop
426    // some information from the old file used for the new chunk. If the old
427    // file is smaller (or even empty when there's no old file) the chunk will
428    // also be empty.
429    vector<Extent> old_extents_chunk = ExtentsSublist(
430        old_extents, block_offset, chunk_blocks);
431    vector<Extent> new_extents_chunk = ExtentsSublist(
432        new_extents, block_offset, chunk_blocks);
433    NormalizeExtents(&old_extents_chunk);
434    NormalizeExtents(&new_extents_chunk);
435
436    TEST_AND_RETURN_FALSE(ReadExtentsToDiff(old_part,
437                                            new_part,
438                                            old_extents_chunk,
439                                            new_extents_chunk,
440                                            bsdiff_allowed,
441                                            &data,
442                                            &operation,
443                                            src_ops_allowed));
444
445    // Check if the operation writes nothing.
446    if (operation.dst_extents_size() == 0) {
447      if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
448        LOG(INFO) << "Empty MOVE operation ("
449                  << name << "), skipping";
450        continue;
451      } else {
452        LOG(ERROR) << "Empty non-MOVE operation";
453        return false;
454      }
455    }
456
457    // Write the data
458    if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE &&
459        operation.type() !=
460            DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY) {
461      operation.set_data_offset(*data_file_size);
462      operation.set_data_length(data.size());
463    }
464
465    TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, data.data(), data.size()));
466    *data_file_size += data.size();
467
468    // Now, insert into the list of operations.
469    AnnotatedOperation aop;
470    aop.name = name;
471    if (static_cast<uint64_t>(chunk_blocks) < total_blocks) {
472      aop.name = base::StringPrintf("%s:%" PRIu64,
473                                    name.c_str(), block_offset / chunk_blocks);
474    }
475    aop.op = operation;
476    aops->emplace_back(aop);
477  }
478  return true;
479}
480
481bool DeltaDiffGenerator::ReadExtentsToDiff(
482    const string& old_part,
483    const string& new_part,
484    const vector<Extent>& old_extents,
485    const vector<Extent>& new_extents,
486    bool bsdiff_allowed,
487    chromeos::Blob* out_data,
488    DeltaArchiveManifest_InstallOperation* out_op,
489    bool src_ops_allowed) {
490  DeltaArchiveManifest_InstallOperation operation;
491  // Data blob that will be written to delta file.
492  const chromeos::Blob* data_blob = nullptr;
493
494  // We read blocks from old_extents and write blocks to new_extents.
495  uint64_t blocks_to_read = BlocksInExtents(old_extents);
496  uint64_t blocks_to_write = BlocksInExtents(new_extents);
497
498  // Make copies of the extents so we can modify them.
499  vector<Extent> src_extents = old_extents;
500  vector<Extent> dst_extents = new_extents;
501
502  // Read in bytes from new data.
503  chromeos::Blob new_data;
504  TEST_AND_RETURN_FALSE(utils::ReadExtents(new_part,
505                                           new_extents,
506                                           &new_data,
507                                           kBlockSize * blocks_to_write,
508                                           kBlockSize));
509  TEST_AND_RETURN_FALSE(!new_data.empty());
510
511
512  // Using a REPLACE is always an option.
513  operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
514  data_blob = &new_data;
515
516  // Try compressing it with bzip2.
517  chromeos::Blob new_data_bz;
518  TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz));
519  CHECK(!new_data_bz.empty());
520  if (new_data_bz.size() < data_blob->size()) {
521    // A REPLACE_BZ is better.
522    operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
523    data_blob = &new_data_bz;
524  }
525
526  chromeos::Blob old_data;
527  chromeos::Blob empty_blob;
528  chromeos::Blob bsdiff_delta;
529  if (blocks_to_read > 0) {
530    // Read old data.
531    TEST_AND_RETURN_FALSE(
532        utils::ReadExtents(old_part, src_extents, &old_data,
533                           kBlockSize * blocks_to_read, kBlockSize));
534    if (old_data == new_data) {
535      // No change in data.
536      if (src_ops_allowed) {
537        operation.set_type(
538            DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
539      } else {
540        operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
541      }
542      data_blob = &empty_blob;
543    } else if (bsdiff_allowed) {
544      // If the source file is considered bsdiff safe (no bsdiff bugs
545      // triggered), see if BSDIFF encoding is smaller.
546      base::FilePath old_chunk;
547      TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&old_chunk));
548      ScopedPathUnlinker old_unlinker(old_chunk.value());
549      TEST_AND_RETURN_FALSE(
550          utils::WriteFile(old_chunk.value().c_str(),
551                           old_data.data(), old_data.size()));
552      base::FilePath new_chunk;
553      TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&new_chunk));
554      ScopedPathUnlinker new_unlinker(new_chunk.value());
555      TEST_AND_RETURN_FALSE(
556          utils::WriteFile(new_chunk.value().c_str(),
557                           new_data.data(), new_data.size()));
558
559      TEST_AND_RETURN_FALSE(
560          BsdiffFiles(old_chunk.value(), new_chunk.value(), &bsdiff_delta));
561      CHECK_GT(bsdiff_delta.size(), static_cast<chromeos::Blob::size_type>(0));
562      if (bsdiff_delta.size() < data_blob->size()) {
563        if (src_ops_allowed) {
564          operation.set_type(
565              DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF);
566        } else {
567          operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
568        }
569        data_blob = &bsdiff_delta;
570      }
571    }
572  }
573
574  size_t removed_bytes = 0;
575  // Remove identical src/dst block ranges in MOVE operations.
576  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
577    removed_bytes = RemoveIdenticalBlockRanges(
578        &src_extents, &dst_extents, new_data.size());
579  }
580  // Set legacy src_length and dst_length fields.
581  operation.set_src_length(old_data.size() - removed_bytes);
582  operation.set_dst_length(new_data.size() - removed_bytes);
583
584  // Embed extents in the operation.
585  StoreExtents(src_extents, operation.mutable_src_extents());
586  StoreExtents(dst_extents, operation.mutable_dst_extents());
587
588  // Replace operations should not have source extents.
589  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
590      operation.type() ==
591          DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
592    operation.clear_src_extents();
593    operation.clear_src_length();
594  }
595
596  *out_data = std::move(*data_blob);
597  *out_op = operation;
598
599  return true;
600}
601
602bool DeltaDiffGenerator::DeltaCompressKernelPartition(
603    const string& old_kernel_part,
604    const string& new_kernel_part,
605    uint64_t old_kernel_size,
606    uint64_t new_kernel_size,
607    uint64_t block_size,
608    vector<AnnotatedOperation>* aops,
609    int blobs_fd,
610    off_t* blobs_length,
611    bool src_ops_allowed) {
612  LOG(INFO) << "Delta compressing kernel partition...";
613  LOG_IF(INFO, old_kernel_part.empty()) << "Generating full kernel update...";
614
615  unique_ptr<RawFilesystem> old_kernel_fs;
616  if (!old_kernel_part.empty())
617    old_kernel_fs = RawFilesystem::Create("<kernel-delta-operation>",
618                                          block_size,
619                                          old_kernel_size / block_size);
620  unique_ptr<RawFilesystem> new_kernel_fs = RawFilesystem::Create(
621      "<kernel-delta-operation>",
622      block_size,
623      new_kernel_size / block_size);
624
625  DeltaReadFilesystem(aops,
626                      old_kernel_part,
627                      new_kernel_part,
628                      old_kernel_fs.get(),
629                      new_kernel_fs.get(),
630                      -1,  // chunk_blocks
631                      blobs_fd,
632                      blobs_length,
633                      src_ops_allowed);
634
635  LOG(INFO) << "Done delta compressing kernel partition.";
636  return true;
637}
638
639bool DeltaDiffGenerator::InitializePartitionInfo(bool is_kernel,
640                                                 const string& partition,
641                                                 PartitionInfo* info) {
642  int64_t size = 0;
643  if (is_kernel) {
644    size = utils::FileSize(partition);
645  } else {
646    int block_count = 0, block_size = 0;
647    TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(partition,
648                                                   &block_count,
649                                                   &block_size));
650    size = static_cast<int64_t>(block_count) * block_size;
651  }
652  TEST_AND_RETURN_FALSE(size > 0);
653  info->set_size(size);
654  OmahaHashCalculator hasher;
655  TEST_AND_RETURN_FALSE(hasher.UpdateFile(partition, size) == size);
656  TEST_AND_RETURN_FALSE(hasher.Finalize());
657  const chromeos::Blob& hash = hasher.raw_hash();
658  info->set_hash(hash.data(), hash.size());
659  LOG(INFO) << partition << ": size=" << size << " hash=" << hasher.hash();
660  return true;
661}
662
663bool InitializePartitionInfos(const PayloadGenerationConfig& config,
664                              DeltaArchiveManifest* manifest) {
665  if (!config.source.kernel_part.empty()) {
666    TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
667        true,
668        config.source.kernel_part,
669        manifest->mutable_old_kernel_info()));
670  }
671  TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
672      true,
673      config.target.kernel_part,
674      manifest->mutable_new_kernel_info()));
675  if (!config.source.rootfs_part.empty()) {
676    TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
677        false,
678        config.source.rootfs_part,
679        manifest->mutable_old_rootfs_info()));
680  }
681  TEST_AND_RETURN_FALSE(DeltaDiffGenerator::InitializePartitionInfo(
682      false,
683      config.target.rootfs_part,
684      manifest->mutable_new_rootfs_info()));
685  return true;
686}
687
688// Stores all Extents in 'extents' into 'out'.
689void DeltaDiffGenerator::StoreExtents(
690    const vector<Extent>& extents,
691    google::protobuf::RepeatedPtrField<Extent>* out) {
692  for (const Extent& extent : extents) {
693    Extent* new_extent = out->Add();
694    *new_extent = extent;
695  }
696}
697
698// Stores all extents in |extents| into |out_vector|.
699void DeltaDiffGenerator::ExtentsToVector(
700    const google::protobuf::RepeatedPtrField<Extent>& extents,
701    vector<Extent>* out_vector) {
702  out_vector->clear();
703  for (int i = 0; i < extents.size(); i++) {
704    out_vector->push_back(extents.Get(i));
705  }
706}
707
708// Returns true if |op| is a no-op operation that doesn't do any useful work
709// (e.g., a move operation that copies blocks onto themselves).
710bool DeltaDiffGenerator::IsNoopOperation(
711    const DeltaArchiveManifest_InstallOperation& op) {
712  return (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE &&
713          ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
714}
715
716void DeltaDiffGenerator::FilterNoopOperations(vector<AnnotatedOperation>* ops) {
717  ops->erase(
718      std::remove_if(
719          ops->begin(), ops->end(),
720          [](const AnnotatedOperation& aop){return IsNoopOperation(aop.op);}),
721      ops->end());
722}
723
724bool DeltaDiffGenerator::ReorderDataBlobs(
725    DeltaArchiveManifest* manifest,
726    const string& data_blobs_path,
727    const string& new_data_blobs_path) {
728  int in_fd = open(data_blobs_path.c_str(), O_RDONLY, 0);
729  TEST_AND_RETURN_FALSE_ERRNO(in_fd >= 0);
730  ScopedFdCloser in_fd_closer(&in_fd);
731
732  DirectFileWriter writer;
733  TEST_AND_RETURN_FALSE(
734      writer.Open(new_data_blobs_path.c_str(),
735                  O_WRONLY | O_TRUNC | O_CREAT,
736                  0644) == 0);
737  ScopedFileWriterCloser writer_closer(&writer);
738  uint64_t out_file_size = 0;
739
740  for (int i = 0; i < (manifest->install_operations_size() +
741                       manifest->kernel_install_operations_size()); i++) {
742    DeltaArchiveManifest_InstallOperation* op = nullptr;
743    if (i < manifest->install_operations_size()) {
744      op = manifest->mutable_install_operations(i);
745    } else {
746      op = manifest->mutable_kernel_install_operations(
747          i - manifest->install_operations_size());
748    }
749    if (!op->has_data_offset())
750      continue;
751    CHECK(op->has_data_length());
752    chromeos::Blob buf(op->data_length());
753    ssize_t rc = pread(in_fd, buf.data(), buf.size(), op->data_offset());
754    TEST_AND_RETURN_FALSE(rc == static_cast<ssize_t>(buf.size()));
755
756    // Add the hash of the data blobs for this operation
757    TEST_AND_RETURN_FALSE(AddOperationHash(op, buf));
758
759    op->set_data_offset(out_file_size);
760    TEST_AND_RETURN_FALSE(writer.Write(buf.data(), buf.size()));
761    out_file_size += buf.size();
762  }
763  return true;
764}
765
766bool DeltaDiffGenerator::AddOperationHash(
767    DeltaArchiveManifest_InstallOperation* op,
768    const chromeos::Blob& buf) {
769  OmahaHashCalculator hasher;
770  TEST_AND_RETURN_FALSE(hasher.Update(buf.data(), buf.size()));
771  TEST_AND_RETURN_FALSE(hasher.Finalize());
772  const chromeos::Blob& hash = hasher.raw_hash();
773  op->set_data_sha256_hash(hash.data(), hash.size());
774  return true;
775}
776
777bool DeltaDiffGenerator::GenerateOperations(
778    const PayloadGenerationConfig& config,
779    int data_file_fd,
780    off_t* data_file_size,
781    vector<AnnotatedOperation>* rootfs_ops,
782    vector<AnnotatedOperation>* kernel_ops) {
783  unique_ptr<Ext2Filesystem> old_fs = Ext2Filesystem::CreateFromFile(
784      config.source.rootfs_part);
785  unique_ptr<Ext2Filesystem> new_fs = Ext2Filesystem::CreateFromFile(
786      config.target.rootfs_part);
787
788  off_t chunk_blocks = config.chunk_size == -1 ? -1 : (
789      config.chunk_size / config.block_size);
790
791  rootfs_ops->clear();
792  TEST_AND_RETURN_FALSE(DeltaReadFilesystem(rootfs_ops,
793                                            config.source.rootfs_part,
794                                            config.target.rootfs_part,
795                                            old_fs.get(),
796                                            new_fs.get(),
797                                            chunk_blocks,
798                                            data_file_fd,
799                                            data_file_size,
800                                            true));  // src_ops_allowed
801  LOG(INFO) << "done reading normal files";
802
803  // Read kernel partition
804  TEST_AND_RETURN_FALSE(
805      DeltaCompressKernelPartition(config.source.kernel_part,
806                                   config.target.kernel_part,
807                                   config.source.kernel_size,
808                                   config.target.kernel_size,
809                                   config.block_size,
810                                   kernel_ops,
811                                   data_file_fd,
812                                   data_file_size,
813                                   true));  // src_ops_allowed
814  LOG(INFO) << "done reading kernel";
815
816  TEST_AND_RETURN_FALSE(FragmentOperations(rootfs_ops,
817                                           config.target.rootfs_part,
818                                           data_file_fd,
819                                           data_file_size));
820  TEST_AND_RETURN_FALSE(FragmentOperations(kernel_ops,
821                                           config.target.kernel_part,
822                                           data_file_fd,
823                                           data_file_size));
824  SortOperationsByDestination(rootfs_ops);
825  SortOperationsByDestination(kernel_ops);
826  // TODO(alliewood): Change merge operations to use config.chunk_size once
827  // specifying chunk_size on the command line works. crbug/485397.
828  TEST_AND_RETURN_FALSE(MergeOperations(rootfs_ops,
829                                        kDefaultChunkSize,
830                                        config.target.rootfs_part,
831                                        data_file_fd,
832                                        data_file_size));
833  TEST_AND_RETURN_FALSE(MergeOperations(kernel_ops,
834                                        kDefaultChunkSize,
835                                        config.target.kernel_part,
836                                        data_file_fd,
837                                        data_file_size));
838  return true;
839}
840
841bool GenerateUpdatePayloadFile(
842    const PayloadGenerationConfig& config,
843    const string& output_path,
844    const string& private_key_path,
845    uint64_t* metadata_size) {
846  if (config.is_delta) {
847    LOG_IF(WARNING, config.source.rootfs_size != config.target.rootfs_size)
848        << "Old and new images have different block counts.";
849    // TODO(deymo): Our tools only support growing the filesystem size during
850    // an update. Remove this check when that's fixed. crbug.com/192136
851    LOG_IF(FATAL, config.source.rootfs_size > config.target.rootfs_size)
852        << "Shirking the rootfs size is not supported at the moment.";
853  }
854
855  // Sanity checks for the partition size.
856  LOG(INFO) << "Rootfs partition size: " << config.rootfs_partition_size;
857  LOG(INFO) << "Actual filesystem size: " << config.target.rootfs_size;
858
859  LOG(INFO) << "Block count: "
860            << config.target.rootfs_size / config.block_size;
861
862  const string kTempFileTemplate("CrAU_temp_data.XXXXXX");
863  string temp_file_path;
864  unique_ptr<ScopedPathUnlinker> temp_file_unlinker;
865  off_t data_file_size = 0;
866
867  LOG(INFO) << "Reading files...";
868
869  // Create empty protobuf Manifest object
870  DeltaArchiveManifest manifest;
871  manifest.set_minor_version(config.minor_version);
872
873  vector<AnnotatedOperation> rootfs_ops;
874  vector<AnnotatedOperation> kernel_ops;
875
876  // Select payload generation strategy based on the config.
877  unique_ptr<OperationsGenerator> strategy;
878  if (config.is_delta) {
879    // We don't efficiently support deltas on squashfs. For now, we will
880    // produce full operations in that case.
881    if (utils::IsSquashfsFilesystem(config.target.rootfs_part)) {
882      LOG(INFO) << "Using generator FullUpdateGenerator::Run for squashfs "
883                   "deltas";
884      strategy.reset(new FullUpdateGenerator());
885    } else if (utils::IsExtFilesystem(config.target.rootfs_part)) {
886      // Delta update (with possibly a full kernel update).
887      if (config.minor_version == kInPlaceMinorPayloadVersion) {
888        LOG(INFO) << "Using generator InplaceGenerator::GenerateInplaceDelta";
889        strategy.reset(new InplaceGenerator());
890      } else if (config.minor_version == kSourceMinorPayloadVersion) {
891        LOG(INFO) << "Using generator DeltaDiffGenerator::GenerateSourceDelta";
892        strategy.reset(new DeltaDiffGenerator());
893      } else {
894        LOG(ERROR) << "Unsupported minor version given for delta payload: "
895                   << config.minor_version;
896        return false;
897      }
898    } else {
899      LOG(ERROR) << "Unsupported filesystem for delta payload in "
900                 << config.target.rootfs_part;
901      return false;
902    }
903  } else {
904    // Full update.
905    LOG(INFO) << "Using generator FullUpdateGenerator::Run";
906    strategy.reset(new FullUpdateGenerator());
907  }
908
909  {
910    int data_file_fd;
911    TEST_AND_RETURN_FALSE(
912        utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &data_file_fd));
913    temp_file_unlinker.reset(new ScopedPathUnlinker(temp_file_path));
914    TEST_AND_RETURN_FALSE(data_file_fd >= 0);
915    ScopedFdCloser data_file_fd_closer(&data_file_fd);
916
917    // Generate the operations using the strategy we selected above.
918    TEST_AND_RETURN_FALSE(strategy->GenerateOperations(config,
919                                                       data_file_fd,
920                                                       &data_file_size,
921                                                       &rootfs_ops,
922                                                       &kernel_ops));
923  }
924
925  if (!config.source.ImageInfoIsEmpty())
926    *(manifest.mutable_old_image_info()) = config.source.image_info;
927
928  if (!config.target.ImageInfoIsEmpty())
929    *(manifest.mutable_new_image_info()) = config.target.image_info;
930
931  // Filter the no-operations. OperationsGenerators should not output this kind
932  // of operations normally, but this is an extra step to fix that if
933  // happened.
934  DeltaDiffGenerator::FilterNoopOperations(&rootfs_ops);
935  DeltaDiffGenerator::FilterNoopOperations(&kernel_ops);
936
937  OperationNameMap op_name_map;
938  InstallOperationsToManifest(rootfs_ops, kernel_ops, &manifest, &op_name_map);
939  manifest.set_block_size(config.block_size);
940
941  // Reorder the data blobs with the newly ordered manifest.
942  string ordered_blobs_path;
943  TEST_AND_RETURN_FALSE(utils::MakeTempFile(
944      "CrAU_temp_data.ordered.XXXXXX",
945      &ordered_blobs_path,
946      nullptr));
947  ScopedPathUnlinker ordered_blobs_unlinker(ordered_blobs_path);
948  TEST_AND_RETURN_FALSE(
949      DeltaDiffGenerator::ReorderDataBlobs(&manifest,
950                                           temp_file_path,
951                                           ordered_blobs_path));
952  temp_file_unlinker.reset();
953
954  // Check that install op blobs are in order.
955  uint64_t next_blob_offset = 0;
956  {
957    for (int i = 0; i < (manifest.install_operations_size() +
958                         manifest.kernel_install_operations_size()); i++) {
959      DeltaArchiveManifest_InstallOperation* op =
960          i < manifest.install_operations_size() ?
961          manifest.mutable_install_operations(i) :
962          manifest.mutable_kernel_install_operations(
963              i - manifest.install_operations_size());
964      if (op->has_data_offset()) {
965        if (op->data_offset() != next_blob_offset) {
966          LOG(FATAL) << "bad blob offset! " << op->data_offset() << " != "
967                     << next_blob_offset;
968        }
969        next_blob_offset += op->data_length();
970      }
971    }
972  }
973
974  // Signatures appear at the end of the blobs. Note the offset in the
975  // manifest
976  if (!private_key_path.empty()) {
977    uint64_t signature_blob_length = 0;
978    TEST_AND_RETURN_FALSE(
979        PayloadSigner::SignatureBlobLength(vector<string>(1, private_key_path),
980                                           &signature_blob_length));
981    DeltaDiffGenerator::AddSignatureOp(
982        next_blob_offset, signature_blob_length, &manifest);
983  }
984
985  TEST_AND_RETURN_FALSE(InitializePartitionInfos(config, &manifest));
986
987  // Serialize protobuf
988  string serialized_manifest;
989
990  TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest));
991
992  LOG(INFO) << "Writing final delta file header...";
993  DirectFileWriter writer;
994  TEST_AND_RETURN_FALSE_ERRNO(writer.Open(output_path.c_str(),
995                                          O_WRONLY | O_CREAT | O_TRUNC,
996                                          0644) == 0);
997  ScopedFileWriterCloser writer_closer(&writer);
998
999  // Write header
1000  TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, strlen(kDeltaMagic)));
1001
1002  // Write major version number
1003  TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, kMajorVersionNumber));
1004
1005  // Write protobuf length
1006  TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer,
1007                                               serialized_manifest.size()));
1008
1009  // Write protobuf
1010  LOG(INFO) << "Writing final delta file protobuf... "
1011            << serialized_manifest.size();
1012  TEST_AND_RETURN_FALSE(writer.Write(serialized_manifest.data(),
1013                                     serialized_manifest.size()));
1014
1015  // Append the data blobs
1016  LOG(INFO) << "Writing final delta file data blobs...";
1017  int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
1018  ScopedFdCloser blobs_fd_closer(&blobs_fd);
1019  TEST_AND_RETURN_FALSE(blobs_fd >= 0);
1020  for (;;) {
1021    vector<char> buf(config.block_size);
1022    ssize_t rc = read(blobs_fd, buf.data(), buf.size());
1023    if (0 == rc) {
1024      // EOF
1025      break;
1026    }
1027    TEST_AND_RETURN_FALSE_ERRNO(rc > 0);
1028    TEST_AND_RETURN_FALSE(writer.Write(buf.data(), rc));
1029  }
1030
1031  // Write signature blob.
1032  if (!private_key_path.empty()) {
1033    LOG(INFO) << "Signing the update...";
1034    chromeos::Blob signature_blob;
1035    TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(
1036        output_path,
1037        vector<string>(1, private_key_path),
1038        &signature_blob));
1039    TEST_AND_RETURN_FALSE(writer.Write(signature_blob.data(),
1040                                       signature_blob.size()));
1041  }
1042
1043  *metadata_size =
1044      strlen(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
1045  ReportPayloadUsage(manifest, *metadata_size, op_name_map);
1046
1047  LOG(INFO) << "All done. Successfully created delta file with "
1048            << "metadata size = " << *metadata_size;
1049  return true;
1050}
1051
1052// Runs the bsdiff tool on two files and returns the resulting delta in
1053// 'out'. Returns true on success.
1054bool DeltaDiffGenerator::BsdiffFiles(const string& old_file,
1055                                     const string& new_file,
1056                                     chromeos::Blob* out) {
1057  const string kPatchFile = "delta.patchXXXXXX";
1058  string patch_file_path;
1059
1060  TEST_AND_RETURN_FALSE(
1061      utils::MakeTempFile(kPatchFile, &patch_file_path, nullptr));
1062
1063  vector<string> cmd;
1064  cmd.push_back(kBsdiffPath);
1065  cmd.push_back(old_file);
1066  cmd.push_back(new_file);
1067  cmd.push_back(patch_file_path);
1068
1069  int rc = 1;
1070  chromeos::Blob patch_file;
1071  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
1072  TEST_AND_RETURN_FALSE(rc == 0);
1073  TEST_AND_RETURN_FALSE(utils::ReadFile(patch_file_path, out));
1074  unlink(patch_file_path.c_str());
1075  return true;
1076}
1077
1078void DeltaDiffGenerator::AddSignatureOp(uint64_t signature_blob_offset,
1079                                        uint64_t signature_blob_length,
1080                                        DeltaArchiveManifest* manifest) {
1081  LOG(INFO) << "Making room for signature in file";
1082  manifest->set_signatures_offset(signature_blob_offset);
1083  LOG(INFO) << "set? " << manifest->has_signatures_offset();
1084  // Add a dummy op at the end to appease older clients
1085  DeltaArchiveManifest_InstallOperation* dummy_op =
1086      manifest->add_kernel_install_operations();
1087  dummy_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
1088  dummy_op->set_data_offset(signature_blob_offset);
1089  manifest->set_signatures_offset(signature_blob_offset);
1090  dummy_op->set_data_length(signature_blob_length);
1091  manifest->set_signatures_size(signature_blob_length);
1092  Extent* dummy_extent = dummy_op->add_dst_extents();
1093  // Tell the dummy op to write this data to a big sparse hole
1094  dummy_extent->set_start_block(kSparseHole);
1095  dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) /
1096                               kBlockSize);
1097}
1098
1099bool DeltaDiffGenerator::FragmentOperations(
1100    vector<AnnotatedOperation>* aops,
1101    const string& target_part_path,
1102    int data_fd,
1103    off_t* data_file_size) {
1104  vector<AnnotatedOperation> fragmented_aops;
1105  for (const AnnotatedOperation& aop : *aops) {
1106    if (aop.op.type() ==
1107        DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY) {
1108      TEST_AND_RETURN_FALSE(SplitSourceCopy(aop, &fragmented_aops));
1109    } else if ((aop.op.type() ==
1110                DeltaArchiveManifest_InstallOperation_Type_REPLACE) ||
1111               (aop.op.type() ==
1112                DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ)) {
1113      TEST_AND_RETURN_FALSE(SplitReplaceOrReplaceBz(aop, &fragmented_aops,
1114                                                    target_part_path, data_fd,
1115                                                    data_file_size));
1116    } else {
1117      fragmented_aops.push_back(aop);
1118    }
1119  }
1120  *aops = fragmented_aops;
1121  return true;
1122}
1123
1124void DeltaDiffGenerator::SortOperationsByDestination(
1125    vector<AnnotatedOperation>* aops) {
1126  sort(aops->begin(), aops->end(), CompareAopsByDestination);
1127}
1128
1129bool DeltaDiffGenerator::SplitSourceCopy(
1130    const AnnotatedOperation& original_aop,
1131    vector<AnnotatedOperation>* result_aops) {
1132  DeltaArchiveManifest_InstallOperation original_op = original_aop.op;
1133  TEST_AND_RETURN_FALSE(original_op.type() ==
1134                        DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
1135  // Keeps track of the index of curr_src_ext.
1136  int curr_src_ext_index = 0;
1137  Extent curr_src_ext = original_op.src_extents(curr_src_ext_index);
1138  for (int i = 0; i < original_op.dst_extents_size(); i++) {
1139    Extent dst_ext = original_op.dst_extents(i);
1140    // The new operation which will have only one dst extent.
1141    DeltaArchiveManifest_InstallOperation new_op;
1142    uint64_t blocks_left = dst_ext.num_blocks();
1143    while (blocks_left > 0) {
1144      if (curr_src_ext.num_blocks() <= blocks_left) {
1145        // If the curr_src_ext is smaller than dst_ext, add it.
1146        blocks_left -= curr_src_ext.num_blocks();
1147        *(new_op.add_src_extents()) = curr_src_ext;
1148        if (curr_src_ext_index + 1 < original_op.src_extents().size()) {
1149          curr_src_ext = original_op.src_extents(++curr_src_ext_index);
1150        } else {
1151          break;
1152        }
1153      } else {
1154        // Split src_exts that are bigger than the dst_ext we're dealing with.
1155        Extent first_ext;
1156        first_ext.set_num_blocks(blocks_left);
1157        first_ext.set_start_block(curr_src_ext.start_block());
1158        *(new_op.add_src_extents()) = first_ext;
1159        // Keep the second half of the split op.
1160        curr_src_ext.set_num_blocks(curr_src_ext.num_blocks() - blocks_left);
1161        curr_src_ext.set_start_block(curr_src_ext.start_block() + blocks_left);
1162        blocks_left -= first_ext.num_blocks();
1163      }
1164    }
1165    // Fix up our new operation and add it to the results.
1166    new_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
1167    *(new_op.add_dst_extents()) = dst_ext;
1168    new_op.set_src_length(dst_ext.num_blocks() * kBlockSize);
1169    new_op.set_dst_length(dst_ext.num_blocks() * kBlockSize);
1170
1171    AnnotatedOperation new_aop;
1172    new_aop.op = new_op;
1173    new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i);
1174    result_aops->push_back(new_aop);
1175  }
1176  if (curr_src_ext_index != original_op.src_extents().size() - 1) {
1177    LOG(FATAL) << "Incorrectly split SOURCE_COPY operation. Did not use all "
1178               << "source extents.";
1179  }
1180  return true;
1181}
1182
1183bool DeltaDiffGenerator::SplitReplaceOrReplaceBz(
1184    const AnnotatedOperation& original_aop,
1185    vector<AnnotatedOperation>* result_aops,
1186    const string& target_part_path,
1187    int data_fd,
1188    off_t* data_file_size) {
1189  DeltaArchiveManifest_InstallOperation original_op = original_aop.op;
1190  const bool is_replace =
1191      original_op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE;
1192  TEST_AND_RETURN_FALSE(
1193      is_replace ||
1194      (original_op.type() ==
1195       DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ));
1196
1197  uint32_t data_offset = original_op.data_offset();
1198  for (int i = 0; i < original_op.dst_extents_size(); i++) {
1199    Extent dst_ext = original_op.dst_extents(i);
1200    // Make a new operation with only one dst extent.
1201    DeltaArchiveManifest_InstallOperation new_op;
1202    *(new_op.add_dst_extents()) = dst_ext;
1203    uint32_t data_size = dst_ext.num_blocks() * kBlockSize;
1204    new_op.set_dst_length(data_size);
1205    // If this is a REPLACE, attempt to reuse portions of the existing blob.
1206    if (is_replace) {
1207      new_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
1208      new_op.set_data_length(data_size);
1209      new_op.set_data_offset(data_offset);
1210      data_offset += data_size;
1211    }
1212
1213    AnnotatedOperation new_aop;
1214    new_aop.op = new_op;
1215    new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i);
1216    TEST_AND_RETURN_FALSE(AddDataAndSetType(&new_aop, target_part_path, data_fd,
1217                                            data_file_size));
1218
1219    result_aops->push_back(new_aop);
1220  }
1221  return true;
1222}
1223
1224bool DeltaDiffGenerator::MergeOperations(vector<AnnotatedOperation>* aops,
1225                                         off_t chunk_size,
1226                                         const string& target_part_path,
1227                                         int data_fd,
1228                                         off_t* data_file_size) {
1229  vector<AnnotatedOperation> new_aops;
1230  for (const AnnotatedOperation& curr_aop : *aops) {
1231    if (new_aops.empty()) {
1232      new_aops.push_back(curr_aop);
1233      continue;
1234    }
1235    AnnotatedOperation& last_aop = new_aops.back();
1236
1237    if (last_aop.op.dst_extents_size() <= 0 ||
1238        curr_aop.op.dst_extents_size() <= 0) {
1239      new_aops.push_back(curr_aop);
1240      continue;
1241    }
1242    uint32_t last_dst_idx = last_aop.op.dst_extents_size() - 1;
1243    uint32_t last_end_block =
1244        last_aop.op.dst_extents(last_dst_idx).start_block() +
1245        last_aop.op.dst_extents(last_dst_idx).num_blocks();
1246    uint32_t curr_start_block = curr_aop.op.dst_extents(0).start_block();
1247    uint32_t combined_block_count =
1248        last_aop.op.dst_extents(last_dst_idx).num_blocks() +
1249        curr_aop.op.dst_extents(0).num_blocks();
1250    bool good_op_type =
1251        curr_aop.op.type() ==
1252            DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY ||
1253        curr_aop.op.type() ==
1254            DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
1255        curr_aop.op.type() ==
1256            DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ;
1257    if (good_op_type &&
1258        last_aop.op.type() == curr_aop.op.type() &&
1259        last_end_block == curr_start_block &&
1260        static_cast<off_t>(combined_block_count * kBlockSize) <= chunk_size) {
1261      // If the operations have the same type (which is a type that we can
1262      // merge), are contiguous, are fragmented to have one destination extent,
1263      // and their combined block count would be less than chunk size, merge
1264      // them.
1265      last_aop.name = base::StringPrintf("%s,%s",
1266                                         last_aop.name.c_str(),
1267                                         curr_aop.name.c_str());
1268
1269      ExtendExtents(last_aop.op.mutable_src_extents(),
1270                    curr_aop.op.src_extents());
1271      if (curr_aop.op.src_length() > 0)
1272        last_aop.op.set_src_length(last_aop.op.src_length() +
1273                                   curr_aop.op.src_length());
1274      ExtendExtents(last_aop.op.mutable_dst_extents(),
1275                    curr_aop.op.dst_extents());
1276      if (curr_aop.op.dst_length() > 0)
1277        last_aop.op.set_dst_length(last_aop.op.dst_length() +
1278                                   curr_aop.op.dst_length());
1279      // Set the data length to zero so we know to add the blob later.
1280      if (curr_aop.op.type() ==
1281          DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
1282          curr_aop.op.type() ==
1283          DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
1284        last_aop.op.set_data_length(0);
1285      }
1286    } else {
1287      // Otherwise just include the extent as is.
1288      new_aops.push_back(curr_aop);
1289    }
1290  }
1291
1292  // Set the blobs for REPLACE/REPLACE_BZ operations that have been merged.
1293  for (AnnotatedOperation& curr_aop : new_aops) {
1294    if (curr_aop.op.data_length() == 0 &&
1295        (curr_aop.op.type() ==
1296            DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
1297         curr_aop.op.type() ==
1298            DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ)) {
1299      TEST_AND_RETURN_FALSE(AddDataAndSetType(&curr_aop, target_part_path,
1300                                              data_fd, data_file_size));
1301    }
1302  }
1303
1304  *aops = new_aops;
1305  return true;
1306}
1307
1308void DeltaDiffGenerator::ExtendExtents(
1309    google::protobuf::RepeatedPtrField<Extent>* extents,
1310    const google::protobuf::RepeatedPtrField<Extent>& extents_to_add) {
1311  vector<Extent> extents_vector;
1312  vector<Extent> extents_to_add_vector;
1313  ExtentsToVector(*extents, &extents_vector);
1314  ExtentsToVector(extents_to_add, &extents_to_add_vector);
1315  extents_vector.insert(extents_vector.end(),
1316                        extents_to_add_vector.begin(),
1317                        extents_to_add_vector.end());
1318  NormalizeExtents(&extents_vector);
1319  extents->Clear();
1320  StoreExtents(extents_vector, extents);
1321}
1322
1323bool DeltaDiffGenerator::AddDataAndSetType(AnnotatedOperation* aop,
1324                                           const string& target_part_path,
1325                                           int data_fd,
1326                                           off_t* data_file_size) {
1327  TEST_AND_RETURN_FALSE(
1328      aop->op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
1329      aop->op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
1330
1331  chromeos::Blob data(aop->op.dst_length());
1332  vector<Extent> dst_extents;
1333  ExtentsToVector(aop->op.dst_extents(), &dst_extents);
1334  TEST_AND_RETURN_FALSE(utils::ReadExtents(target_part_path,
1335                                           dst_extents,
1336                                           &data,
1337                                           data.size(),
1338                                           kBlockSize));
1339
1340  chromeos::Blob data_bz;
1341  TEST_AND_RETURN_FALSE(BzipCompress(data, &data_bz));
1342  CHECK(!data_bz.empty());
1343
1344  chromeos::Blob* data_p = nullptr;
1345  DeltaArchiveManifest_InstallOperation_Type new_op_type;
1346  if (data_bz.size() < data.size()) {
1347    new_op_type = DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ;
1348    data_p = &data_bz;
1349  } else {
1350    new_op_type = DeltaArchiveManifest_InstallOperation_Type_REPLACE;
1351    data_p = &data;
1352  }
1353
1354  // If the operation already points to a data blob, check whether it's
1355  // identical to the new one, in which case don't add it.
1356  if (aop->op.type() == new_op_type &&
1357      aop->op.data_length() == data_p->size()) {
1358    chromeos::Blob current_data(data_p->size());
1359    ssize_t bytes_read;
1360    TEST_AND_RETURN_FALSE(utils::PReadAll(data_fd,
1361                                          current_data.data(),
1362                                          aop->op.data_length(),
1363                                          aop->op.data_offset(),
1364                                          &bytes_read));
1365    TEST_AND_RETURN_FALSE(bytes_read ==
1366                          static_cast<ssize_t>(aop->op.data_length()));
1367    if (current_data == *data_p)
1368      data_p = nullptr;
1369  }
1370
1371  if (data_p) {
1372    aop->op.set_type(new_op_type);
1373    aop->SetOperationBlob(data_p, data_fd, data_file_size);
1374  }
1375
1376  return true;
1377}
1378
1379};  // namespace chromeos_update_engine
1380