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/delta_diff_utils.h"
18
19 #include <algorithm>
20 #include <random>
21 #include <string>
22 #include <vector>
23
24 #include <base/files/scoped_file.h>
25 #include <base/format_macros.h>
26 #include <base/strings/stringprintf.h>
27 #include <gtest/gtest.h>
28
29 #include "update_engine/common/test_utils.h"
30 #include "update_engine/common/utils.h"
31 #include "update_engine/payload_generator/delta_diff_generator.h"
32 #include "update_engine/payload_generator/extent_ranges.h"
33 #include "update_engine/payload_generator/extent_utils.h"
34 #include "update_engine/payload_generator/fake_filesystem.h"
35
36 using std::string;
37 using std::vector;
38
39 namespace chromeos_update_engine {
40
41 namespace {
42
43 // Writes the |data| in the blocks specified by |extents| on the partition
44 // |part_path|. The |data| size could be smaller than the size of the blocks
45 // passed.
WriteExtents(const string & part_path,const vector<Extent> & extents,off_t block_size,const brillo::Blob & data)46 bool WriteExtents(const string& part_path,
47 const vector<Extent>& extents,
48 off_t block_size,
49 const brillo::Blob& data) {
50 uint64_t offset = 0;
51 base::ScopedFILE fp(fopen(part_path.c_str(), "r+"));
52 TEST_AND_RETURN_FALSE(fp.get());
53
54 for (const Extent& extent : extents) {
55 if (offset >= data.size())
56 break;
57 TEST_AND_RETURN_FALSE(
58 fseek(fp.get(), extent.start_block() * block_size, SEEK_SET) == 0);
59 uint64_t to_write = std::min(extent.num_blocks() * block_size,
60 data.size() - offset);
61 TEST_AND_RETURN_FALSE(
62 fwrite(data.data() + offset, 1, to_write, fp.get()) == to_write);
63 offset += extent.num_blocks() * block_size;
64 }
65 return true;
66 }
67
68 // Create a fake filesystem of the given |size| and initialize the partition
69 // holding it in the PartitionConfig |part|.
CreatePartition(PartitionConfig * part,const string & pattern,uint64_t block_size,off_t size)70 void CreatePartition(PartitionConfig* part, const string& pattern,
71 uint64_t block_size, off_t size) {
72 int fd = -1;
73 ASSERT_TRUE(utils::MakeTempFile(pattern.c_str(), &part->path, &fd));
74 ASSERT_EQ(0, ftruncate(fd, size));
75 ASSERT_EQ(0, close(fd));
76 part->fs_interface.reset(new FakeFilesystem(block_size, size / block_size));
77 part->size = size;
78 }
79
80 // Writes to the |partition| path blocks such they are all different and they
81 // include the tag passed in |tag|, making them also different to any other
82 // block on a partition initialized with this function with a different tag.
83 // The |block_size| should be a divisor of the partition size.
84 // Returns whether the function succeeded writing the partition.
InitializePartitionWithUniqueBlocks(const PartitionConfig & part,uint64_t block_size,uint64_t tag)85 bool InitializePartitionWithUniqueBlocks(const PartitionConfig& part,
86 uint64_t block_size,
87 uint64_t tag) {
88 TEST_AND_RETURN_FALSE(part.size % block_size == 0);
89 size_t num_blocks = part.size / block_size;
90 brillo::Blob file_data(part.size);
91 for (size_t i = 0; i < num_blocks; ++i) {
92 string prefix = base::StringPrintf(
93 "block tag 0x%.16" PRIx64 ", block number %16" PRIuS " ", tag, i);
94 brillo::Blob block_data(prefix.begin(), prefix.end());
95 TEST_AND_RETURN_FALSE(prefix.size() <= block_size);
96 block_data.resize(block_size, 'X');
97 std::copy(block_data.begin(), block_data.end(),
98 file_data.begin() + i * block_size);
99 }
100 return test_utils::WriteFileVector(part.path, file_data);
101 }
102
103 } // namespace
104
105 class DeltaDiffUtilsTest : public ::testing::Test {
106 protected:
107 const uint64_t kDefaultBlockCount = 128;
108
SetUp()109 void SetUp() override {
110 CreatePartition(&old_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
111 block_size_, block_size_ * kDefaultBlockCount);
112 CreatePartition(&new_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
113 block_size_, block_size_ * kDefaultBlockCount);
114 ASSERT_TRUE(utils::MakeTempFile("DeltaDiffUtilsTest-blob-XXXXXX",
115 &blob_path_,
116 &blob_fd_));
117 }
118
TearDown()119 void TearDown() override {
120 unlink(old_part_.path.c_str());
121 unlink(new_part_.path.c_str());
122 if (blob_fd_ != -1)
123 close(blob_fd_);
124 unlink(blob_path_.c_str());
125 }
126
127 // Helper function to call DeltaMovedAndZeroBlocks() using this class' data
128 // members. This simply avoids repeating all the arguments that never change.
RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,uint32_t minor_version)129 bool RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,
130 uint32_t minor_version) {
131 BlobFileWriter blob_file(blob_fd_, &blob_size_);
132 PayloadVersion version(kChromeOSMajorPayloadVersion, minor_version);
133 version.imgdiff_allowed = true; // Assume no fingerprint mismatch.
134 return diff_utils::DeltaMovedAndZeroBlocks(&aops_,
135 old_part_.path,
136 new_part_.path,
137 old_part_.size / block_size_,
138 new_part_.size / block_size_,
139 chunk_blocks,
140 version,
141 &blob_file,
142 &old_visited_blocks_,
143 &new_visited_blocks_);
144 }
145
146 // Old and new temporary partitions used in the tests. These are initialized
147 // with
148 PartitionConfig old_part_{"part"};
149 PartitionConfig new_part_{"part"};
150
151 // The file holding the output blob from the various diff utils functions.
152 string blob_path_;
153 int blob_fd_{-1};
154 off_t blob_size_{0};
155
156 size_t block_size_{kBlockSize};
157
158 // Default input/output arguments used when calling DeltaMovedAndZeroBlocks().
159 vector<AnnotatedOperation> aops_;
160 ExtentRanges old_visited_blocks_;
161 ExtentRanges new_visited_blocks_;
162 };
163
TEST_F(DeltaDiffUtilsTest,MoveSmallTest)164 TEST_F(DeltaDiffUtilsTest, MoveSmallTest) {
165 brillo::Blob data_blob(block_size_);
166 test_utils::FillWithData(&data_blob);
167
168 // The old file is on a different block than the new one.
169 vector<Extent> old_extents = { ExtentForRange(11, 1) };
170 vector<Extent> new_extents = { ExtentForRange(1, 1) };
171
172 EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
173 EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
174
175 brillo::Blob data;
176 InstallOperation op;
177 EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
178 old_part_.path,
179 new_part_.path,
180 old_extents,
181 new_extents,
182 PayloadVersion(kChromeOSMajorPayloadVersion, kInPlaceMinorPayloadVersion),
183 &data,
184 &op));
185 EXPECT_TRUE(data.empty());
186
187 EXPECT_TRUE(op.has_type());
188 EXPECT_EQ(InstallOperation::MOVE, op.type());
189 EXPECT_FALSE(op.has_data_offset());
190 EXPECT_FALSE(op.has_data_length());
191 EXPECT_EQ(1, op.src_extents_size());
192 EXPECT_EQ(kBlockSize, op.src_length());
193 EXPECT_EQ(1, op.dst_extents_size());
194 EXPECT_EQ(kBlockSize, op.dst_length());
195 EXPECT_EQ(BlocksInExtents(op.src_extents()),
196 BlocksInExtents(op.dst_extents()));
197 EXPECT_EQ(1U, BlocksInExtents(op.dst_extents()));
198 }
199
TEST_F(DeltaDiffUtilsTest,MoveWithSameBlock)200 TEST_F(DeltaDiffUtilsTest, MoveWithSameBlock) {
201 // Setup the old/new files so that it has immobile chunks; we make sure to
202 // utilize all sub-cases of such chunks: blocks 21--22 induce a split (src)
203 // and complete removal (dst), whereas blocks 24--25 induce trimming of the
204 // tail (src) and head (dst) of extents. The final block (29) is used for
205 // ensuring we properly account for the number of bytes removed in cases where
206 // the last block is partly filled. The detailed configuration:
207 //
208 // Old: [ 20 21 22 23 24 25 ] [ 28 29 ]
209 // New: [ 18 ] [ 21 22 ] [ 20 ] [ 24 25 26 ] [ 29 ]
210 // Same: ^^ ^^ ^^ ^^ ^^
211 vector<Extent> old_extents = {
212 ExtentForRange(20, 6),
213 ExtentForRange(28, 2) };
214 vector<Extent> new_extents = {
215 ExtentForRange(18, 1),
216 ExtentForRange(21, 2),
217 ExtentForRange(20, 1),
218 ExtentForRange(24, 3),
219 ExtentForRange(29, 1) };
220
221 uint64_t num_blocks = BlocksInExtents(old_extents);
222 EXPECT_EQ(num_blocks, BlocksInExtents(new_extents));
223
224 // The size of the data should match the total number of blocks. Each block
225 // has a different content.
226 brillo::Blob file_data;
227 for (uint64_t i = 0; i < num_blocks; ++i) {
228 file_data.resize(file_data.size() + kBlockSize, 'a' + i);
229 }
230
231 EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, file_data));
232 EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, file_data));
233
234 brillo::Blob data;
235 InstallOperation op;
236 EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
237 old_part_.path,
238 new_part_.path,
239 old_extents,
240 new_extents,
241 PayloadVersion(kChromeOSMajorPayloadVersion, kInPlaceMinorPayloadVersion),
242 &data,
243 &op));
244
245 EXPECT_TRUE(data.empty());
246
247 EXPECT_TRUE(op.has_type());
248 EXPECT_EQ(InstallOperation::MOVE, op.type());
249 EXPECT_FALSE(op.has_data_offset());
250 EXPECT_FALSE(op.has_data_length());
251
252 // The expected old and new extents that actually moved. See comment above.
253 old_extents = {
254 ExtentForRange(20, 1),
255 ExtentForRange(23, 1),
256 ExtentForRange(28, 1) };
257 new_extents = {
258 ExtentForRange(18, 1),
259 ExtentForRange(20, 1),
260 ExtentForRange(26, 1) };
261 num_blocks = BlocksInExtents(old_extents);
262
263 EXPECT_EQ(num_blocks * kBlockSize, op.src_length());
264 EXPECT_EQ(num_blocks * kBlockSize, op.dst_length());
265
266 EXPECT_EQ(old_extents.size(), static_cast<size_t>(op.src_extents_size()));
267 for (int i = 0; i < op.src_extents_size(); i++) {
268 EXPECT_EQ(old_extents[i].start_block(), op.src_extents(i).start_block())
269 << "i == " << i;
270 EXPECT_EQ(old_extents[i].num_blocks(), op.src_extents(i).num_blocks())
271 << "i == " << i;
272 }
273
274 EXPECT_EQ(new_extents.size(), static_cast<size_t>(op.dst_extents_size()));
275 for (int i = 0; i < op.dst_extents_size(); i++) {
276 EXPECT_EQ(new_extents[i].start_block(), op.dst_extents(i).start_block())
277 << "i == " << i;
278 EXPECT_EQ(new_extents[i].num_blocks(), op.dst_extents(i).num_blocks())
279 << "i == " << i;
280 }
281 }
282
TEST_F(DeltaDiffUtilsTest,BsdiffSmallTest)283 TEST_F(DeltaDiffUtilsTest, BsdiffSmallTest) {
284 // Test a BSDIFF operation from block 1 to block 2.
285 brillo::Blob data_blob(kBlockSize);
286 test_utils::FillWithData(&data_blob);
287
288 // The old file is on a different block than the new one.
289 vector<Extent> old_extents = { ExtentForRange(1, 1) };
290 vector<Extent> new_extents = { ExtentForRange(2, 1) };
291
292 EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
293 // Modify one byte in the new file.
294 data_blob[0]++;
295 EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
296
297 brillo::Blob data;
298 InstallOperation op;
299 EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
300 old_part_.path,
301 new_part_.path,
302 old_extents,
303 new_extents,
304 PayloadVersion(kChromeOSMajorPayloadVersion, kInPlaceMinorPayloadVersion),
305 &data,
306 &op));
307
308 EXPECT_FALSE(data.empty());
309
310 EXPECT_TRUE(op.has_type());
311 EXPECT_EQ(InstallOperation::BSDIFF, op.type());
312 EXPECT_FALSE(op.has_data_offset());
313 EXPECT_FALSE(op.has_data_length());
314 EXPECT_EQ(1, op.src_extents_size());
315 EXPECT_EQ(kBlockSize, op.src_length());
316 EXPECT_EQ(1, op.dst_extents_size());
317 EXPECT_EQ(kBlockSize, op.dst_length());
318 EXPECT_EQ(BlocksInExtents(op.src_extents()),
319 BlocksInExtents(op.dst_extents()));
320 EXPECT_EQ(1U, BlocksInExtents(op.dst_extents()));
321 }
322
TEST_F(DeltaDiffUtilsTest,ReplaceSmallTest)323 TEST_F(DeltaDiffUtilsTest, ReplaceSmallTest) {
324 // The old file is on a different block than the new one.
325 vector<Extent> old_extents = { ExtentForRange(1, 1) };
326 vector<Extent> new_extents = { ExtentForRange(2, 1) };
327
328 // Make a blob that's just 1's that will compress well.
329 brillo::Blob ones(kBlockSize, 1);
330
331 // Make a blob with random data that won't compress well.
332 brillo::Blob random_data;
333 std::mt19937 gen(12345);
334 std::uniform_int_distribution<uint8_t> dis(0, 255);
335 for (uint32_t i = 0; i < kBlockSize; i++) {
336 random_data.push_back(dis(gen));
337 }
338
339 for (int i = 0; i < 2; i++) {
340 brillo::Blob data_to_test = i == 0 ? random_data : ones;
341 // The old_extents will be initialized with 0.
342 EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize,
343 data_to_test));
344
345 brillo::Blob data;
346 InstallOperation op;
347 EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
348 old_part_.path,
349 new_part_.path,
350 old_extents,
351 new_extents,
352 PayloadVersion(kChromeOSMajorPayloadVersion,
353 kInPlaceMinorPayloadVersion),
354 &data,
355 &op));
356 EXPECT_FALSE(data.empty());
357
358 EXPECT_TRUE(op.has_type());
359 const InstallOperation_Type expected_type =
360 (i == 0 ? InstallOperation::REPLACE : InstallOperation::REPLACE_BZ);
361 EXPECT_EQ(expected_type, op.type());
362 EXPECT_FALSE(op.has_data_offset());
363 EXPECT_FALSE(op.has_data_length());
364 EXPECT_EQ(0, op.src_extents_size());
365 EXPECT_FALSE(op.has_src_length());
366 EXPECT_EQ(1, op.dst_extents_size());
367 EXPECT_EQ(data_to_test.size(), op.dst_length());
368 EXPECT_EQ(1U, BlocksInExtents(op.dst_extents()));
369 }
370 }
371
TEST_F(DeltaDiffUtilsTest,SourceCopyTest)372 TEST_F(DeltaDiffUtilsTest, SourceCopyTest) {
373 // Makes sure SOURCE_COPY operations are emitted whenever src_ops_allowed
374 // is true. It is the same setup as MoveSmallTest, which checks that
375 // the operation is well-formed.
376 brillo::Blob data_blob(kBlockSize);
377 test_utils::FillWithData(&data_blob);
378
379 // The old file is on a different block than the new one.
380 vector<Extent> old_extents = { ExtentForRange(11, 1) };
381 vector<Extent> new_extents = { ExtentForRange(1, 1) };
382
383 EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
384 EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
385
386 brillo::Blob data;
387 InstallOperation op;
388 EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
389 old_part_.path,
390 new_part_.path,
391 old_extents,
392 new_extents,
393 PayloadVersion(kChromeOSMajorPayloadVersion, kSourceMinorPayloadVersion),
394 &data,
395 &op));
396 EXPECT_TRUE(data.empty());
397
398 EXPECT_TRUE(op.has_type());
399 EXPECT_EQ(InstallOperation::SOURCE_COPY, op.type());
400 }
401
TEST_F(DeltaDiffUtilsTest,SourceBsdiffTest)402 TEST_F(DeltaDiffUtilsTest, SourceBsdiffTest) {
403 // Makes sure SOURCE_BSDIFF operations are emitted whenever src_ops_allowed
404 // is true. It is the same setup as BsdiffSmallTest, which checks
405 // that the operation is well-formed.
406 brillo::Blob data_blob(kBlockSize);
407 test_utils::FillWithData(&data_blob);
408
409 // The old file is on a different block than the new one.
410 vector<Extent> old_extents = { ExtentForRange(1, 1) };
411 vector<Extent> new_extents = { ExtentForRange(2, 1) };
412
413 EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
414 // Modify one byte in the new file.
415 data_blob[0]++;
416 EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
417
418 brillo::Blob data;
419 InstallOperation op;
420 EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
421 old_part_.path,
422 new_part_.path,
423 old_extents,
424 new_extents,
425 PayloadVersion(kChromeOSMajorPayloadVersion, kSourceMinorPayloadVersion),
426 &data,
427 &op));
428
429 EXPECT_FALSE(data.empty());
430 EXPECT_TRUE(op.has_type());
431 EXPECT_EQ(InstallOperation::SOURCE_BSDIFF, op.type());
432 }
433
TEST_F(DeltaDiffUtilsTest,IsNoopOperationTest)434 TEST_F(DeltaDiffUtilsTest, IsNoopOperationTest) {
435 InstallOperation op;
436 op.set_type(InstallOperation::REPLACE_BZ);
437 EXPECT_FALSE(diff_utils::IsNoopOperation(op));
438 op.set_type(InstallOperation::MOVE);
439 EXPECT_TRUE(diff_utils::IsNoopOperation(op));
440 *(op.add_src_extents()) = ExtentForRange(3, 2);
441 *(op.add_dst_extents()) = ExtentForRange(3, 2);
442 EXPECT_TRUE(diff_utils::IsNoopOperation(op));
443 *(op.add_src_extents()) = ExtentForRange(7, 5);
444 *(op.add_dst_extents()) = ExtentForRange(7, 5);
445 EXPECT_TRUE(diff_utils::IsNoopOperation(op));
446 *(op.add_src_extents()) = ExtentForRange(20, 2);
447 *(op.add_dst_extents()) = ExtentForRange(20, 1);
448 *(op.add_dst_extents()) = ExtentForRange(21, 1);
449 EXPECT_TRUE(diff_utils::IsNoopOperation(op));
450 *(op.add_src_extents()) = ExtentForRange(24, 1);
451 *(op.add_dst_extents()) = ExtentForRange(25, 1);
452 EXPECT_FALSE(diff_utils::IsNoopOperation(op));
453 }
454
TEST_F(DeltaDiffUtilsTest,FilterNoopOperations)455 TEST_F(DeltaDiffUtilsTest, FilterNoopOperations) {
456 AnnotatedOperation aop1;
457 aop1.op.set_type(InstallOperation::REPLACE_BZ);
458 *(aop1.op.add_dst_extents()) = ExtentForRange(3, 2);
459 aop1.name = "aop1";
460
461 AnnotatedOperation aop2 = aop1;
462 aop2.name = "aop2";
463
464 AnnotatedOperation noop;
465 noop.op.set_type(InstallOperation::MOVE);
466 *(noop.op.add_src_extents()) = ExtentForRange(3, 2);
467 *(noop.op.add_dst_extents()) = ExtentForRange(3, 2);
468 noop.name = "noop";
469
470 vector<AnnotatedOperation> ops = {noop, aop1, noop, noop, aop2, noop};
471 diff_utils::FilterNoopOperations(&ops);
472 EXPECT_EQ(2u, ops.size());
473 EXPECT_EQ("aop1", ops[0].name);
474 EXPECT_EQ("aop2", ops[1].name);
475 }
476
477 // Test the simple case where all the blocks are different and no new blocks are
478 // zeroed.
TEST_F(DeltaDiffUtilsTest,NoZeroedOrUniqueBlocksDetected)479 TEST_F(DeltaDiffUtilsTest, NoZeroedOrUniqueBlocksDetected) {
480 InitializePartitionWithUniqueBlocks(old_part_, block_size_, 5);
481 InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
482
483 EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
484 kInPlaceMinorPayloadVersion));
485
486 EXPECT_EQ(0U, old_visited_blocks_.blocks());
487 EXPECT_EQ(0U, new_visited_blocks_.blocks());
488 EXPECT_EQ(0, blob_size_);
489 EXPECT_TRUE(aops_.empty());
490 }
491
492 // Test that when the partitions have identical blocks in the same positions no
493 // MOVE operation is performed and all the blocks are handled.
TEST_F(DeltaDiffUtilsTest,IdenticalPartitionsDontMove)494 TEST_F(DeltaDiffUtilsTest, IdenticalPartitionsDontMove) {
495 InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
496 InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
497
498 // Mark some of the blocks as already visited.
499 vector<Extent> already_visited = {ExtentForRange(5, 10),
500 ExtentForRange(25, 10)};
501 old_visited_blocks_.AddExtents(already_visited);
502 new_visited_blocks_.AddExtents(already_visited);
503
504 // Most of the blocks rest in the same place, but there's no need for MOVE
505 // operations on those blocks.
506 EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
507 kInPlaceMinorPayloadVersion));
508
509 EXPECT_EQ(kDefaultBlockCount, old_visited_blocks_.blocks());
510 EXPECT_EQ(kDefaultBlockCount, new_visited_blocks_.blocks());
511 EXPECT_EQ(0, blob_size_);
512 EXPECT_TRUE(aops_.empty());
513 }
514
515 // Test that when the partitions have identical blocks in the same positions
516 // MOVE operation is performed and all the blocks are handled.
TEST_F(DeltaDiffUtilsTest,IdenticalBlocksAreCopiedFromSource)517 TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedFromSource) {
518 // We use a smaller partition for this test.
519 old_part_.size = kBlockSize * 50;
520 new_part_.size = kBlockSize * 50;
521
522 InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
523 InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
524
525 // Mark some of the blocks as already visited.
526 vector<Extent> already_visited = {ExtentForRange(5, 5),
527 ExtentForRange(25, 7)};
528 old_visited_blocks_.AddExtents(already_visited);
529 new_visited_blocks_.AddExtents(already_visited);
530
531 // Override some of the old blocks with different data.
532 vector<Extent> different_blocks = {ExtentForRange(40, 5)};
533 EXPECT_TRUE(WriteExtents(old_part_.path, different_blocks, kBlockSize,
534 brillo::Blob(5 * kBlockSize, 'a')));
535
536 EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(10, // chunk_blocks
537 kSourceMinorPayloadVersion));
538
539 ExtentRanges expected_ranges;
540 expected_ranges.AddExtent(ExtentForRange(0, 50));
541 expected_ranges.SubtractExtents(different_blocks);
542
543 EXPECT_EQ(expected_ranges.extent_set(), old_visited_blocks_.extent_set());
544 EXPECT_EQ(expected_ranges.extent_set(), new_visited_blocks_.extent_set());
545 EXPECT_EQ(0, blob_size_);
546
547 // We expect all the blocks that we didn't override with |different_blocks|
548 // and that we didn't mark as visited in |already_visited| to match and have a
549 // SOURCE_COPY operation chunked at 10 blocks.
550 vector<Extent> expected_op_extents = {
551 ExtentForRange(0, 5),
552 ExtentForRange(10, 10),
553 ExtentForRange(20, 5),
554 ExtentForRange(32, 8),
555 ExtentForRange(45, 5),
556 };
557
558 EXPECT_EQ(expected_op_extents.size(), aops_.size());
559 for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
560 SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
561 const AnnotatedOperation& aop = aops_[i];
562 EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
563 EXPECT_EQ(1, aop.op.src_extents_size());
564 EXPECT_EQ(expected_op_extents[i], aop.op.src_extents(0));
565 EXPECT_EQ(1, aop.op.dst_extents_size());
566 EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
567 }
568 }
569
TEST_F(DeltaDiffUtilsTest,IdenticalBlocksAreCopiedInOder)570 TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedInOder) {
571 // We use a smaller partition for this test.
572 old_part_.size = block_size_ * 50;
573 new_part_.size = block_size_ * 50;
574
575 // Create two identical partitions with 5 copies of the same unique "file".
576 brillo::Blob file_data(block_size_ * 10, 'a');
577 for (size_t offset = 0; offset < file_data.size(); offset += block_size_)
578 file_data[offset] = 'a' + offset / block_size_;
579
580 brillo::Blob partition_data(old_part_.size);
581 for (size_t offset = 0; offset < partition_data.size();
582 offset += file_data.size()) {
583 std::copy(file_data.begin(), file_data.end(),
584 partition_data.begin() + offset);
585 }
586 EXPECT_TRUE(test_utils::WriteFileVector(old_part_.path, partition_data));
587 EXPECT_TRUE(test_utils::WriteFileVector(new_part_.path, partition_data));
588
589 EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
590 kSourceMinorPayloadVersion));
591
592 // There should be only one SOURCE_COPY, for the whole partition and the
593 // source extents should cover only the first copy of the source file since
594 // we prefer to re-read files (maybe cached) instead of continue reading the
595 // rest of the partition.
596 EXPECT_EQ(1U, aops_.size());
597 const AnnotatedOperation& aop = aops_[0];
598 EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
599 EXPECT_EQ(5, aop.op.src_extents_size());
600 for (int i = 0; i < aop.op.src_extents_size(); ++i) {
601 EXPECT_EQ(ExtentForRange(0, 10), aop.op.src_extents(i));
602 }
603
604 EXPECT_EQ(1, aop.op.dst_extents_size());
605 EXPECT_EQ(ExtentForRange(0, 50), aop.op.dst_extents(0));
606
607 EXPECT_EQ(0, blob_size_);
608 }
609
610 // Test that all blocks with zeros are handled separately using REPLACE_BZ
611 // operations unless they are not moved.
TEST_F(DeltaDiffUtilsTest,ZeroBlocksUseReplaceBz)612 TEST_F(DeltaDiffUtilsTest, ZeroBlocksUseReplaceBz) {
613 InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
614 InitializePartitionWithUniqueBlocks(new_part_, block_size_, 5);
615
616 // We set four ranges of blocks with zeros: a single block, a range that fits
617 // in the chunk size, a range that doesn't and finally a range of zeros that
618 // was also zeros in the old image.
619 vector<Extent> new_zeros = {
620 ExtentForRange(10, 1),
621 ExtentForRange(20, 4),
622 // The last range is split since the old image has zeros in part of it.
623 ExtentForRange(30, 20),
624 };
625 brillo::Blob zeros_data(BlocksInExtents(new_zeros) * block_size_, '\0');
626 EXPECT_TRUE(WriteExtents(new_part_.path, new_zeros, block_size_, zeros_data));
627
628 vector<Extent> old_zeros = vector<Extent>{ExtentForRange(43, 7)};
629 EXPECT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
630
631 EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(5, // chunk_blocks
632 kInPlaceMinorPayloadVersion));
633
634 // Zeroed blocks from old_visited_blocks_ were copied over, so me actually
635 // use them regardless of the trivial MOVE operation not being emitted.
636 EXPECT_EQ(old_zeros,
637 old_visited_blocks_.GetExtentsForBlockCount(
638 old_visited_blocks_.blocks()));
639
640 // All the new zeroed blocks should be used, part with REPLACE_BZ and part
641 // trivial MOVE operations (not included).
642 EXPECT_EQ(new_zeros,
643 new_visited_blocks_.GetExtentsForBlockCount(
644 new_visited_blocks_.blocks()));
645
646 vector<Extent> expected_op_extents = {
647 ExtentForRange(10, 1),
648 ExtentForRange(20, 4),
649 // This range should be split.
650 ExtentForRange(30, 5),
651 ExtentForRange(35, 5),
652 ExtentForRange(40, 3),
653 };
654
655 EXPECT_EQ(expected_op_extents.size(), aops_.size());
656 for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
657 SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
658 const AnnotatedOperation& aop = aops_[i];
659 EXPECT_EQ(InstallOperation::REPLACE_BZ, aop.op.type());
660 EXPECT_EQ(0, aop.op.src_extents_size());
661 EXPECT_EQ(1, aop.op.dst_extents_size());
662 EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
663 }
664 EXPECT_NE(0, blob_size_);
665 }
666
TEST_F(DeltaDiffUtilsTest,ShuffledBlocksAreTracked)667 TEST_F(DeltaDiffUtilsTest, ShuffledBlocksAreTracked) {
668 vector<uint64_t> permutation = {0, 1, 5, 6, 7, 2, 3, 4, 9, 10, 11, 12, 8};
669 vector<Extent> perm_extents;
670 for (uint64_t x : permutation)
671 AppendBlockToExtents(&perm_extents, x);
672
673 // We use a smaller partition for this test.
674 old_part_.size = block_size_ * permutation.size();
675 new_part_.size = block_size_ * permutation.size();
676 InitializePartitionWithUniqueBlocks(new_part_, block_size_, 123);
677
678 // We initialize the old_part_ with the blocks from new_part but in the
679 // |permutation| order. Block i in the old_part_ will contain the same data
680 // as block permutation[i] in the new_part_.
681 brillo::Blob new_contents;
682 EXPECT_TRUE(utils::ReadFile(new_part_.path, &new_contents));
683 EXPECT_TRUE(WriteExtents(old_part_.path, perm_extents, block_size_,
684 new_contents));
685
686 EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1, // chunk_blocks
687 kSourceMinorPayloadVersion));
688
689 EXPECT_EQ(permutation.size(), old_visited_blocks_.blocks());
690 EXPECT_EQ(permutation.size(), new_visited_blocks_.blocks());
691
692 // There should be only one SOURCE_COPY, with a complicate list of extents.
693 EXPECT_EQ(1U, aops_.size());
694 const AnnotatedOperation& aop = aops_[0];
695 EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
696 vector<Extent> aop_src_extents;
697 ExtentsToVector(aop.op.src_extents(), &aop_src_extents);
698 EXPECT_EQ(perm_extents, aop_src_extents);
699
700 EXPECT_EQ(1, aop.op.dst_extents_size());
701 EXPECT_EQ(ExtentForRange(0, permutation.size()), aop.op.dst_extents(0));
702
703 EXPECT_EQ(0, blob_size_);
704 }
705
706 } // namespace chromeos_update_engine
707