delta_diff_utils.cc revision 8cc502dacbccdab96824d42287f230ce04004784
1// Copyright 2015 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_utils.h"
6
7#include <algorithm>
8#include <map>
9
10#include <base/files/file_util.h>
11#include <base/format_macros.h>
12#include <base/strings/stringprintf.h>
13
14#include "update_engine/bzip.h"
15#include "update_engine/omaha_hash_calculator.h"
16#include "update_engine/payload_generator/block_mapping.h"
17#include "update_engine/payload_generator/delta_diff_generator.h"
18#include "update_engine/payload_generator/extent_ranges.h"
19#include "update_engine/payload_generator/extent_utils.h"
20#include "update_engine/subprocess.h"
21#include "update_engine/utils.h"
22
23using std::map;
24using std::string;
25using std::vector;
26
27namespace chromeos_update_engine {
28namespace {
29
30const char* const kBsdiffPath = "bsdiff";
31
32// The maximum destination size allowed for bsdiff. In general, bsdiff should
33// work for arbitrary big files, but the payload generation and payload
34// application requires a significant amount of RAM. We put a hard-limit of
35// 200 MiB that should not affect any released board, but will limit the
36// Chrome binary in ASan builders.
37const uint64_t kMaxBsdiffDestinationSize = 200 * 1024 * 1024;  // bytes
38
39// Process a range of blocks from |range_start| to |range_end| in the extent at
40// position |*idx_p| of |extents|. If |do_remove| is true, this range will be
41// removed, which may cause the extent to be trimmed, split or removed entirely.
42// The value of |*idx_p| is updated to point to the next extent to be processed.
43// Returns true iff the next extent to process is a new or updated one.
44bool ProcessExtentBlockRange(vector<Extent>* extents, size_t* idx_p,
45                             const bool do_remove, uint64_t range_start,
46                             uint64_t range_end) {
47  size_t idx = *idx_p;
48  uint64_t start_block = (*extents)[idx].start_block();
49  uint64_t num_blocks = (*extents)[idx].num_blocks();
50  uint64_t range_size = range_end - range_start;
51
52  if (do_remove) {
53    if (range_size == num_blocks) {
54      // Remove the entire extent.
55      extents->erase(extents->begin() + idx);
56    } else if (range_end == num_blocks) {
57      // Trim the end of the extent.
58      (*extents)[idx].set_num_blocks(num_blocks - range_size);
59      idx++;
60    } else if (range_start == 0) {
61      // Trim the head of the extent.
62      (*extents)[idx].set_start_block(start_block + range_size);
63      (*extents)[idx].set_num_blocks(num_blocks - range_size);
64    } else {
65      // Trim the middle, splitting the remainder into two parts.
66      (*extents)[idx].set_num_blocks(range_start);
67      Extent e;
68      e.set_start_block(start_block + range_end);
69      e.set_num_blocks(num_blocks - range_end);
70      idx++;
71      extents->insert(extents->begin() + idx, e);
72    }
73  } else if (range_end == num_blocks) {
74    // Done with this extent.
75    idx++;
76  } else {
77    return false;
78  }
79
80  *idx_p = idx;
81  return true;
82}
83
84// Remove identical corresponding block ranges in |src_extents| and
85// |dst_extents|. Used for preventing moving of blocks onto themselves during
86// MOVE operations. The value of |total_bytes| indicates the actual length of
87// content; this may be slightly less than the total size of blocks, in which
88// case the last block is only partly occupied with data. Returns the total
89// number of bytes removed.
90size_t RemoveIdenticalBlockRanges(vector<Extent>* src_extents,
91                                  vector<Extent>* dst_extents,
92                                  const size_t total_bytes) {
93  size_t src_idx = 0;
94  size_t dst_idx = 0;
95  uint64_t src_offset = 0, dst_offset = 0;
96  bool new_src = true, new_dst = true;
97  size_t removed_bytes = 0, nonfull_block_bytes;
98  bool do_remove = false;
99  while (src_idx < src_extents->size() && dst_idx < dst_extents->size()) {
100    if (new_src) {
101      src_offset = 0;
102      new_src = false;
103    }
104    if (new_dst) {
105      dst_offset = 0;
106      new_dst = false;
107    }
108
109    do_remove = ((*src_extents)[src_idx].start_block() + src_offset ==
110                 (*dst_extents)[dst_idx].start_block() + dst_offset);
111
112    uint64_t src_num_blocks = (*src_extents)[src_idx].num_blocks();
113    uint64_t dst_num_blocks = (*dst_extents)[dst_idx].num_blocks();
114    uint64_t min_num_blocks = std::min(src_num_blocks - src_offset,
115                                       dst_num_blocks - dst_offset);
116    uint64_t prev_src_offset = src_offset;
117    uint64_t prev_dst_offset = dst_offset;
118    src_offset += min_num_blocks;
119    dst_offset += min_num_blocks;
120
121    new_src = ProcessExtentBlockRange(src_extents, &src_idx, do_remove,
122                                      prev_src_offset, src_offset);
123    new_dst = ProcessExtentBlockRange(dst_extents, &dst_idx, do_remove,
124                                      prev_dst_offset, dst_offset);
125    if (do_remove)
126      removed_bytes += min_num_blocks * kBlockSize;
127  }
128
129  // If we removed the last block and this block is only partly used by file
130  // content, deduct the unused portion from the total removed byte count.
131  if (do_remove && (nonfull_block_bytes = total_bytes % kBlockSize))
132    removed_bytes -= kBlockSize - nonfull_block_bytes;
133
134  return removed_bytes;
135}
136
137}  // namespace
138
139namespace diff_utils {
140
141bool DeltaReadPartition(
142    vector<AnnotatedOperation>* aops,
143    const PartitionConfig& old_part,
144    const PartitionConfig& new_part,
145    ssize_t hard_chunk_blocks,
146    size_t soft_chunk_blocks,
147    BlobFileWriter* blob_file,
148    bool src_ops_allowed) {
149  ExtentRanges old_visited_blocks;
150  ExtentRanges new_visited_blocks;
151
152  TEST_AND_RETURN_FALSE(DeltaMovedAndZeroBlocks(
153      aops,
154      old_part.path,
155      new_part.path,
156      old_part.fs_interface ? old_part.fs_interface->GetBlockCount() : 0,
157      new_part.fs_interface->GetBlockCount(),
158      soft_chunk_blocks,
159      src_ops_allowed,
160      blob_file,
161      &old_visited_blocks,
162      &new_visited_blocks));
163
164  map<string, vector<Extent>> old_files_map;
165  if (old_part.fs_interface) {
166    vector<FilesystemInterface::File> old_files;
167    old_part.fs_interface->GetFiles(&old_files);
168    for (const FilesystemInterface::File& file : old_files)
169      old_files_map[file.name] = file.extents;
170  }
171
172  TEST_AND_RETURN_FALSE(new_part.fs_interface);
173  vector<FilesystemInterface::File> new_files;
174  new_part.fs_interface->GetFiles(&new_files);
175
176  // The processing is very straightforward here, we generate operations for
177  // every file (and pseudo-file such as the metadata) in the new filesystem
178  // based on the file with the same name in the old filesystem, if any.
179  // Files with overlapping data blocks (like hardlinks or filesystems with tail
180  // packing or compression where the blocks store more than one file) are only
181  // generated once in the new image, but are also used only once from the old
182  // image due to some simplifications (see below).
183  for (const FilesystemInterface::File& new_file : new_files) {
184    // Ignore the files in the new filesystem without blocks. Symlinks with
185    // data blocks (for example, symlinks bigger than 60 bytes in ext2) are
186    // handled as normal files. We also ignore blocks that were already
187    // processed by a previous file.
188    vector<Extent> new_file_extents = FilterExtentRanges(
189        new_file.extents, new_visited_blocks);
190    new_visited_blocks.AddExtents(new_file_extents);
191
192    if (new_file_extents.empty())
193      continue;
194
195    LOG(INFO) << "Encoding file " << new_file.name << " ("
196              << BlocksInExtents(new_file_extents) << " blocks)";
197
198    // We can't visit each dst image inode more than once, as that would
199    // duplicate work. Here, we avoid visiting each source image inode
200    // more than once. Technically, we could have multiple operations
201    // that read the same blocks from the source image for diffing, but
202    // we choose not to avoid complexity. Eventually we will move away
203    // from using a graph/cycle detection/etc to generate diffs, and at that
204    // time, it will be easy (non-complex) to have many operations read
205    // from the same source blocks. At that time, this code can die. -adlr
206    vector<Extent> old_file_extents = FilterExtentRanges(
207        old_files_map[new_file.name], old_visited_blocks);
208    old_visited_blocks.AddExtents(old_file_extents);
209
210    TEST_AND_RETURN_FALSE(DeltaReadFile(
211        aops,
212        old_part.path,
213        new_part.path,
214        old_file_extents,
215        new_file_extents,
216        new_file.name,  // operation name
217        hard_chunk_blocks,
218        blob_file,
219        src_ops_allowed));
220  }
221  // Process all the blocks not included in any file. We provided all the unused
222  // blocks in the old partition as available data.
223  vector<Extent> new_unvisited = {
224      ExtentForRange(0, new_part.size / kBlockSize)};
225  new_unvisited = FilterExtentRanges(new_unvisited, new_visited_blocks);
226  if (new_unvisited.empty())
227    return true;
228
229  vector<Extent> old_unvisited;
230  if (old_part.fs_interface) {
231    old_unvisited.push_back(ExtentForRange(0, old_part.size / kBlockSize));
232    old_unvisited = FilterExtentRanges(old_unvisited, old_visited_blocks);
233  }
234
235  LOG(INFO) << "Scanning " << BlocksInExtents(new_unvisited)
236            << " unwritten blocks using chunk size of "
237            << soft_chunk_blocks << " blocks.";
238  // We use the soft_chunk_blocks limit for the <non-file-data> as we don't
239  // really know the structure of this data and we should not expect it to have
240  // redundancy between partitions.
241  TEST_AND_RETURN_FALSE(DeltaReadFile(
242      aops,
243      old_part.path,
244      new_part.path,
245      old_unvisited,
246      new_unvisited,
247      "<non-file-data>",  // operation name
248      soft_chunk_blocks,
249      blob_file,
250      src_ops_allowed));
251
252  return true;
253}
254
255bool DeltaMovedAndZeroBlocks(
256    vector<AnnotatedOperation>* aops,
257    const string& old_part,
258    const string& new_part,
259    size_t old_num_blocks,
260    size_t new_num_blocks,
261    ssize_t chunk_blocks,
262    bool src_ops_allowed,
263    BlobFileWriter* blob_file,
264    ExtentRanges* old_visited_blocks,
265    ExtentRanges* new_visited_blocks) {
266  vector<BlockMapping::BlockId> old_block_ids;
267  vector<BlockMapping::BlockId> new_block_ids;
268  TEST_AND_RETURN_FALSE(MapPartitionBlocks(old_part,
269                                           new_part,
270                                           old_num_blocks * kBlockSize,
271                                           new_num_blocks * kBlockSize,
272                                           kBlockSize,
273                                           &old_block_ids,
274                                           &new_block_ids));
275
276  // For minor-version=1, we map all the blocks that didn't move, regardless of
277  // the contents since they are already copied and no operation is required.
278  if (!src_ops_allowed) {
279    uint64_t num_blocks = std::min(old_num_blocks, new_num_blocks);
280    for (uint64_t block = 0; block < num_blocks; block++) {
281      if (old_block_ids[block] == new_block_ids[block] &&
282          !old_visited_blocks->ContainsBlock(block) &&
283          !new_visited_blocks->ContainsBlock(block)) {
284        old_visited_blocks->AddBlock(block);
285        new_visited_blocks->AddBlock(block);
286      }
287    }
288  }
289
290  // A mapping from the block_id to the list of block numbers with that block id
291  // in the old partition. This is used to lookup where in the old partition
292  // is a block from the new partition.
293  map<BlockMapping::BlockId, vector<uint64_t>> old_blocks_map;
294
295  for (uint64_t block = old_num_blocks; block-- > 0; ) {
296    if (old_block_ids[block] != 0 && !old_visited_blocks->ContainsBlock(block))
297      old_blocks_map[old_block_ids[block]].push_back(block);
298  }
299
300  // The collection of blocks in the new partition with just zeros. This is a
301  // common case for free-space that's also problematic for bsdiff, so we want
302  // to optimize it using REPLACE_BZ operations. The blob for a REPLACE_BZ of
303  // just zeros is so small that it doesn't make sense to spend the I/O reading
304  // zeros from the old partition.
305  vector<Extent> new_zeros;
306
307  vector<Extent> old_identical_blocks;
308  vector<Extent> new_identical_blocks;
309
310  for (uint64_t block = 0; block < new_num_blocks; block++) {
311    // Only produce operations for blocks that were not yet visited.
312    if (new_visited_blocks->ContainsBlock(block))
313      continue;
314    if (new_block_ids[block] == 0) {
315      AppendBlockToExtents(&new_zeros, block);
316      continue;
317    }
318
319    auto old_blocks_map_it = old_blocks_map.find(new_block_ids[block]);
320    // Check if the block exists in the old partition at all.
321    if (old_blocks_map_it == old_blocks_map.end() ||
322        old_blocks_map_it->second.empty())
323      continue;
324
325    AppendBlockToExtents(&old_identical_blocks,
326                         old_blocks_map_it->second.back());
327    AppendBlockToExtents(&new_identical_blocks, block);
328    // We can't reuse source blocks in minor version 1 because the cycle
329    // breaking algorithm doesn't support that.
330    if (!src_ops_allowed)
331      old_blocks_map_it->second.pop_back();
332  }
333
334  // Produce operations for the zero blocks split per output extent.
335  size_t num_ops = aops->size();
336  new_visited_blocks->AddExtents(new_zeros);
337  for (Extent extent : new_zeros) {
338    TEST_AND_RETURN_FALSE(DeltaReadFile(
339        aops,
340        "",
341        new_part,
342        vector<Extent>(),  // old_extents
343        vector<Extent>{extent},  // new_extents
344        "<zeros>",
345        chunk_blocks,
346        blob_file,
347        src_ops_allowed));
348  }
349  LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
350            << BlocksInExtents(new_zeros) << " zeroed blocks";
351
352  // Produce MOVE/SOURCE_COPY operations for the moved blocks.
353  num_ops = aops->size();
354  if (chunk_blocks == -1)
355    chunk_blocks = new_num_blocks;
356  uint64_t used_blocks = 0;
357  old_visited_blocks->AddExtents(old_identical_blocks);
358  new_visited_blocks->AddExtents(new_identical_blocks);
359  for (Extent extent : new_identical_blocks) {
360    // We split the operation at the extent boundary or when bigger than
361    // chunk_blocks.
362    for (uint64_t op_block_offset = 0; op_block_offset < extent.num_blocks();
363         op_block_offset += chunk_blocks) {
364      aops->emplace_back();
365      AnnotatedOperation* aop = &aops->back();
366      aop->name = "<identical-blocks>";
367      aop->op.set_type(src_ops_allowed ?
368                       DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY :
369                       DeltaArchiveManifest_InstallOperation_Type_MOVE);
370
371      uint64_t chunk_num_blocks =
372        std::min(extent.num_blocks() - op_block_offset,
373                 static_cast<uint64_t>(chunk_blocks));
374
375      // The current operation represents the move/copy operation for the
376      // sublist starting at |used_blocks| of length |chunk_num_blocks| where
377      // the src and dst are from |old_identical_blocks| and
378      // |new_identical_blocks| respectively.
379      StoreExtents(
380          ExtentsSublist(old_identical_blocks, used_blocks, chunk_num_blocks),
381          aop->op.mutable_src_extents());
382
383      Extent* op_dst_extent = aop->op.add_dst_extents();
384      op_dst_extent->set_start_block(extent.start_block() + op_block_offset);
385      op_dst_extent->set_num_blocks(chunk_num_blocks);
386      CHECK(
387          vector<Extent>{*op_dst_extent} ==  // NOLINT(whitespace/braces)
388          ExtentsSublist(new_identical_blocks, used_blocks, chunk_num_blocks));
389
390      used_blocks += chunk_num_blocks;
391    }
392  }
393  LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
394            << used_blocks << " identical blocks moved";
395
396  return true;
397}
398
399bool DeltaReadFile(
400    vector<AnnotatedOperation>* aops,
401    const string& old_part,
402    const string& new_part,
403    const vector<Extent>& old_extents,
404    const vector<Extent>& new_extents,
405    const string& name,
406    ssize_t chunk_blocks,
407    BlobFileWriter* blob_file,
408    bool src_ops_allowed) {
409  chromeos::Blob data;
410  DeltaArchiveManifest_InstallOperation operation;
411
412  uint64_t total_blocks = BlocksInExtents(new_extents);
413  if (chunk_blocks == -1)
414    chunk_blocks = total_blocks;
415
416  // If bsdiff breaks again, blacklist the problem file by using:
417  //   bsdiff_allowed = (name != "/foo/bar")
418  //
419  // TODO(dgarrett): chromium-os:15274 connect this test to the command line.
420  bool bsdiff_allowed = true;
421  if (static_cast<uint64_t>(chunk_blocks) * kBlockSize >
422      kMaxBsdiffDestinationSize) {
423    bsdiff_allowed = false;
424  }
425
426  if (!bsdiff_allowed) {
427    LOG(INFO) << "bsdiff blacklisting: " << name;
428  }
429
430  for (uint64_t block_offset = 0; block_offset < total_blocks;
431      block_offset += chunk_blocks) {
432    // Split the old/new file in the same chunks. Note that this could drop
433    // some information from the old file used for the new chunk. If the old
434    // file is smaller (or even empty when there's no old file) the chunk will
435    // also be empty.
436    vector<Extent> old_extents_chunk = ExtentsSublist(
437        old_extents, block_offset, chunk_blocks);
438    vector<Extent> new_extents_chunk = ExtentsSublist(
439        new_extents, block_offset, chunk_blocks);
440    NormalizeExtents(&old_extents_chunk);
441    NormalizeExtents(&new_extents_chunk);
442
443    TEST_AND_RETURN_FALSE(ReadExtentsToDiff(old_part,
444                                            new_part,
445                                            old_extents_chunk,
446                                            new_extents_chunk,
447                                            bsdiff_allowed,
448                                            &data,
449                                            &operation,
450                                            src_ops_allowed));
451
452    // Check if the operation writes nothing.
453    if (operation.dst_extents_size() == 0) {
454      if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
455        LOG(INFO) << "Empty MOVE operation ("
456                  << name << "), skipping";
457        continue;
458      } else {
459        LOG(ERROR) << "Empty non-MOVE operation";
460        return false;
461      }
462    }
463
464    // Now, insert into the list of operations.
465    AnnotatedOperation aop;
466    aop.name = name;
467    if (static_cast<uint64_t>(chunk_blocks) < total_blocks) {
468      aop.name = base::StringPrintf("%s:%" PRIu64,
469                                    name.c_str(), block_offset / chunk_blocks);
470    }
471    aop.op = operation;
472
473    // Write the data
474    if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE &&
475        operation.type() !=
476            DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY) {
477      TEST_AND_RETURN_FALSE(aop.SetOperationBlob(&data, blob_file));
478    } else {
479      TEST_AND_RETURN_FALSE(blob_file->StoreBlob(data) != -1);
480    }
481    aops->emplace_back(aop);
482  }
483  return true;
484}
485
486bool ReadExtentsToDiff(const string& old_part,
487                       const string& new_part,
488                       const vector<Extent>& old_extents,
489                       const vector<Extent>& new_extents,
490                       bool bsdiff_allowed,
491                       chromeos::Blob* out_data,
492                       DeltaArchiveManifest_InstallOperation* out_op,
493                       bool src_ops_allowed) {
494  DeltaArchiveManifest_InstallOperation operation;
495  // Data blob that will be written to delta file.
496  const chromeos::Blob* data_blob = nullptr;
497
498  // We read blocks from old_extents and write blocks to new_extents.
499  uint64_t blocks_to_read = BlocksInExtents(old_extents);
500  uint64_t blocks_to_write = BlocksInExtents(new_extents);
501
502  // Make copies of the extents so we can modify them.
503  vector<Extent> src_extents = old_extents;
504  vector<Extent> dst_extents = new_extents;
505
506  // Read in bytes from new data.
507  chromeos::Blob new_data;
508  TEST_AND_RETURN_FALSE(utils::ReadExtents(new_part,
509                                           new_extents,
510                                           &new_data,
511                                           kBlockSize * blocks_to_write,
512                                           kBlockSize));
513  TEST_AND_RETURN_FALSE(!new_data.empty());
514
515
516  // Using a REPLACE is always an option.
517  operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
518  data_blob = &new_data;
519
520  // Try compressing it with bzip2.
521  chromeos::Blob new_data_bz;
522  TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz));
523  CHECK(!new_data_bz.empty());
524  if (new_data_bz.size() < data_blob->size()) {
525    // A REPLACE_BZ is better.
526    operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
527    data_blob = &new_data_bz;
528  }
529
530  chromeos::Blob old_data;
531  chromeos::Blob empty_blob;
532  chromeos::Blob bsdiff_delta;
533  if (blocks_to_read > 0) {
534    // Read old data.
535    TEST_AND_RETURN_FALSE(
536        utils::ReadExtents(old_part, src_extents, &old_data,
537                           kBlockSize * blocks_to_read, kBlockSize));
538    if (old_data == new_data) {
539      // No change in data.
540      if (src_ops_allowed) {
541        operation.set_type(
542            DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
543      } else {
544        operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
545      }
546      data_blob = &empty_blob;
547    } else if (bsdiff_allowed) {
548      // If the source file is considered bsdiff safe (no bsdiff bugs
549      // triggered), see if BSDIFF encoding is smaller.
550      base::FilePath old_chunk;
551      TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&old_chunk));
552      ScopedPathUnlinker old_unlinker(old_chunk.value());
553      TEST_AND_RETURN_FALSE(
554          utils::WriteFile(old_chunk.value().c_str(),
555                           old_data.data(), old_data.size()));
556      base::FilePath new_chunk;
557      TEST_AND_RETURN_FALSE(base::CreateTemporaryFile(&new_chunk));
558      ScopedPathUnlinker new_unlinker(new_chunk.value());
559      TEST_AND_RETURN_FALSE(
560          utils::WriteFile(new_chunk.value().c_str(),
561                           new_data.data(), new_data.size()));
562
563      TEST_AND_RETURN_FALSE(
564          BsdiffFiles(old_chunk.value(), new_chunk.value(), &bsdiff_delta));
565      CHECK_GT(bsdiff_delta.size(), static_cast<chromeos::Blob::size_type>(0));
566      if (bsdiff_delta.size() < data_blob->size()) {
567        if (src_ops_allowed) {
568          operation.set_type(
569              DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF);
570        } else {
571          operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
572        }
573        data_blob = &bsdiff_delta;
574      }
575    }
576  }
577
578  size_t removed_bytes = 0;
579  // Remove identical src/dst block ranges in MOVE operations.
580  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
581    removed_bytes = RemoveIdenticalBlockRanges(
582        &src_extents, &dst_extents, new_data.size());
583  }
584  // Set legacy src_length and dst_length fields.
585  operation.set_src_length(old_data.size() - removed_bytes);
586  operation.set_dst_length(new_data.size() - removed_bytes);
587
588  // Embed extents in the operation.
589  StoreExtents(src_extents, operation.mutable_src_extents());
590  StoreExtents(dst_extents, operation.mutable_dst_extents());
591
592  // Replace operations should not have source extents.
593  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
594      operation.type() ==
595          DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
596    operation.clear_src_extents();
597    operation.clear_src_length();
598  }
599
600  *out_data = std::move(*data_blob);
601  *out_op = operation;
602
603  return true;
604}
605
606// Runs the bsdiff tool on two files and returns the resulting delta in
607// 'out'. Returns true on success.
608bool BsdiffFiles(const string& old_file,
609                 const string& new_file,
610                 chromeos::Blob* out) {
611  const string kPatchFile = "delta.patchXXXXXX";
612  string patch_file_path;
613
614  TEST_AND_RETURN_FALSE(
615      utils::MakeTempFile(kPatchFile, &patch_file_path, nullptr));
616
617  vector<string> cmd;
618  cmd.push_back(kBsdiffPath);
619  cmd.push_back(old_file);
620  cmd.push_back(new_file);
621  cmd.push_back(patch_file_path);
622
623  int rc = 1;
624  chromeos::Blob patch_file;
625  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
626  TEST_AND_RETURN_FALSE(rc == 0);
627  TEST_AND_RETURN_FALSE(utils::ReadFile(patch_file_path, out));
628  unlink(patch_file_path.c_str());
629  return true;
630}
631
632// Returns true if |op| is a no-op operation that doesn't do any useful work
633// (e.g., a move operation that copies blocks onto themselves).
634bool IsNoopOperation(const DeltaArchiveManifest_InstallOperation& op) {
635  return (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE &&
636          ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
637}
638
639void FilterNoopOperations(vector<AnnotatedOperation>* ops) {
640  ops->erase(
641      std::remove_if(
642          ops->begin(), ops->end(),
643          [](const AnnotatedOperation& aop){return IsNoopOperation(aop.op);}),
644      ops->end());
645}
646
647bool InitializePartitionInfo(const PartitionConfig& part, PartitionInfo* info) {
648  info->set_size(part.size);
649  OmahaHashCalculator hasher;
650  TEST_AND_RETURN_FALSE(hasher.UpdateFile(part.path, part.size) ==
651                        static_cast<off_t>(part.size));
652  TEST_AND_RETURN_FALSE(hasher.Finalize());
653  const chromeos::Blob& hash = hasher.raw_hash();
654  info->set_hash(hash.data(), hash.size());
655  LOG(INFO) << part.path << ": size=" << part.size << " hash=" << hasher.hash();
656  return true;
657}
658
659bool CompareAopsByDestination(AnnotatedOperation first_aop,
660                              AnnotatedOperation second_aop) {
661  // We want empty operations to be at the end of the payload.
662  if (!first_aop.op.dst_extents().size() || !second_aop.op.dst_extents().size())
663    return ((!first_aop.op.dst_extents().size()) <
664            (!second_aop.op.dst_extents().size()));
665  uint32_t first_dst_start = first_aop.op.dst_extents(0).start_block();
666  uint32_t second_dst_start = second_aop.op.dst_extents(0).start_block();
667  return first_dst_start < second_dst_start;
668}
669
670}  // namespace diff_utils
671
672}  // namespace chromeos_update_engine
673