delta_diff_generator.cc revision b42b98db059a12c44110588fc0b3d5f82d32a2f8
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 <memory>
15#include <string>
16#include <utility>
17#include <vector>
18
19#include <base/logging.h>
20
21#include "update_engine/delta_performer.h"
22#include "update_engine/payload_constants.h"
23#include "update_engine/payload_generator/ab_generator.h"
24#include "update_engine/payload_generator/delta_diff_utils.h"
25#include "update_engine/payload_generator/full_update_generator.h"
26#include "update_engine/payload_generator/inplace_generator.h"
27#include "update_engine/payload_generator/payload_file.h"
28#include "update_engine/utils.h"
29
30using std::string;
31using std::unique_ptr;
32using std::vector;
33
34namespace chromeos_update_engine {
35
36// bytes
37const size_t kRootFSPartitionSize = static_cast<size_t>(2) * 1024 * 1024 * 1024;
38const size_t kBlockSize = 4096;  // bytes
39
40bool GenerateUpdatePayloadFile(
41    const PayloadGenerationConfig& config,
42    const string& output_path,
43    const string& private_key_path,
44    uint64_t* metadata_size) {
45  if (config.is_delta) {
46    LOG_IF(WARNING, config.source.rootfs.size != config.target.rootfs.size)
47        << "Old and new images have different block counts.";
48    // TODO(deymo): Our tools only support growing the filesystem size during
49    // an update. Remove this check when that's fixed. crbug.com/192136
50    LOG_IF(FATAL, config.source.rootfs.size > config.target.rootfs.size)
51        << "Shirking the rootfs size is not supported at the moment.";
52  }
53
54  // Sanity checks for the partition size.
55  LOG(INFO) << "Rootfs partition size: " << config.rootfs_partition_size;
56  LOG(INFO) << "Actual filesystem size: " << config.target.rootfs.size;
57
58  LOG(INFO) << "Block count: "
59            << config.target.rootfs.size / config.block_size;
60
61  LOG_IF(INFO, config.source.kernel.path.empty())
62      << "Will generate full kernel update.";
63
64  const string kTempFileTemplate("CrAU_temp_data.XXXXXX");
65  string temp_file_path;
66  off_t data_file_size = 0;
67
68  LOG(INFO) << "Reading files...";
69
70  // Create empty payload file object.
71  PayloadFile payload;
72  TEST_AND_RETURN_FALSE(payload.Init(config));
73
74  vector<AnnotatedOperation> rootfs_ops;
75  vector<AnnotatedOperation> kernel_ops;
76
77  // Select payload generation strategy based on the config.
78  unique_ptr<OperationsGenerator> strategy;
79  if (config.is_delta) {
80    // We don't efficiently support deltas on squashfs. For now, we will
81    // produce full operations in that case.
82    if (utils::IsSquashfsFilesystem(config.target.rootfs.path)) {
83      LOG(INFO) << "Using generator FullUpdateGenerator() for squashfs deltas";
84      strategy.reset(new FullUpdateGenerator());
85    } else if (utils::IsExtFilesystem(config.target.rootfs.path)) {
86      // Delta update (with possibly a full kernel update).
87      if (config.minor_version == kInPlaceMinorPayloadVersion) {
88        LOG(INFO) << "Using generator InplaceGenerator()";
89        strategy.reset(new InplaceGenerator());
90      } else if (config.minor_version == kSourceMinorPayloadVersion) {
91        LOG(INFO) << "Using generator ABGenerator()";
92        strategy.reset(new ABGenerator());
93      } else {
94        LOG(ERROR) << "Unsupported minor version given for delta payload: "
95                   << config.minor_version;
96        return false;
97      }
98    } else {
99      LOG(ERROR) << "Unsupported filesystem for delta payload in "
100                 << config.target.rootfs.path;
101      return false;
102    }
103  } else {
104    // Full update.
105    LOG(INFO) << "Using generator FullUpdateGenerator()";
106    strategy.reset(new FullUpdateGenerator());
107  }
108
109  int data_file_fd;
110  TEST_AND_RETURN_FALSE(
111      utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &data_file_fd));
112  unique_ptr<ScopedPathUnlinker> temp_file_unlinker(
113      new ScopedPathUnlinker(temp_file_path));
114  TEST_AND_RETURN_FALSE(data_file_fd >= 0);
115
116  {
117    ScopedFdCloser data_file_fd_closer(&data_file_fd);
118    // Generate the operations using the strategy we selected above.
119    TEST_AND_RETURN_FALSE(strategy->GenerateOperations(config,
120                                                       data_file_fd,
121                                                       &data_file_size,
122                                                       &rootfs_ops,
123                                                       &kernel_ops));
124  }
125
126  // Filter the no-operations. OperationsGenerators should not output this kind
127  // of operations normally, but this is an extra step to fix that if
128  // happened.
129  diff_utils::FilterNoopOperations(&rootfs_ops);
130  diff_utils::FilterNoopOperations(&kernel_ops);
131
132  // Write payload file to disk.
133  payload.AddPartitionOperations(PartitionName::kRootfs, rootfs_ops);
134  payload.AddPartitionOperations(PartitionName::kKernel, kernel_ops);
135  payload.WritePayload(output_path, temp_file_path, private_key_path,
136                       metadata_size);
137  temp_file_unlinker.reset();
138
139  LOG(INFO) << "All done. Successfully created delta file with "
140            << "metadata size = " << *metadata_size;
141  return true;
142}
143
144};  // namespace chromeos_update_engine
145