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