Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(270)

Side by Side Diff: delta_diff_generator.cc

Issue 3767002: AU: Really delta compress the kernel if an old kernel is provided. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/update_engine.git
Patch Set: Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « delta_diff_generator.h ('k') | delta_diff_generator_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "update_engine/delta_diff_generator.h" 5 #include "update_engine/delta_diff_generator.h"
6 6
7 #include <errno.h> 7 #include <errno.h>
8 #include <fcntl.h> 8 #include <fcntl.h>
9 #include <inttypes.h> 9 #include <inttypes.h>
10 #include <sys/stat.h> 10 #include <sys/stat.h>
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
47 namespace chromeos_update_engine { 47 namespace chromeos_update_engine {
48 48
49 typedef DeltaDiffGenerator::Block Block; 49 typedef DeltaDiffGenerator::Block Block;
50 50
51 namespace { 51 namespace {
52 const size_t kBlockSize = 4096; // bytes 52 const size_t kBlockSize = 4096; // bytes
53 const size_t kRootFSPartitionSize = 1 * 1024 * 1024 * 1024; // 1 GiB 53 const size_t kRootFSPartitionSize = 1 * 1024 * 1024 * 1024; // 1 GiB
54 const uint64_t kVersionNumber = 1; 54 const uint64_t kVersionNumber = 1;
55 const uint64_t kFullUpdateChunkSize = 128 * 1024; // bytes 55 const uint64_t kFullUpdateChunkSize = 128 * 1024; // bytes
56 56
57 static const char* kInstallOperationTypes[] = {
58 "REPLACE",
59 "REPLACE_BZ",
60 "MOVE",
61 "BSDIFF"
62 };
63
57 // Stores all Extents for a file into 'out'. Returns true on success. 64 // Stores all Extents for a file into 'out'. Returns true on success.
58 bool GatherExtents(const string& path, 65 bool GatherExtents(const string& path,
59 google::protobuf::RepeatedPtrField<Extent>* out) { 66 google::protobuf::RepeatedPtrField<Extent>* out) {
60 vector<Extent> extents; 67 vector<Extent> extents;
61 TEST_AND_RETURN_FALSE(extent_mapper::ExtentsForFileFibmap(path, &extents)); 68 TEST_AND_RETURN_FALSE(extent_mapper::ExtentsForFileFibmap(path, &extents));
62 DeltaDiffGenerator::StoreExtents(extents, out); 69 DeltaDiffGenerator::StoreExtents(extents, out);
63 return true; 70 return true;
64 } 71 }
65 72
66 // Runs the bsdiff tool on two files and returns the resulting delta in 73 // Runs the bsdiff tool on two files and returns the resulting delta in
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 const string& new_root, 163 const string& new_root,
157 const string& path, // within new_root 164 const string& path, // within new_root
158 int data_fd, 165 int data_fd,
159 off_t* data_file_size) { 166 off_t* data_file_size) {
160 vector<char> data; 167 vector<char> data;
161 DeltaArchiveManifest_InstallOperation operation; 168 DeltaArchiveManifest_InstallOperation operation;
162 169
163 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ReadFileToDiff(old_root + path, 170 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ReadFileToDiff(old_root + path,
164 new_root + path, 171 new_root + path,
165 &data, 172 &data,
166 &operation)); 173 &operation,
174 true));
167 175
168 // Write the data 176 // Write the data
169 if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) { 177 if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) {
170 operation.set_data_offset(*data_file_size); 178 operation.set_data_offset(*data_file_size);
171 operation.set_data_length(data.size()); 179 operation.set_data_length(data.size());
172 } 180 }
173 181
174 TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, &data[0], data.size())); 182 TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, &data[0], data.size()));
175 *data_file_size += data.size(); 183 *data_file_size += data.size();
176 184
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
388 *op = *it; 396 *op = *it;
389 } 397 }
390 } 398 }
391 399
392 void CheckGraph(const Graph& graph) { 400 void CheckGraph(const Graph& graph) {
393 for (Graph::const_iterator it = graph.begin(); it != graph.end(); ++it) { 401 for (Graph::const_iterator it = graph.begin(); it != graph.end(); ++it) {
394 CHECK(it->op.has_type()); 402 CHECK(it->op.has_type());
395 } 403 }
396 } 404 }
397 405
398 // Delta compresses a kernel partition new_kernel_part with knowledge of 406 // Delta compresses a kernel partition |new_kernel_part| with knowledge of the
399 // the old kernel partition old_kernel_part. 407 // old kernel partition |old_kernel_part|. If |old_kernel_part| is an empty
408 // string, generates a full update of the partition.
400 bool DeltaCompressKernelPartition( 409 bool DeltaCompressKernelPartition(
401 const string& old_kernel_part, 410 const string& old_kernel_part,
402 const string& new_kernel_part, 411 const string& new_kernel_part,
403 vector<DeltaArchiveManifest_InstallOperation>* ops, 412 vector<DeltaArchiveManifest_InstallOperation>* ops,
404 int blobs_fd, 413 int blobs_fd,
405 off_t* blobs_length) { 414 off_t* blobs_length) {
406 // For now, just bsdiff the kernel partition as a whole.
407 // TODO(adlr): Use knowledge of how the kernel partition is laid out
408 // to more efficiently compress it.
409
410 LOG(INFO) << "Delta compressing kernel partition..."; 415 LOG(INFO) << "Delta compressing kernel partition...";
416 LOG_IF(INFO, old_kernel_part.empty()) << "Generating full kernel update...";
411 417
412 // Add a new install operation 418 // Add a new install operation
413 ops->resize(1); 419 ops->resize(1);
414 DeltaArchiveManifest_InstallOperation* op = &(*ops)[0]; 420 DeltaArchiveManifest_InstallOperation* op = &(*ops)[0];
415 op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
416 op->set_data_offset(*blobs_length);
417 421
418 // Do the actual compression
419 vector<char> data; 422 vector<char> data;
420 TEST_AND_RETURN_FALSE(utils::ReadFile(new_kernel_part, &data)); 423 TEST_AND_RETURN_FALSE(DeltaDiffGenerator::ReadFileToDiff(old_kernel_part,
421 TEST_AND_RETURN_FALSE(!data.empty()); 424 new_kernel_part,
425 &data,
426 op,
427 false));
422 428
423 vector<char> data_bz; 429 // Write the data
424 TEST_AND_RETURN_FALSE(BzipCompress(data, &data_bz)); 430 if (op->type() != DeltaArchiveManifest_InstallOperation_Type_MOVE) {
425 CHECK(!data_bz.empty()); 431 op->set_data_offset(*blobs_length);
432 op->set_data_length(data.size());
433 }
426 434
427 TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd, &data_bz[0], data_bz.size())); 435 TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd, &data[0], data.size()));
428 *blobs_length += data_bz.size(); 436 *blobs_length += data.size();
429 437
430 off_t new_part_size = utils::FileSize(new_kernel_part); 438 LOG(INFO) << "Done delta compressing kernel partition: "
431 TEST_AND_RETURN_FALSE(new_part_size >= 0); 439 << kInstallOperationTypes[op->type()];
432
433 op->set_data_length(data_bz.size());
434
435 op->set_dst_length(new_part_size);
436
437 // There's a single dest extent
438 Extent* dst_extent = op->add_dst_extents();
439 dst_extent->set_start_block(0);
440 dst_extent->set_num_blocks((new_part_size + kBlockSize - 1) / kBlockSize);
441
442 LOG(INFO) << "Done compressing kernel partition.";
443 return true; 440 return true;
444 } 441 }
445 442
446 struct DeltaObject { 443 struct DeltaObject {
447 DeltaObject(const string& in_name, const int in_type, const off_t in_size) 444 DeltaObject(const string& in_name, const int in_type, const off_t in_size)
448 : name(in_name), 445 : name(in_name),
449 type(in_type), 446 type(in_type),
450 size(in_size) {} 447 size(in_size) {}
451 bool operator <(const DeltaObject& object) const { 448 bool operator <(const DeltaObject& object) const {
452 return size < object.size; 449 return size < object.size;
453 } 450 }
454 string name; 451 string name;
455 int type; 452 int type;
456 off_t size; 453 off_t size;
457 }; 454 };
458 455
459 static const char* kInstallOperationTypes[] = {
460 "REPLACE",
461 "REPLACE_BZ",
462 "MOVE",
463 "BSDIFF"
464 };
465
466 void ReportPayloadUsage(const Graph& graph, 456 void ReportPayloadUsage(const Graph& graph,
467 const DeltaArchiveManifest& manifest, 457 const DeltaArchiveManifest& manifest,
468 const int64_t manifest_metadata_size) { 458 const int64_t manifest_metadata_size) {
469 vector<DeltaObject> objects; 459 vector<DeltaObject> objects;
470 off_t total_size = 0; 460 off_t total_size = 0;
471 461
472 // Graph nodes with information about file names. 462 // Graph nodes with information about file names.
473 for (Vertex::Index node = 0; node < graph.size(); node++) { 463 for (Vertex::Index node = 0; node < graph.size(); node++) {
474 const Vertex& vertex = graph[node]; 464 const Vertex& vertex = graph[node];
475 if (!vertex.valid) { 465 if (!vertex.valid) {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 } 500 }
511 fprintf(stderr, kFormatString, 100.0, total_size, "", "<total>"); 501 fprintf(stderr, kFormatString, 100.0, total_size, "", "<total>");
512 } 502 }
513 503
514 } // namespace {} 504 } // namespace {}
515 505
516 bool DeltaDiffGenerator::ReadFileToDiff( 506 bool DeltaDiffGenerator::ReadFileToDiff(
517 const string& old_filename, 507 const string& old_filename,
518 const string& new_filename, 508 const string& new_filename,
519 vector<char>* out_data, 509 vector<char>* out_data,
520 DeltaArchiveManifest_InstallOperation* out_op) { 510 DeltaArchiveManifest_InstallOperation* out_op,
511 bool gather_extents) {
521 // Read new data in 512 // Read new data in
522 vector<char> new_data; 513 vector<char> new_data;
523 TEST_AND_RETURN_FALSE(utils::ReadFile(new_filename, &new_data)); 514 TEST_AND_RETURN_FALSE(utils::ReadFile(new_filename, &new_data));
524 515
525 TEST_AND_RETURN_FALSE(!new_data.empty()); 516 TEST_AND_RETURN_FALSE(!new_data.empty());
526 517
527 vector<char> new_data_bz; 518 vector<char> new_data_bz;
528 TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz)); 519 TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz));
529 CHECK(!new_data_bz.empty()); 520 CHECK(!new_data_bz.empty());
530 521
531 vector<char> data; // Data blob that will be written to delta file. 522 vector<char> data; // Data blob that will be written to delta file.
532 523
533 DeltaArchiveManifest_InstallOperation operation; 524 DeltaArchiveManifest_InstallOperation operation;
534 size_t current_best_size = 0; 525 size_t current_best_size = 0;
535 if (new_data.size() <= new_data_bz.size()) { 526 if (new_data.size() <= new_data_bz.size()) {
536 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE); 527 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
537 current_best_size = new_data.size(); 528 current_best_size = new_data.size();
538 data = new_data; 529 data = new_data;
539 } else { 530 } else {
540 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ); 531 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
541 current_best_size = new_data_bz.size(); 532 current_best_size = new_data_bz.size();
542 data = new_data_bz; 533 data = new_data_bz;
543 } 534 }
544 535
545 // Do we have an original file to consider? 536 // Do we have an original file to consider?
546 struct stat old_stbuf; 537 struct stat old_stbuf;
547 if (0 != stat(old_filename.c_str(), &old_stbuf)) { 538 bool no_original = old_filename.empty();
539 if (!no_original && 0 != stat(old_filename.c_str(), &old_stbuf)) {
548 // If stat-ing the old file fails, it should be because it doesn't exist. 540 // If stat-ing the old file fails, it should be because it doesn't exist.
549 TEST_AND_RETURN_FALSE(errno == ENOTDIR || errno == ENOENT); 541 TEST_AND_RETURN_FALSE(errno == ENOTDIR || errno == ENOENT);
550 } else { 542 no_original = true;
543 }
544 if (!no_original) {
551 // Read old data 545 // Read old data
552 vector<char> old_data; 546 vector<char> old_data;
553 TEST_AND_RETURN_FALSE(utils::ReadFile(old_filename, &old_data)); 547 TEST_AND_RETURN_FALSE(utils::ReadFile(old_filename, &old_data));
554 if (old_data == new_data) { 548 if (old_data == new_data) {
555 // No change in data. 549 // No change in data.
556 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE); 550 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
557 current_best_size = 0; 551 current_best_size = 0;
558 data.clear(); 552 data.clear();
559 } else { 553 } else {
560 // Try bsdiff of old to new data 554 // Try bsdiff of old to new data
561 vector<char> bsdiff_delta; 555 vector<char> bsdiff_delta;
562 TEST_AND_RETURN_FALSE( 556 TEST_AND_RETURN_FALSE(
563 BsdiffFiles(old_filename, new_filename, &bsdiff_delta)); 557 BsdiffFiles(old_filename, new_filename, &bsdiff_delta));
564 CHECK_GT(bsdiff_delta.size(), 0); 558 CHECK_GT(bsdiff_delta.size(), 0);
565 if (bsdiff_delta.size() < current_best_size) { 559 if (bsdiff_delta.size() < current_best_size) {
566 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF); 560 operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
567 current_best_size = bsdiff_delta.size(); 561 current_best_size = bsdiff_delta.size();
568 562
569 data = bsdiff_delta; 563 data = bsdiff_delta;
570 } 564 }
571 } 565 }
572 } 566 }
573 567
574 // Set parameters of the operations 568 // Set parameters of the operations
575 CHECK_EQ(data.size(), current_best_size); 569 CHECK_EQ(data.size(), current_best_size);
576 570
577 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE || 571 if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE ||
578 operation.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) { 572 operation.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) {
579 TEST_AND_RETURN_FALSE( 573 if (gather_extents) {
580 GatherExtents(old_filename, operation.mutable_src_extents())); 574 TEST_AND_RETURN_FALSE(
575 GatherExtents(old_filename, operation.mutable_src_extents()));
576 } else {
577 Extent* src_extent = operation.add_src_extents();
578 src_extent->set_start_block(0);
579 src_extent->set_num_blocks(
580 (old_stbuf.st_size + kBlockSize - 1) / kBlockSize);
581 }
581 operation.set_src_length(old_stbuf.st_size); 582 operation.set_src_length(old_stbuf.st_size);
582 } 583 }
583 584
584 TEST_AND_RETURN_FALSE( 585 if (gather_extents) {
585 GatherExtents(new_filename, operation.mutable_dst_extents())); 586 TEST_AND_RETURN_FALSE(
587 GatherExtents(new_filename, operation.mutable_dst_extents()));
588 } else {
589 Extent* dst_extent = operation.add_dst_extents();
590 dst_extent->set_start_block(0);
591 dst_extent->set_num_blocks((new_data.size() + kBlockSize - 1) / kBlockSize);
592 }
586 operation.set_dst_length(new_data.size()); 593 operation.set_dst_length(new_data.size());
587 594
588 out_data->swap(data); 595 out_data->swap(data);
589 *out_op = operation; 596 *out_op = operation;
590 597
591 return true; 598 return true;
592 } 599 }
593 600
594 bool InitializePartitionInfo(bool is_kernel, 601 bool InitializePartitionInfo(bool is_kernel,
595 const string& partition, 602 const string& partition,
(...skipping 670 matching lines...) Expand 10 before | Expand all | Expand 10 after
1266 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(new_image, 1273 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(new_image,
1267 &new_image_block_count, 1274 &new_image_block_count,
1268 &new_image_block_size)); 1275 &new_image_block_size));
1269 if (!old_image.empty()) { 1276 if (!old_image.empty()) {
1270 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(old_image, 1277 TEST_AND_RETURN_FALSE(utils::GetFilesystemSize(old_image,
1271 &old_image_block_count, 1278 &old_image_block_count,
1272 &old_image_block_size)); 1279 &old_image_block_size));
1273 TEST_AND_RETURN_FALSE(old_image_block_size == new_image_block_size); 1280 TEST_AND_RETURN_FALSE(old_image_block_size == new_image_block_size);
1274 LOG_IF(WARNING, old_image_block_count != new_image_block_count) 1281 LOG_IF(WARNING, old_image_block_count != new_image_block_count)
1275 << "Old and new images have different block counts."; 1282 << "Old and new images have different block counts.";
1276 // Sanity check kernel partition arg
1277 TEST_AND_RETURN_FALSE(utils::FileSize(old_kernel_part) >= 0);
1278 } 1283 }
1279 // Sanity check kernel partition arg 1284 // Sanity check kernel partition arg
1280 TEST_AND_RETURN_FALSE(utils::FileSize(new_kernel_part) >= 0); 1285 TEST_AND_RETURN_FALSE(utils::FileSize(new_kernel_part) >= 0);
1281 1286
1282 vector<Block> blocks(max(old_image_block_count, new_image_block_count)); 1287 vector<Block> blocks(max(old_image_block_count, new_image_block_count));
1283 LOG(INFO) << "Invalid block index: " << Vertex::kInvalidIndex; 1288 LOG(INFO) << "Invalid block index: " << Vertex::kInvalidIndex;
1284 LOG(INFO) << "Block count: " << blocks.size(); 1289 LOG(INFO) << "Block count: " << blocks.size();
1285 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) { 1290 for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
1286 CHECK(blocks[i].reader == Vertex::kInvalidIndex); 1291 CHECK(blocks[i].reader == Vertex::kInvalidIndex);
1287 CHECK(blocks[i].writer == Vertex::kInvalidIndex); 1292 CHECK(blocks[i].writer == Vertex::kInvalidIndex);
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
1509 1514
1510 LOG(INFO) << "All done. Successfully created delta file."; 1515 LOG(INFO) << "All done. Successfully created delta file.";
1511 return true; 1516 return true;
1512 } 1517 }
1513 1518
1514 const char* const kBsdiffPath = "/usr/bin/bsdiff"; 1519 const char* const kBsdiffPath = "/usr/bin/bsdiff";
1515 const char* const kBspatchPath = "/usr/bin/bspatch"; 1520 const char* const kBspatchPath = "/usr/bin/bspatch";
1516 const char* const kDeltaMagic = "CrAU"; 1521 const char* const kDeltaMagic = "CrAU";
1517 1522
1518 }; // namespace chromeos_update_engine 1523 }; // namespace chromeos_update_engine
OLDNEW
« no previous file with comments | « delta_diff_generator.h ('k') | delta_diff_generator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698