Add human friendly payload information to the manifest.

This adds new strings to the payload manifest that describe what images
the payload updates from/to. These strings are passed in when delta_generator
is invoked, and are trusted implicitly.

BUG=chromium:226310
TEST=Built/Updated.

Change-Id: I278137c97cf8376d4e2fd8e82402cbb7d4f1a104
Reviewed-on: https://gerrit.chromium.org/gerrit/47347
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Don Garrett <dgarrett@chromium.org>
Commit-Queue: Don Garrett <dgarrett@chromium.org>
diff --git a/delta_diff_generator.cc b/delta_diff_generator.cc
index ee16454..9448c63 100644
--- a/delta_diff_generator.cc
+++ b/delta_diff_generator.cc
@@ -1387,6 +1387,8 @@
     const string& private_key_path,
     off_t chunk_size,
     size_t rootfs_partition_size,
+    const ImageInfo* old_image_info,
+    const ImageInfo* new_image_info,
     uint64_t* metadata_size) {
   TEST_AND_RETURN_FALSE(chunk_size == -1 || chunk_size % kBlockSize == 0);
   int old_image_block_count = 0, old_image_block_size = 0;
@@ -1401,6 +1403,12 @@
     TEST_AND_RETURN_FALSE(old_image_block_size == new_image_block_size);
     LOG_IF(WARNING, old_image_block_count != new_image_block_count)
         << "Old and new images have different block counts.";
+
+    // If new_image_info is present, old_image_info must but be present.
+    TEST_AND_RETURN_FALSE((bool)old_image_info == (bool)new_image_info);
+  } else {
+    // old_image_info must not be present for a full update.
+    TEST_AND_RETURN_FALSE(!old_image_info);
   }
 
   // Sanity checks for the partition size.
@@ -1521,6 +1529,13 @@
 
   // Convert to protobuf Manifest object
   DeltaArchiveManifest manifest;
+
+  if (old_image_info)
+    *(manifest.mutable_old_image_info()) = *old_image_info;
+
+  if (new_image_info)
+    *(manifest.mutable_new_image_info()) = *new_image_info;
+
   OperationNameMap op_name_map;
   CheckGraph(graph);
   InstallOperationsToManifest(graph,
diff --git a/delta_diff_generator.h b/delta_diff_generator.h
index c258a58..aec403a 100644
--- a/delta_diff_generator.h
+++ b/delta_diff_generator.h
@@ -76,6 +76,8 @@
                                       const std::string& private_key_path,
                                       off_t chunk_size,
                                       size_t rootfs_partition_size,
+                                      const ImageInfo* old_image_info,
+                                      const ImageInfo* new_image_info,
                                       uint64_t* metadata_size);
 
   // These functions are public so that the unit tests can access them:
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 8964a3c..410fceb 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -269,6 +269,26 @@
       state->image_size + 1024 * 1024 - 1)));
   EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img));
 
+  // Create ImageInfo A & B
+  ImageInfo old_image_info;
+  ImageInfo new_image_info;
+
+  if (!full_rootfs) {
+    old_image_info.set_channel("src-channel");
+    old_image_info.set_board("src-board");
+    old_image_info.set_version("src-version");
+    old_image_info.set_key("src-key");
+    old_image_info.set_build_channel("src-build-channel");
+    old_image_info.set_build_version("src-build-version");
+  }
+
+  new_image_info.set_channel("test-channel");
+  new_image_info.set_board("test-board");
+  new_image_info.set_version("test-version");
+  new_image_info.set_key("test-key");
+  new_image_info.set_build_channel("test-build-channel");
+  new_image_info.set_build_version("test-build-version");
+
   // Make some changes to the A image.
   {
     string a_mnt;
@@ -311,6 +331,7 @@
   if (noop) {
     EXPECT_TRUE(file_util::CopyFile(FilePath(state->a_img),
                                     FilePath(state->b_img)));
+    old_image_info = new_image_info;
   } else {
     CreateExtImageAtPath(state->b_img, NULL);
     EXPECT_EQ(0, System(base::StringPrintf(
@@ -424,6 +445,8 @@
             private_key,
             chunk_size,
             kRootFSPartitionSize,
+            full_rootfs ? NULL : &old_image_info,
+            &new_image_info,
             &state->metadata_size));
   }
 
@@ -454,6 +477,8 @@
                                            &state->metadata_size));
     LOG(INFO) << "Metadata size: " << state->metadata_size;
 
+
+
     if (signature_test == kSignatureNone) {
       EXPECT_FALSE(manifest.has_signatures_offset());
       EXPECT_FALSE(manifest.has_signatures_size());
@@ -498,8 +523,41 @@
       EXPECT_FALSE(manifest.old_kernel_info().hash().empty());
     }
 
