Home | History | Annotate | Download | only in payload_generator
      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