generate_delta_main.cc revision d2779df63aaad8b65fc5d4badee7dbc9bed7f2b6
1// Copyright (c) 2010 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 <errno.h>
6#include <fcntl.h>
7#include <sys/stat.h>
8#include <sys/types.h>
9#include <unistd.h>
10
11#include <set>
12#include <string>
13#include <vector>
14
15#include <base/command_line.h>
16#include <base/logging.h>
17#include <base/strings/string_number_conversions.h>
18#include <base/strings/string_split.h>
19#include <gflags/gflags.h>
20#include <glib.h>
21
22#include "update_engine/delta_performer.h"
23#include "update_engine/payload_generator/delta_diff_generator.h"
24#include "update_engine/payload_signer.h"
25#include "update_engine/prefs.h"
26#include "update_engine/subprocess.h"
27#include "update_engine/terminator.h"
28#include "update_engine/update_metadata.pb.h"
29#include "update_engine/utils.h"
30
31DEFINE_string(old_dir, "",
32              "Directory where the old rootfs is loop mounted read-only");
33DEFINE_string(new_dir, "",
34              "Directory where the new rootfs is loop mounted read-only");
35DEFINE_string(old_image, "", "Path to the old rootfs");
36DEFINE_string(new_image, "", "Path to the new rootfs");
37DEFINE_string(old_kernel, "", "Path to the old kernel partition image");
38DEFINE_string(new_kernel, "", "Path to the new kernel partition image");
39DEFINE_string(in_file, "",
40              "Path to input delta payload file used to hash/sign payloads "
41              "and apply delta over old_image (for debugging)");
42DEFINE_string(out_file, "", "Path to output delta payload file");
43DEFINE_string(out_hash_file, "", "Path to output hash file");
44DEFINE_string(out_metadata_hash_file, "", "Path to output metadata hash file");
45DEFINE_string(private_key, "", "Path to private key in .pem format");
46DEFINE_string(public_key, "", "Path to public key in .pem format");
47DEFINE_int32(public_key_version,
48             chromeos_update_engine::kSignatureMessageCurrentVersion,
49             "Key-check version # of client");
50DEFINE_string(prefs_dir, "/tmp/update_engine_prefs",
51              "Preferences directory, used with apply_delta");
52DEFINE_string(signature_size, "",
53              "Raw signature size used for hash calculation. "
54              "You may pass in multiple sizes by colon separating them. E.g. "
55              "2048:2048:4096 will assume 3 signatures, the first two with "
56              "2048 size and the last 4096.");
57DEFINE_string(signature_file, "",
58              "Raw signature file to sign payload with. To pass multiple "
59              "signatures, use a single argument with a colon between paths, "
60              "e.g. /path/to/sig:/path/to/next:/path/to/last_sig . Each "
61              "signature will be assigned a client version, starting from "
62              "kSignatureOriginalVersion.");
63DEFINE_int32(chunk_size, -1, "Payload chunk size (-1 -- no limit/default)");
64DEFINE_int64(rootfs_partition_size,
65             chromeos_update_engine::kRootFSPartitionSize,
66             "RootFS partition size for the image once installed");
67
68DEFINE_string(old_channel, "",
69              "The channel for the old image. 'dev-channel', 'npo-channel', "
70              "etc. Ignored, except during delta generation.");
71DEFINE_string(old_board, "",
72              "The board for the old image. 'x86-mario', 'lumpy', "
73              "etc. Ignored, except during delta generation.");
74DEFINE_string(old_version, "",
75              "The build version of the old image. 1.2.3, etc.");
76DEFINE_string(old_key, "",
77              "The key used to sign the old image. 'premp', 'mp', 'mp-v3',"
78              " etc");
79DEFINE_string(old_build_channel, "",
80              "The channel for the build of the old image. 'dev-channel', "
81              "etc, but will never contain special channels such as "
82              "'npo-channel'. Ignored, except during delta generation.");
83DEFINE_string(old_build_version, "",
84              "The version of the build containing the old image.");
85
86DEFINE_string(new_channel, "",
87              "The channel for the new image. 'dev-channel', 'npo-channel', "
88              "etc. Ignored, except during delta generation.");
89DEFINE_string(new_board, "",
90              "The board for the new image. 'x86-mario', 'lumpy', "
91              "etc. Ignored, except during delta generation.");
92DEFINE_string(new_version, "",
93              "The build version of the new image. 1.2.3, etc.");
94DEFINE_string(new_key, "",
95              "The key used to sign the new image. 'premp', 'mp', 'mp-v3',"
96              " etc");
97DEFINE_string(new_build_channel, "",
98              "The channel for the build of the new image. 'dev-channel', "
99              "etc, but will never contain special channels such as "
100              "'npo-channel'. Ignored, except during delta generation.");
101DEFINE_string(new_build_version, "",
102              "The version of the build containing the new image.");
103
104// This file contains a simple program that takes an old path, a new path,
105// and an output file as arguments and the path to an output file and
106// generates a delta that can be sent to Chrome OS clients.
107
108using std::set;
109using std::string;
110using std::vector;
111
112namespace chromeos_update_engine {
113
114namespace {
115
116void ParseSignatureSizes(const string& signature_sizes_flag,
117                         vector<int>* signature_sizes) {
118  signature_sizes->clear();
119  vector<string> split_strings;
120
121  base::SplitString(signature_sizes_flag, ':', &split_strings);
122  for (vector<string>::iterator i = split_strings.begin();
123       i < split_strings.end();
124       i++) {
125    int size = 0;
126    bool parsing_successful = base::StringToInt(*i, &size);
127    LOG_IF(FATAL, !parsing_successful)
128        << "Invalid signature size: " << *i;
129
130    LOG_IF(FATAL, size != (2048 / 8)) <<
131        "Only signature sizes of 256 bytes are supported.";
132
133    signature_sizes->push_back(size);
134  }
135}
136
137
138bool ParseImageInfo(const string& channel,
139                    const string& board,
140                    const string& version,
141                    const string& key,
142                    const string& build_channel,
143                    const string& build_version,
144                    ImageInfo* image_info) {
145
146  // All of these arguments should be present or missing.
147  bool empty = channel.empty();
148
149  CHECK_EQ(channel.empty(), empty);
150  CHECK_EQ(board.empty(), empty);
151  CHECK_EQ(version.empty(), empty);
152  CHECK_EQ(key.empty(), empty);
153
154  if (empty)
155    return false;
156
157  image_info->set_channel(channel);
158  image_info->set_board(board);
159  image_info->set_version(version);
160  image_info->set_key(key);
161
162  image_info->set_build_channel(
163      build_channel.empty() ? channel : build_channel);
164
165  image_info->set_build_version(
166      build_version.empty() ? version : build_version);
167
168  return true;
169}
170
171void CalculatePayloadHashForSigning(const vector<int> &sizes,
172                                    const string& out_hash_file) {
173  LOG(INFO) << "Calculating payload hash for signing.";
174  LOG_IF(FATAL, FLAGS_in_file.empty())
175      << "Must pass --in_file to calculate hash for signing.";
176  LOG_IF(FATAL, out_hash_file.empty())
177      << "Must pass --out_hash_file to calculate hash for signing.";
178
179  vector<char> hash;
180  bool result = PayloadSigner::HashPayloadForSigning(FLAGS_in_file, sizes,
181                                                     &hash);
182  CHECK(result);
183
184  result = utils::WriteFile(out_hash_file.c_str(), hash.data(), hash.size());
185  CHECK(result);
186  LOG(INFO) << "Done calculating payload hash for signing.";
187}
188
189
190void CalculateMetadataHashForSigning(const vector<int> &sizes,
191                                     const string& out_metadata_hash_file) {
192  LOG(INFO) << "Calculating metadata hash for signing.";
193  LOG_IF(FATAL, FLAGS_in_file.empty())
194      << "Must pass --in_file to calculate metadata hash for signing.";
195  LOG_IF(FATAL, out_metadata_hash_file.empty())
196      << "Must pass --out_metadata_hash_file to calculate metadata hash.";
197
198  vector<char> hash;
199  bool result = PayloadSigner::HashMetadataForSigning(FLAGS_in_file, sizes,
200                                                      &hash);
201  CHECK(result);
202
203  result = utils::WriteFile(out_metadata_hash_file.c_str(), hash.data(),
204                            hash.size());
205  CHECK(result);
206
207  LOG(INFO) << "Done calculating metadata hash for signing.";
208}
209
210void SignPayload() {
211  LOG(INFO) << "Signing payload.";
212  LOG_IF(FATAL, FLAGS_in_file.empty())
213      << "Must pass --in_file to sign payload.";
214  LOG_IF(FATAL, FLAGS_out_file.empty())
215      << "Must pass --out_file to sign payload.";
216  LOG_IF(FATAL, FLAGS_signature_file.empty())
217      << "Must pass --signature_file to sign payload.";
218  vector<vector<char> > signatures;
219  vector<string> signature_files;
220  base::SplitString(FLAGS_signature_file, ':', &signature_files);
221  for (vector<string>::iterator it = signature_files.begin(),
222           e = signature_files.end(); it != e; ++it) {
223    vector<char> signature;
224    CHECK(utils::ReadFile(*it, &signature));
225    signatures.push_back(signature);
226  }
227  uint64_t final_metadata_size;
228  CHECK(PayloadSigner::AddSignatureToPayload(
229      FLAGS_in_file, signatures, FLAGS_out_file, &final_metadata_size));
230  LOG(INFO) << "Done signing payload. Final metadata size = "
231            << final_metadata_size;
232}
233
234void VerifySignedPayload() {
235  LOG(INFO) << "Verifying signed payload.";
236  LOG_IF(FATAL, FLAGS_in_file.empty())
237      << "Must pass --in_file to verify signed payload.";
238  LOG_IF(FATAL, FLAGS_public_key.empty())
239      << "Must pass --public_key to verify signed payload.";
240  CHECK(PayloadSigner::VerifySignedPayload(FLAGS_in_file, FLAGS_public_key,
241                                           FLAGS_public_key_version));
242  LOG(INFO) << "Done verifying signed payload.";
243}
244
245void ApplyDelta() {
246  LOG(INFO) << "Applying delta.";
247  LOG_IF(FATAL, FLAGS_old_image.empty())
248      << "Must pass --old_image to apply delta.";
249  Prefs prefs;
250  InstallPlan install_plan;
251  LOG(INFO) << "Setting up preferences under: " << FLAGS_prefs_dir;
252  LOG_IF(ERROR, !prefs.Init(base::FilePath(FLAGS_prefs_dir)))
253      << "Failed to initialize preferences.";
254  // Get original checksums
255  LOG(INFO) << "Calculating original checksums";
256  PartitionInfo kern_info, root_info;
257  CHECK(DeltaDiffGenerator::InitializePartitionInfo(true,  // is_kernel
258                                                    FLAGS_old_kernel,
259                                                    &kern_info));
260  CHECK(DeltaDiffGenerator::InitializePartitionInfo(false,  // is_kernel
261                                                    FLAGS_old_image,
262                                                    &root_info));
263  install_plan.kernel_hash.assign(kern_info.hash().begin(),
264                                  kern_info.hash().end());
265  install_plan.rootfs_hash.assign(root_info.hash().begin(),
266                                  root_info.hash().end());
267  DeltaPerformer performer(&prefs, NULL, &install_plan);
268  CHECK_EQ(performer.Open(FLAGS_old_image.c_str(), 0, 0), 0);
269  CHECK(performer.OpenKernel(FLAGS_old_kernel.c_str()));
270  vector<char> buf(1024 * 1024);
271  int fd = open(FLAGS_in_file.c_str(), O_RDONLY, 0);
272  CHECK_GE(fd, 0);
273  ScopedFdCloser fd_closer(&fd);
274  for (off_t offset = 0;; offset += buf.size()) {
275    ssize_t bytes_read;
276    CHECK(utils::PReadAll(fd, &buf[0], buf.size(), offset, &bytes_read));
277    if (bytes_read == 0)
278      break;
279    CHECK_EQ(performer.Write(&buf[0], bytes_read), bytes_read);
280  }
281  CHECK_EQ(performer.Close(), 0);
282  DeltaPerformer::ResetUpdateProgress(&prefs, false);
283  LOG(INFO) << "Done applying delta.";
284}
285
286int Main(int argc, char** argv) {
287  google::SetUsageMessage(
288      "Generates a payload to provide to ChromeOS' update_engine.\n\n"
289      "This tool can create full payloads and also delta payloads if the src\n"
290      "image is provided. It also provides debugging options to apply, sign\n"
291      "and verify payloads.");
292  google::ParseCommandLineFlags(&argc, &argv, true);
293  CommandLine::Init(argc, argv);
294  Terminator::Init();
295  Subprocess::Init();
296
297  logging::LoggingSettings log_settings;
298  log_settings.log_file     = "delta_generator.log";
299  log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
300  log_settings.lock_log     = logging::DONT_LOCK_LOG_FILE;
301  log_settings.delete_old   = logging::APPEND_TO_OLD_LOG_FILE;
302
303  logging::InitLogging(log_settings);
304
305  vector<int> signature_sizes;
306  ParseSignatureSizes(FLAGS_signature_size, &signature_sizes);
307
308  if (!FLAGS_out_hash_file.empty() || !FLAGS_out_metadata_hash_file.empty()) {
309    if (!FLAGS_out_hash_file.empty()) {
310      CalculatePayloadHashForSigning(signature_sizes, FLAGS_out_hash_file);
311    }
312    if (!FLAGS_out_metadata_hash_file.empty()) {
313      CalculateMetadataHashForSigning(signature_sizes,
314                                      FLAGS_out_metadata_hash_file);
315    }
316    return 0;
317  }
318  if (!FLAGS_signature_file.empty()) {
319    SignPayload();
320    return 0;
321  }
322  if (!FLAGS_public_key.empty()) {
323    VerifySignedPayload();
324    return 0;
325  }
326  if (!FLAGS_in_file.empty()) {
327    ApplyDelta();
328    return 0;
329  }
330  CHECK(!FLAGS_new_image.empty());
331  CHECK(!FLAGS_out_file.empty());
332  CHECK(!FLAGS_new_kernel.empty());
333
334  bool is_delta = !FLAGS_old_image.empty();
335
336  ImageInfo old_image_info;
337  ImageInfo new_image_info;
338
339  // Ignore failures. These are optional arguments.
340  ParseImageInfo(FLAGS_new_channel,
341                 FLAGS_new_board,
342                 FLAGS_new_version,
343                 FLAGS_new_key,
344                 FLAGS_new_build_channel,
345                 FLAGS_new_build_version,
346                 &new_image_info);
347
348  // Ignore failures. These are optional arguments.
349  ParseImageInfo(FLAGS_old_channel,
350                 FLAGS_old_board,
351                 FLAGS_old_version,
352                 FLAGS_old_key,
353                 FLAGS_old_build_channel,
354                 FLAGS_old_build_version,
355                 &old_image_info);
356
357  if (is_delta) {
358    LOG(INFO) << "Generating delta update";
359    CHECK(!FLAGS_old_dir.empty());
360    CHECK(!FLAGS_new_dir.empty());
361    if (!utils::IsDir(FLAGS_old_dir.c_str()) ||
362        !utils::IsDir(FLAGS_new_dir.c_str())) {
363      LOG(FATAL) << "old_dir or new_dir not directory";
364    }
365  } else {
366    LOG(INFO) << "Generating full update";
367  }
368
369  uint64_t metadata_size;
370  if (!DeltaDiffGenerator::GenerateDeltaUpdateFile(
371      FLAGS_old_dir,
372      FLAGS_old_image,
373      FLAGS_new_dir,
374      FLAGS_new_image,
375      FLAGS_old_kernel,
376      FLAGS_new_kernel,
377      FLAGS_out_file,
378      FLAGS_private_key,
379      FLAGS_chunk_size,
380      FLAGS_rootfs_partition_size,
381      is_delta ? &old_image_info : NULL,
382      &new_image_info,
383      &metadata_size)) {
384    return 1;
385  }
386  return 0;
387}
388
389}  // namespace
390
391}  // namespace chromeos_update_engine
392
393int main(int argc, char** argv) {
394  return chromeos_update_engine::Main(argc, argv);
395}
396