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. 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|. 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. 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 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 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. 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 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 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 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 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 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 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 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 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. 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. 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. 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 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. 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 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