+    EXPECT_EQ(manifest.new_image_info().channel(), "test-channel");
+    EXPECT_EQ(manifest.new_image_info().board(), "test-board");
+    EXPECT_EQ(manifest.new_image_info().version(), "test-version");
+    EXPECT_EQ(manifest.new_image_info().key(), "test-key");
+    EXPECT_EQ(manifest.new_image_info().build_channel(), "test-build-channel");
+    EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version");
+
+    if (!full_rootfs) {
+
+      if (noop) {
+        EXPECT_EQ(manifest.old_image_info().channel(), "test-channel");
+        EXPECT_EQ(manifest.old_image_info().board(), "test-board");
+        EXPECT_EQ(manifest.old_image_info().version(), "test-version");
+        EXPECT_EQ(manifest.old_image_info().key(), "test-key");
+        EXPECT_EQ(manifest.old_image_info().build_channel(),
+                  "test-build-channel");
+        EXPECT_EQ(manifest.old_image_info().build_version(),
+                  "test-build-version");
+      } else {
+        EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
+        EXPECT_EQ(manifest.old_image_info().board(), "src-board");
+        EXPECT_EQ(manifest.old_image_info().version(), "src-version");
+        EXPECT_EQ(manifest.old_image_info().key(), "src-key");
+        EXPECT_EQ(manifest.old_image_info().build_channel(),
+                  "src-build-channel");
+        EXPECT_EQ(manifest.old_image_info().build_version(),
+                  "src-build-version");
+      }
+    }
+
+
     if (full_rootfs) {
       EXPECT_FALSE(manifest.has_old_rootfs_info());
+      EXPECT_FALSE(manifest.has_old_image_info());
+      EXPECT_TRUE(manifest.has_new_image_info());
     } else {
       EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size());
       EXPECT_FALSE(manifest.old_rootfs_info().hash().empty());
@@ -653,9 +711,9 @@
   uint64_t new_rootfs_size;
   vector<char> new_rootfs_hash;
   EXPECT_TRUE(performer->GetNewPartitionInfo(&new_kernel_size,
-                                            &new_kernel_hash,
-                                            &new_rootfs_size,
-                                            &new_rootfs_hash));
+                                             &new_kernel_hash,
+                                             &new_rootfs_size,
+                                             &new_rootfs_hash));
   EXPECT_EQ(kDefaultKernelSize, new_kernel_size);
   vector<char> expected_new_kernel_hash;
   EXPECT_TRUE(OmahaHashCalculator::RawHashOfData(state->new_kernel_data,
@@ -695,6 +753,7 @@
   DeltaPerformer *performer;
   GenerateDeltaFile(full_kernel, full_rootfs, noop, chunk_size,
                     signature_test, &state);
+
   ScopedPathUnlinker a_img_unlinker(state.a_img);
   ScopedPathUnlinker b_img_unlinker(state.b_img);
   ScopedPathUnlinker delta_unlinker(state.delta_path);
diff --git a/generate_delta_main.cc b/generate_delta_main.cc
index 7c8b54b..1f49c42 100644
--- a/generate_delta_main.cc
+++ b/generate_delta_main.cc
@@ -65,6 +65,42 @@
              chromeos_update_engine::kRootFSPartitionSize,
              "RootFS partition size for the image once installed");
 
+DEFINE_string(old_channel, "",
+              "The channel for the old image. 'dev-channel', 'npo-channel', "
+              "etc. Ignored, except during delta generation.");
+DEFINE_string(old_board, "",
+              "The board for the old image. 'x86-mario', 'lumpy', "
+              "etc. Ignored, except during delta generation.");
+DEFINE_string(old_version, "",
+              "The build version of the old image. 1.2.3, etc.");
+DEFINE_string(old_key, "",
+              "The key used to sign the old image. 'premp', 'mp', 'mp-v3',"
+              " etc");
+DEFINE_string(old_build_channel, "",
+              "The channel for the build of the old image. 'dev-channel', "
+              "etc, but will never contain special channels such as "
+              "'npo-channel'. Ignored, except during delta generation.");
+DEFINE_string(old_build_version, "",
+              "The version of the build containing the old image.");
+
+DEFINE_string(new_channel, "",
+              "The channel for the new image. 'dev-channel', 'npo-channel', "
+              "etc. Ignored, except during delta generation.");
+DEFINE_string(new_board, "",
+              "The board for the new image. 'x86-mario', 'lumpy', "
+              "etc. Ignored, except during delta generation.");
+DEFINE_string(new_version, "",
+              "The build version of the new image. 1.2.3, etc.");
+DEFINE_string(new_key, "",
+              "The key used to sign the new image. 'premp', 'mp', 'mp-v3',"
+              " etc");
+DEFINE_string(new_build_channel, "",
+              "The channel for the build of the new image. 'dev-channel', "
+              "etc, but will never contain special channels such as "
+              "'npo-channel'. Ignored, except during delta generation.");
+DEFINE_string(new_build_version, "",
+              "The version of the build containing the new image.");
+
 // This file contains a simple program that takes an old path, a new path,
 // and an output file as arguments and the path to an output file and
 // generates a delta that can be sent to Chrome OS clients.
@@ -99,6 +135,40 @@
 }
 
 
