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