delta_diff_generator.cc revision ebdf17d4202c67933764135bfc1cece629829201
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 payload.AddPartitionOperations(PartitionName::kRootfs, rootfs_ops); 153 payload.AddPartitionOperations(PartitionName::kKernel, kernel_ops); 154 payload.WritePayload(output_path, temp_file_path, private_key_path, 155 metadata_size); 156 temp_file_unlinker.reset(); 157 158 LOG(INFO) << "All done. Successfully created delta file with " 159 << "metadata size = " << *metadata_size; 160 return true; 161} 162 163}; // namespace chromeos_update_engine 164