+bool ParseImageInfo(const string& channel,
+                    const string& board,
+                    const string& version,
+                    const string& key,
+                    const string& build_channel,
+                    const string& build_version,
+                    ImageInfo* image_info) {
+
+  // All of these arguments should be present or missing.
+  bool empty = channel.empty();
+
+  CHECK_EQ(channel.empty(), empty);
+  CHECK_EQ(board.empty(), empty);
+  CHECK_EQ(version.empty(), empty);
+  CHECK_EQ(key.empty(), empty);
+
+  if (empty)
+    return false;
+
+  image_info->set_channel(channel);
+  image_info->set_board(board);
+  image_info->set_version(version);
+  image_info->set_key(key);
+
+  image_info->set_build_channel(
+      build_channel.empty() ? channel : build_channel);
+
+  image_info->set_build_version(
+      build_version.empty() ? version : build_version);
+
+  return true;
+}
+
+
 void CalculatePayloadHashForSigning() {
   LOG(INFO) << "Calculating payload hash for signing.";
   LOG_IF(FATAL, FLAGS_in_file.empty())
@@ -257,28 +327,56 @@
   CHECK(!FLAGS_new_image.empty());
   CHECK(!FLAGS_out_file.empty());
   CHECK(!FLAGS_new_kernel.empty());
-  if (FLAGS_old_image.empty()) {
-    LOG(INFO) << "Generating full update";
-  } else {
+
+  bool is_delta = !FLAGS_old_image.empty();
+
+  ImageInfo old_image_info;
+  ImageInfo new_image_info;
+
+  // TODO(dgarrett) Check the result is True when these args are required.
+  ParseImageInfo(FLAGS_new_channel,
+                 FLAGS_new_board,
+                 FLAGS_new_version,
+                 FLAGS_new_key,
+                 FLAGS_new_build_channel,
+                 FLAGS_new_build_version,
+                 &new_image_info);
+
+  CHECK(is_delta || !ParseImageInfo(FLAGS_old_channel,
+                                    FLAGS_old_board,
+                                    FLAGS_old_version,
+                                    FLAGS_old_key,
+                                    FLAGS_old_build_channel,
+                                    FLAGS_old_build_version,
+                                    &old_image_info));
+
+
+  if (is_delta) {
     LOG(INFO) << "Generating delta update";
     CHECK(!FLAGS_old_dir.empty());
     CHECK(!FLAGS_new_dir.empty());
     if ((!IsDir(FLAGS_old_dir.c_str())) || (!IsDir(FLAGS_new_dir.c_str()))) {
       LOG(FATAL) << "old_dir or new_dir not directory";
     }
+  } else {
+    LOG(INFO) << "Generating full update";
   }
+
   uint64_t metadata_size;
-  if (!DeltaDiffGenerator::GenerateDeltaUpdateFile(FLAGS_old_dir,
-                                                   FLAGS_old_image,
-                                                   FLAGS_new_dir,
-                                                   FLAGS_new_image,
-                                                   FLAGS_old_kernel,
-                                                   FLAGS_new_kernel,
-                                                   FLAGS_out_file,
-                                                   FLAGS_private_key,
-                                                   FLAGS_chunk_size,
-                                                   FLAGS_rootfs_partition_size,
-                                                   &metadata_size)) {
+  if (!DeltaDiffGenerator::GenerateDeltaUpdateFile(
+      FLAGS_old_dir,
+      FLAGS_old_image,
+      FLAGS_new_dir,
+      FLAGS_new_image,
+      FLAGS_old_kernel,
+      FLAGS_new_kernel,
+      FLAGS_out_file,
+      FLAGS_private_key,
+      FLAGS_chunk_size,
+      FLAGS_rootfs_partition_size,
+      is_delta ? &old_image_info : NULL,
+      &new_image_info,
+      &metadata_size)) {
     return 1;
   }
   return 0;
diff --git a/update_metadata.proto b/update_metadata.proto
index b9c061b..28ed0c6 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -90,6 +90,25 @@
   optional bytes hash = 2;
 }
 
+// Describe an image we are based on in a human friendly way.
+// Examples:
+//   dev-channel, x86-alex, 1.2.3, mp-v3
+//   nplusone-channel, x86-alex, 1.2.4, mp-v3, dev-channel, 1.2.3
+//
+// All fields will be set, if this message is present.
+message ImageInfo {
+  optional string board = 1;
+  optional string key = 2;
+  optional string channel = 3;
+  optional string version = 4;
+
+  // If these values aren't present, they should be assumed to match
+  // the equivalent value above. They are normally only different for
+  // special image types such as nplusone images.
+  optional string build_channel = 5;
+  optional string build_version = 6;
+}
+
 message DeltaArchiveManifest {
   message InstallOperation {
     enum Type {
@@ -144,4 +163,9 @@
   optional PartitionInfo new_kernel_info = 7;
   optional PartitionInfo old_rootfs_info = 8;
   optional PartitionInfo new_rootfs_info = 9;
+
+  // old_image_info will only be present for delta images.
+  optional ImageInfo old_image_info = 10;
+
+  optional ImageInfo new_image_info = 11;
 }