delta_diff_generator.cc revision b9ef491e87c17d809368ede0196569d8a106b963
1//
2// Copyright (C) 2012 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#include "update_engine/payload_generator/delta_diff_generator.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <inttypes.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24
25#include <algorithm>
26#include <memory>
27#include <string>
28#include <utility>
29#include <vector>
30
31#include <base/logging.h>
32
33#include "update_engine/delta_performer.h"
34#include "update_engine/payload_constants.h"
35#include "update_engine/payload_generator/ab_generator.h"
36#include "update_engine/payload_generator/blob_file_writer.h"
37#include "update_engine/payload_generator/delta_diff_utils.h"
38#include "update_engine/payload_generator/full_update_generator.h"
39#include "update_engine/payload_generator/inplace_generator.h"
40#include "update_engine/payload_generator/payload_file.h"
41#include "update_engine/utils.h"
42
43using std::string;
44using std::unique_ptr;
45using std::vector;
46
47namespace chromeos_update_engine {
48
49// bytes
50const size_t kRootFSPartitionSize = static_cast<size_t>(2) * 1024 * 1024 * 1024;
51const size_t kBlockSize = 4096;  // bytes
52
53bool GenerateUpdatePayloadFile(
54    const PayloadGenerationConfig& config,
55    const string& output_path,
56    const string& private_key_path,
57    uint64_t* metadata_size) {
58  if (config.is_delta) {
59    LOG_IF(WARNING, config.source.rootfs.size != config.target.rootfs.size)
60        << "Old and new images have different block counts.";
61    // TODO(deymo): Our tools only support growing the filesystem size during
62    // an update. Remove this check when that's fixed. crbug.com/192136
63    LOG_IF(FATAL, config.source.rootfs.size > config.target.rootfs.size)
64        << "Shirking the rootfs size is not supported at the moment.";
65  }
66
67  // Sanity checks for the partition size.
68  LOG(INFO) << "Rootfs partition size: " << config.rootfs_partition_size;
69  LOG(INFO) << "Actual filesystem size: " << config.target.rootfs.size;
70
71  LOG(INFO) << "Block count: "
72            << config.target.rootfs.size / config.block_size;
73
74  LOG_IF(INFO, config.source.kernel.path.empty())
75      << "Will generate full kernel update.";
76
77  const string kTempFileTemplate("CrAU_temp_data.XXXXXX");
78  string temp_file_path;
79  off_t data_file_size = 0;
80
81  LOG(INFO) << "Reading files...";
82
83  // Create empty payload file object.
84  PayloadFile payload;
85  TEST_AND_RETURN_FALSE(payload.Init(config));
86
87  vector<AnnotatedOperation> rootfs_ops;
88  vector<AnnotatedOperation> kernel_ops;
89
90  // Select payload generation strategy based on the config.
91  unique_ptr<OperationsGenerator> strategy;
92  if (config.is_delta) {
93    // We don't efficiently support deltas on squashfs. For now, we will
94    // produce full operations in that case.
95    if (utils::IsSquashfsFilesystem(config.target.rootfs.path)) {
96      LOG(INFO) << "Using generator FullUpdateGenerator() for squashfs deltas";
97      strategy.reset(new FullUpdateGenerator());
98    } else if (utils::IsExtFilesystem(config.target.rootfs.path)) {
99      // Delta update (with possibly a full kernel update).
100      if (config.minor_version == kInPlaceMinorPayloadVersion) {
101        LOG(INFO) << "Using generator InplaceGenerator()";
102        strategy.reset(new InplaceGenerator());
103      } else if (config.minor_version == kSourceMinorPayloadVersion) {
104        LOG(INFO) << "Using generator ABGenerator()";
105        strategy.reset(new ABGenerator());
106      } else {
107        LOG(ERROR) << "Unsupported minor version given for delta payload: "
108                   << config.minor_version;
109        return false;
110      }
111    } else {
112      LOG(ERROR) << "Unsupported filesystem for delta payload in "
113                 << config.target.rootfs.path;
114      return false;
115    }
116  } else {
117    // Full update.
118    LOG(INFO) << "Using generator FullUpdateGenerator()";
119    strategy.reset(new FullUpdateGenerator());
120  }
121
122  int data_file_fd;
123  TEST_AND_RETURN_FALSE(
124      utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &data_file_fd));
125  unique_ptr<ScopedPathUnlinker> temp_file_unlinker(
126      new ScopedPathUnlinker(temp_file_path));
127  TEST_AND_RETURN_FALSE(data_file_fd >= 0);
128
129  {
130    ScopedFdCloser data_file_fd_closer(&data_file_fd);
131    BlobFileWriter blob_file(data_file_fd, &data_file_size);
132    // Generate the operations using the strategy we selected above.
133    TEST_AND_RETURN_FALSE(strategy->GenerateOperations(config,
134                                                       config.source.rootfs,
135                                                       config.target.rootfs,
136                                                       &blob_file,
137                                                       &rootfs_ops));
138    TEST_AND_RETURN_FALSE(strategy->GenerateOperations(config,
139                                                       config.source.kernel,
140                                                       config.target.kernel,
141                                                       &blob_file,
142                                                       &kernel_ops));
143  }
144
145  // Filter the no-operations. OperationsGenerators should not output this kind
146  // of operations normally, but this is an extra step to fix that if
147  // happened.
148  diff_utils::FilterNoopOperations(&rootfs_ops);
149  diff_utils::FilterNoopOperations(&kernel_ops);
150
151  // Write payload file to disk.
152  TEST_AND_RETURN_FALSE(payload.AddPartition(config.source.rootfs,
153                                             config.target.rootfs,
154                                             rootfs_ops));
155  TEST_AND_RETURN_FALSE(payload.AddPartition(config.source.kernel,
156                                             config.target.kernel,
157                                             kernel_ops));
158  TEST_AND_RETURN_FALSE(payload.WritePayload(output_path, temp_file_path,
159                                             private_key_path, metadata_size));
160  temp_file_unlinker.reset();
161
162  LOG(INFO) << "All done. Successfully created delta file with "
163            << "metadata size = " << *metadata_size;
164  return true;
165}
166
167};  // namespace chromeos_update_engine
168