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