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