ab_generator.cc revision a12ee11c78ac6d7c2605921a4006b6a7416e0c35
1// Copyright 2015 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/ab_generator.h" 6 7#include <algorithm> 8 9#include <base/strings/stringprintf.h> 10 11#include "update_engine/bzip.h" 12#include "update_engine/delta_performer.h" 13#include "update_engine/payload_generator/annotated_operation.h" 14#include "update_engine/payload_generator/delta_diff_generator.h" 15#include "update_engine/payload_generator/delta_diff_utils.h" 16#include "update_engine/utils.h" 17 18using std::string; 19using std::vector; 20 21namespace chromeos_update_engine { 22 23bool ABGenerator::GenerateOperations( 24 const PayloadGenerationConfig& config, 25 BlobFileWriter* blob_file, 26 vector<AnnotatedOperation>* rootfs_ops, 27 vector<AnnotatedOperation>* kernel_ops) { 28 29 ssize_t hard_chunk_blocks = (config.hard_chunk_size == -1 ? -1 : 30 config.hard_chunk_size / config.block_size); 31 size_t soft_chunk_blocks = config.soft_chunk_size / config.block_size; 32 33 rootfs_ops->clear(); 34 TEST_AND_RETURN_FALSE(diff_utils::DeltaReadPartition( 35 rootfs_ops, 36 config.source.rootfs, 37 config.target.rootfs, 38 hard_chunk_blocks, 39 soft_chunk_blocks, 40 blob_file, 41 true)); // src_ops_allowed 42 LOG(INFO) << "done reading normal files"; 43 44 // Read kernel partition 45 TEST_AND_RETURN_FALSE(diff_utils::DeltaReadPartition( 46 kernel_ops, 47 config.source.kernel, 48 config.target.kernel, 49 hard_chunk_blocks, 50 soft_chunk_blocks, 51 blob_file, 52 true)); // src_ops_allowed 53 LOG(INFO) << "done reading kernel"; 54 55 TEST_AND_RETURN_FALSE(FragmentOperations(rootfs_ops, 56 config.target.rootfs.path, 57 blob_file)); 58 TEST_AND_RETURN_FALSE(FragmentOperations(kernel_ops, 59 config.target.kernel.path, 60 blob_file)); 61 SortOperationsByDestination(rootfs_ops); 62 SortOperationsByDestination(kernel_ops); 63 64 // Use the soft_chunk_size when merging operations to prevent merging all 65 // the operations into a huge one if there's no hard limit. 66 size_t merge_chunk_blocks = soft_chunk_blocks; 67 if (hard_chunk_blocks != -1 && 68 static_cast<size_t>(hard_chunk_blocks) < soft_chunk_blocks) { 69 merge_chunk_blocks = hard_chunk_blocks; 70 } 71 72 TEST_AND_RETURN_FALSE(MergeOperations(rootfs_ops, 73 merge_chunk_blocks, 74 config.target.rootfs.path, 75 blob_file)); 76 TEST_AND_RETURN_FALSE(MergeOperations(kernel_ops, 77 merge_chunk_blocks, 78 config.target.kernel.path, 79 blob_file)); 80 return true; 81} 82 83void ABGenerator::SortOperationsByDestination( 84 vector<AnnotatedOperation>* aops) { 85 sort(aops->begin(), aops->end(), diff_utils::CompareAopsByDestination); 86} 87 88bool ABGenerator::FragmentOperations( 89 vector<AnnotatedOperation>* aops, 90 const string& target_part_path, 91 BlobFileWriter* blob_file) { 92 vector<AnnotatedOperation> fragmented_aops; 93 for (const AnnotatedOperation& aop : *aops) { 94 if (aop.op.type() == InstallOperation::SOURCE_COPY) { 95 TEST_AND_RETURN_FALSE(SplitSourceCopy(aop, &fragmented_aops)); 96 } else if ((aop.op.type() == InstallOperation::REPLACE) || 97 (aop.op.type() == InstallOperation::REPLACE_BZ)) { 98 TEST_AND_RETURN_FALSE(SplitReplaceOrReplaceBz(aop, &fragmented_aops, 99 target_part_path, 100 blob_file)); 101 } else { 102 fragmented_aops.push_back(aop); 103 } 104 } 105 *aops = fragmented_aops; 106 return true; 107} 108 109bool ABGenerator::SplitSourceCopy( 110 const AnnotatedOperation& original_aop, 111 vector<AnnotatedOperation>* result_aops) { 112 InstallOperation original_op = original_aop.op; 113 TEST_AND_RETURN_FALSE(original_op.type() == InstallOperation::SOURCE_COPY); 114 // Keeps track of the index of curr_src_ext. 115 int curr_src_ext_index = 0; 116 Extent curr_src_ext = original_op.src_extents(curr_src_ext_index); 117 for (int i = 0; i < original_op.dst_extents_size(); i++) { 118 Extent dst_ext = original_op.dst_extents(i); 119 // The new operation which will have only one dst extent. 120 InstallOperation new_op; 121 uint64_t blocks_left = dst_ext.num_blocks(); 122 while (blocks_left > 0) { 123 if (curr_src_ext.num_blocks() <= blocks_left) { 124 // If the curr_src_ext is smaller than dst_ext, add it. 125 blocks_left -= curr_src_ext.num_blocks(); 126 *(new_op.add_src_extents()) = curr_src_ext; 127 if (curr_src_ext_index + 1 < original_op.src_extents().size()) { 128 curr_src_ext = original_op.src_extents(++curr_src_ext_index); 129 } else { 130 break; 131 } 132 } else { 133 // Split src_exts that are bigger than the dst_ext we're dealing with. 134 Extent first_ext; 135 first_ext.set_num_blocks(blocks_left); 136 first_ext.set_start_block(curr_src_ext.start_block()); 137 *(new_op.add_src_extents()) = first_ext; 138 // Keep the second half of the split op. 139 curr_src_ext.set_num_blocks(curr_src_ext.num_blocks() - blocks_left); 140 curr_src_ext.set_start_block(curr_src_ext.start_block() + blocks_left); 141 blocks_left -= first_ext.num_blocks(); 142 } 143 } 144 // Fix up our new operation and add it to the results. 145 new_op.set_type(InstallOperation::SOURCE_COPY); 146 *(new_op.add_dst_extents()) = dst_ext; 147 new_op.set_src_length(dst_ext.num_blocks() * kBlockSize); 148 new_op.set_dst_length(dst_ext.num_blocks() * kBlockSize); 149 150 AnnotatedOperation new_aop; 151 new_aop.op = new_op; 152 new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i); 153 result_aops->push_back(new_aop); 154 } 155 if (curr_src_ext_index != original_op.src_extents().size() - 1) { 156 LOG(FATAL) << "Incorrectly split SOURCE_COPY operation. Did not use all " 157 << "source extents."; 158 } 159 return true; 160} 161 162bool ABGenerator::SplitReplaceOrReplaceBz( 163 const AnnotatedOperation& original_aop, 164 vector<AnnotatedOperation>* result_aops, 165 const string& target_part_path, 166 BlobFileWriter* blob_file) { 167 InstallOperation original_op = original_aop.op; 168 const bool is_replace = original_op.type() == InstallOperation::REPLACE; 169 TEST_AND_RETURN_FALSE(is_replace || 170 original_op.type() == InstallOperation::REPLACE_BZ); 171 172 uint32_t data_offset = original_op.data_offset(); 173 for (int i = 0; i < original_op.dst_extents_size(); i++) { 174 Extent dst_ext = original_op.dst_extents(i); 175 // Make a new operation with only one dst extent. 176 InstallOperation new_op; 177 *(new_op.add_dst_extents()) = dst_ext; 178 uint32_t data_size = dst_ext.num_blocks() * kBlockSize; 179 new_op.set_dst_length(data_size); 180 // If this is a REPLACE, attempt to reuse portions of the existing blob. 181 if (is_replace) { 182 new_op.set_type(InstallOperation::REPLACE); 183 new_op.set_data_length(data_size); 184 new_op.set_data_offset(data_offset); 185 data_offset += data_size; 186 } 187 188 AnnotatedOperation new_aop; 189 new_aop.op = new_op; 190 new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i); 191 TEST_AND_RETURN_FALSE(AddDataAndSetType(&new_aop, target_part_path, 192 blob_file)); 193 194 result_aops->push_back(new_aop); 195 } 196 return true; 197} 198 199bool ABGenerator::MergeOperations(vector<AnnotatedOperation>* aops, 200 size_t chunk_blocks, 201 const string& target_part_path, 202 BlobFileWriter* blob_file) { 203 vector<AnnotatedOperation> new_aops; 204 for (const AnnotatedOperation& curr_aop : *aops) { 205 if (new_aops.empty()) { 206 new_aops.push_back(curr_aop); 207 continue; 208 } 209 AnnotatedOperation& last_aop = new_aops.back(); 210 211 if (last_aop.op.dst_extents_size() <= 0 || 212 curr_aop.op.dst_extents_size() <= 0) { 213 new_aops.push_back(curr_aop); 214 continue; 215 } 216 uint32_t last_dst_idx = last_aop.op.dst_extents_size() - 1; 217 uint32_t last_end_block = 218 last_aop.op.dst_extents(last_dst_idx).start_block() + 219 last_aop.op.dst_extents(last_dst_idx).num_blocks(); 220 uint32_t curr_start_block = curr_aop.op.dst_extents(0).start_block(); 221 uint32_t combined_block_count = 222 last_aop.op.dst_extents(last_dst_idx).num_blocks() + 223 curr_aop.op.dst_extents(0).num_blocks(); 224 bool good_op_type = curr_aop.op.type() == InstallOperation::SOURCE_COPY || 225 curr_aop.op.type() == InstallOperation::REPLACE || 226 curr_aop.op.type() == InstallOperation::REPLACE_BZ; 227 if (good_op_type && 228 last_aop.op.type() == curr_aop.op.type() && 229 last_end_block == curr_start_block && 230 combined_block_count <= chunk_blocks) { 231 // If the operations have the same type (which is a type that we can 232 // merge), are contiguous, are fragmented to have one destination extent, 233 // and their combined block count would be less than chunk size, merge 234 // them. 235 last_aop.name = base::StringPrintf("%s,%s", 236 last_aop.name.c_str(), 237 curr_aop.name.c_str()); 238 239 ExtendExtents(last_aop.op.mutable_src_extents(), 240 curr_aop.op.src_extents()); 241 if (curr_aop.op.src_length() > 0) 242 last_aop.op.set_src_length(last_aop.op.src_length() + 243 curr_aop.op.src_length()); 244 ExtendExtents(last_aop.op.mutable_dst_extents(), 245 curr_aop.op.dst_extents()); 246 if (curr_aop.op.dst_length() > 0) 247 last_aop.op.set_dst_length(last_aop.op.dst_length() + 248 curr_aop.op.dst_length()); 249 // Set the data length to zero so we know to add the blob later. 250 if (curr_aop.op.type() == InstallOperation::REPLACE || 251 curr_aop.op.type() == InstallOperation::REPLACE_BZ) { 252 last_aop.op.set_data_length(0); 253 } 254 } else { 255 // Otherwise just include the extent as is. 256 new_aops.push_back(curr_aop); 257 } 258 } 259 260 // Set the blobs for REPLACE/REPLACE_BZ operations that have been merged. 261 for (AnnotatedOperation& curr_aop : new_aops) { 262 if (curr_aop.op.data_length() == 0 && 263 (curr_aop.op.type() == InstallOperation::REPLACE || 264 curr_aop.op.type() == InstallOperation::REPLACE_BZ)) { 265 TEST_AND_RETURN_FALSE(AddDataAndSetType(&curr_aop, target_part_path, 266 blob_file)); 267 } 268 } 269 270 *aops = new_aops; 271 return true; 272} 273 274bool ABGenerator::AddDataAndSetType(AnnotatedOperation* aop, 275 const string& target_part_path, 276 BlobFileWriter* blob_file) { 277 TEST_AND_RETURN_FALSE(aop->op.type() == InstallOperation::REPLACE || 278 aop->op.type() == InstallOperation::REPLACE_BZ); 279 280 chromeos::Blob data(aop->op.dst_length()); 281 vector<Extent> dst_extents; 282 ExtentsToVector(aop->op.dst_extents(), &dst_extents); 283 TEST_AND_RETURN_FALSE(utils::ReadExtents(target_part_path, 284 dst_extents, 285 &data, 286 data.size(), 287 kBlockSize)); 288 289 chromeos::Blob data_bz; 290 TEST_AND_RETURN_FALSE(BzipCompress(data, &data_bz)); 291 CHECK(!data_bz.empty()); 292 293 chromeos::Blob* data_p = nullptr; 294 InstallOperation_Type new_op_type; 295 if (data_bz.size() < data.size()) { 296 new_op_type = InstallOperation::REPLACE_BZ; 297 data_p = &data_bz; 298 } else { 299 new_op_type = InstallOperation::REPLACE; 300 data_p = &data; 301 } 302 303 // If the operation doesn't point to a data blob, then we add it. 304 if (aop->op.type() != new_op_type || 305 aop->op.data_length() != data_p->size()) { 306 aop->op.set_type(new_op_type); 307 aop->SetOperationBlob(data_p, blob_file); 308 } 309 310 return true; 311} 312 313} // namespace chromeos_update_engine 314