• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/delta_diff_generator.h"
29 #include "update_engine/payload_generator/delta_diff_utils.h"
30 
31 using chromeos_update_engine::diff_utils::IsAReplaceOperation;
32 using std::string;
33 using std::vector;
34 
35 namespace chromeos_update_engine {
36 
GenerateOperations(const PayloadGenerationConfig & config,const PartitionConfig & old_part,const PartitionConfig & new_part,BlobFileWriter * blob_file,vector<AnnotatedOperation> * aops)37 bool ABGenerator::GenerateOperations(const PayloadGenerationConfig& config,
38                                      const PartitionConfig& old_part,
39                                      const PartitionConfig& new_part,
40                                      BlobFileWriter* blob_file,
41                                      vector<AnnotatedOperation>* aops) {
42   TEST_AND_RETURN_FALSE(old_part.name == new_part.name);
43 
44   ssize_t hard_chunk_blocks =
45       (config.hard_chunk_size == -1
46            ? -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,
57                                                        blob_file));
58   LOG(INFO) << "done reading " << new_part.name;
59 
60   SortOperationsByDestination(aops);
61 
62   // Use the soft_chunk_size when merging operations to prevent merging all
63   // the operations into a huge one if there's no hard limit.
64   size_t merge_chunk_blocks = soft_chunk_blocks;
65   if (hard_chunk_blocks != -1 &&
66       static_cast<size_t>(hard_chunk_blocks) < soft_chunk_blocks) {
67     merge_chunk_blocks = hard_chunk_blocks;
68   }
69 
70   LOG(INFO) << "Merging " << aops->size() << " operations.";
71   TEST_AND_RETURN_FALSE(MergeOperations(
72       aops, config.version, merge_chunk_blocks, new_part.path, blob_file));
73   LOG(INFO) << aops->size() << " operations after merge.";
74 
75   if (config.version.minor >= kOpSrcHashMinorPayloadVersion)
76     TEST_AND_RETURN_FALSE(AddSourceHash(aops, old_part.path));
77 
78   return true;
79 }
80 
SortOperationsByDestination(vector<AnnotatedOperation> * aops)81 void ABGenerator::SortOperationsByDestination(
82     vector<AnnotatedOperation>* aops) {
83   sort(aops->begin(), aops->end(), diff_utils::CompareAopsByDestination);
84 }
85 
FragmentOperations(const PayloadVersion & version,vector<AnnotatedOperation> * aops,const string & target_part_path,BlobFileWriter * blob_file)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 
SplitSourceCopy(const AnnotatedOperation & original_aop,vector<AnnotatedOperation> * result_aops)110 bool ABGenerator::SplitSourceCopy(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     const 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 
148     AnnotatedOperation new_aop;
149     new_aop.op = new_op;
150     new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i);
151     result_aops->push_back(new_aop);
152   }
153   if (curr_src_ext_index != original_op.src_extents().size() - 1) {
154     LOG(FATAL) << "Incorrectly split SOURCE_COPY operation. Did not use all "
155                << "source extents.";
156   }
157   return true;
158 }
159 
SplitAReplaceOp(const PayloadVersion & version,const AnnotatedOperation & original_aop,const string & target_part_path,vector<AnnotatedOperation> * result_aops,BlobFileWriter * blob_file)160 bool ABGenerator::SplitAReplaceOp(const PayloadVersion& version,
161                                   const AnnotatedOperation& original_aop,
162                                   const string& target_part_path,
163                                   vector<AnnotatedOperation>* result_aops,
164                                   BlobFileWriter* blob_file) {
165   InstallOperation original_op = original_aop.op;
166   TEST_AND_RETURN_FALSE(IsAReplaceOperation(original_op.type()));
167   const bool is_replace = original_op.type() == InstallOperation::REPLACE;
168 
169   uint64_t data_offset = original_op.data_offset();
170   for (int i = 0; i < original_op.dst_extents_size(); i++) {
171     const Extent& dst_ext = original_op.dst_extents(i);
172     // Make a new operation with only one dst extent.
173     InstallOperation new_op;
174     *(new_op.add_dst_extents()) = dst_ext;
175     uint64_t data_size = dst_ext.num_blocks() * kBlockSize;
176     // If this is a REPLACE, attempt to reuse portions of the existing blob.
177     if (is_replace) {
178       new_op.set_type(InstallOperation::REPLACE);
179       new_op.set_data_length(data_size);
180       new_op.set_data_offset(data_offset);
181       data_offset += data_size;
182     }
183 
184     AnnotatedOperation new_aop;
185     new_aop.op = new_op;
186     new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i);
187     TEST_AND_RETURN_FALSE(
188         AddDataAndSetType(&new_aop, version, target_part_path, blob_file));
189 
190     result_aops->push_back(new_aop);
191   }
192   return true;
193 }
194 
MergeOperations(vector<AnnotatedOperation> * aops,const PayloadVersion & version,size_t chunk_blocks,const string & target_part_path,BlobFileWriter * blob_file)195 bool ABGenerator::MergeOperations(vector<AnnotatedOperation>* aops,
196                                   const PayloadVersion& version,
197                                   size_t chunk_blocks,
198                                   const string& target_part_path,
199                                   BlobFileWriter* blob_file) {
200   vector<AnnotatedOperation> new_aops;
201   for (const AnnotatedOperation& curr_aop : *aops) {
202     if (new_aops.empty()) {
203       new_aops.push_back(curr_aop);
204       continue;
205     }
206     AnnotatedOperation& last_aop = new_aops.back();
207     bool last_is_a_replace = IsAReplaceOperation(last_aop.op.type());
208 
209     if (last_aop.op.dst_extents_size() <= 0 ||
210         curr_aop.op.dst_extents_size() <= 0) {
211       new_aops.push_back(curr_aop);
212       continue;
213     }
214     uint32_t last_dst_idx = last_aop.op.dst_extents_size() - 1;
215     uint32_t last_end_block =
216         last_aop.op.dst_extents(last_dst_idx).start_block() +
217         last_aop.op.dst_extents(last_dst_idx).num_blocks();
218     uint32_t curr_start_block = curr_aop.op.dst_extents(0).start_block();
219     uint32_t combined_block_count =
220         last_aop.op.dst_extents(last_dst_idx).num_blocks() +
221         curr_aop.op.dst_extents(0).num_blocks();
222     bool is_a_replace = IsAReplaceOperation(curr_aop.op.type());
223 
224     bool is_delta_op = curr_aop.op.type() == InstallOperation::SOURCE_COPY;
225     if (((is_delta_op && (last_aop.op.type() == curr_aop.op.type())) ||
226          (is_a_replace && last_is_a_replace)) &&
227         last_end_block == curr_start_block &&
228         combined_block_count <= chunk_blocks) {
229       // If the operations have the same type (which is a type that we can
230       // merge), are contiguous, are fragmented to have one destination extent,
231       // and their combined block count would be less than chunk size, merge
232       // them.
233       last_aop.name = base::StringPrintf(
234           "%s,%s", last_aop.name.c_str(), curr_aop.name.c_str());
235 
236       if (is_delta_op) {
237         ExtendExtents(last_aop.op.mutable_src_extents(),
238                       curr_aop.op.src_extents());
239       }
240       ExtendExtents(last_aop.op.mutable_dst_extents(),
241                     curr_aop.op.dst_extents());
242       // Set the data length to zero so we know to add the blob later.
243       if (is_a_replace)
244         last_aop.op.set_data_length(0);
245     } else {
246       // Otherwise just include the extent as is.
247       new_aops.push_back(curr_aop);
248     }
249   }
250 
251   // Set the blobs for REPLACE/REPLACE_BZ/REPLACE_XZ operations that have been
252   // merged.
253   for (AnnotatedOperation& curr_aop : new_aops) {
254     if (curr_aop.op.data_length() == 0 &&
255         IsAReplaceOperation(curr_aop.op.type())) {
256       TEST_AND_RETURN_FALSE(
257           AddDataAndSetType(&curr_aop, version, target_part_path, blob_file));
258     }
259   }
260 
261   *aops = new_aops;
262   return true;
263 }
264 
AddDataAndSetType(AnnotatedOperation * aop,const PayloadVersion & version,const string & target_part_path,BlobFileWriter * blob_file)265 bool ABGenerator::AddDataAndSetType(AnnotatedOperation* aop,
266                                     const PayloadVersion& version,
267                                     const string& target_part_path,
268                                     BlobFileWriter* blob_file) {
269   TEST_AND_RETURN_FALSE(IsAReplaceOperation(aop->op.type()));
270 
271   vector<Extent> dst_extents;
272   ExtentsToVector(aop->op.dst_extents(), &dst_extents);
273   brillo::Blob data(utils::BlocksInExtents(dst_extents) * kBlockSize);
274   TEST_AND_RETURN_FALSE(utils::ReadExtents(
275       target_part_path, dst_extents, &data, data.size(), kBlockSize));
276 
277   brillo::Blob blob;
278   InstallOperation::Type op_type;
279   TEST_AND_RETURN_FALSE(
280       diff_utils::GenerateBestFullOperation(data, version, &blob, &op_type));
281 
282   // If the operation doesn't point to a data blob or points to a data blob of
283   // a different type then we add it.
284   if (aop->op.type() != op_type || aop->op.data_length() != blob.size()) {
285     aop->op.set_type(op_type);
286     aop->SetOperationBlob(blob, blob_file);
287   }
288 
289   return true;
290 }
291 
AddSourceHash(vector<AnnotatedOperation> * aops,const string & source_part_path)292 bool ABGenerator::AddSourceHash(vector<AnnotatedOperation>* aops,
293                                 const string& source_part_path) {
294   for (AnnotatedOperation& aop : *aops) {
295     if (aop.op.src_extents_size() == 0)
296       continue;
297 
298     vector<Extent> src_extents;
299     ExtentsToVector(aop.op.src_extents(), &src_extents);
300     brillo::Blob src_data, src_hash;
301     uint64_t src_length =
302         aop.op.has_src_length()
303             ? aop.op.src_length()
304             : utils::BlocksInExtents(aop.op.src_extents()) * kBlockSize;
305     TEST_AND_RETURN_FALSE(utils::ReadExtents(
306         source_part_path, src_extents, &src_data, src_length, kBlockSize));
307     TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfData(src_data, &src_hash));
308     aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
309   }
310   return true;
311 }
312 
313 }  // namespace chromeos_update_engine
314