update_engine: Add fds for the source partitions.

Add new fds for the source partition, one for the rootfs and another for
the kernel. These are opened if we have a delta update with minor
version 2.

This change also adds support for changing the minor versions in tests.
There is a new private member, supported_minor_version_, which defaults
to kSupportedMinorPayloadVersion. It is set in the unit tests with calls
to SetSupportedVersion.

BUG=chromium:463573
TEST=`FEATURES=test emerge-link update_engine`

Change-Id: Ib988c91eb450b2499c615ae65b271691dfd9c651
Reviewed-on: https://chromium-review.googlesource.com/260950
Trybot-Ready: Allie Wood <alliewood@chromium.org>
Tested-by: Allie Wood <alliewood@chromium.org>
Reviewed-by: Alex Deymo <deymo@chromium.org>
Commit-Queue: Allie Wood <alliewood@chromium.org>
diff --git a/delta_performer.cc b/delta_performer.cc
index 28f61b8..825084d 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -57,6 +57,9 @@
 const unsigned DeltaPerformer::kProgressDownloadWeight = 50;
 const unsigned DeltaPerformer::kProgressOperationsWeight = 50;
 
+const uint32_t kInPlaceMinorPayloadVersion = 1;
+const uint32_t kSourceMinorPayloadVersion = 2;
+
 namespace {
 const int kUpdateStateOperationInvalid = -1;
 const int kMaxResumedUpdateFailures = 10;
@@ -272,6 +275,18 @@
   return static_cast<bool>(kernel_fd_);
 }
 
+bool DeltaPerformer::OpenSourceRootfs(const std::string& source_path) {
+  int err;
+  source_fd_ = OpenFile(source_path.c_str(), &err);
+  return static_cast<bool>(source_fd_);
+}
+
+bool DeltaPerformer::OpenSourceKernel(const std::string& source_kernel_path) {
+  int err;
+  source_kernel_fd_ = OpenFile(source_kernel_path.c_str(), &err);
+  return static_cast<bool>(source_kernel_fd_);
+}
+
 int DeltaPerformer::Close() {
   int err = 0;
   if (!kernel_fd_->Close()) {
@@ -282,8 +297,19 @@
     err = errno;
     PLOG(ERROR) << "Unable to close rootfs fd:";
   }
+  if (source_fd_ && !source_fd_->Close()) {
+    err = errno;
+    PLOG(ERROR) << "Unable to close source rootfs fd:";
+  }
+  if (source_kernel_fd_ && !source_kernel_fd_->Close()) {
+    err = errno;
+    PLOG(ERROR) << "Unable to close source kernel fd:";
+  }
   LOG_IF(ERROR, !hash_calculator_.Finalize()) << "Unable to finalize the hash.";
   fd_.reset();  // Set to invalid so that calls to Open() will fail.
+  kernel_fd_.reset();
+  source_fd_.reset();
+  source_kernel_fd_.reset();
   path_ = "";
   if (!buffer_.empty()) {
     LOG(INFO) << "Discarding " << buffer_.size() << " unused downloaded bytes";
@@ -333,6 +359,16 @@
   return metadata_size_;
 }
 
+uint32_t DeltaPerformer::GetMinorVersion() const {
+  if (manifest_.has_minor_version()) {
+    return manifest_.minor_version();
+  } else {
+    return (install_plan_->is_full_update ?
+            kFullPayloadMinorVersion :
+            kSupportedMinorPayloadVersion);
+  }
+}
+
 bool DeltaPerformer::GetManifest(DeltaArchiveManifest* out_manifest_p) const {
   if (!manifest_parsed_)
     return false;
@@ -501,6 +537,23 @@
       return false;
     }
 
+    // Open source fds if we have a delta payload with minor version 2.
+    if (!install_plan_->is_full_update &&
+        GetMinorVersion() == kSourceMinorPayloadVersion) {
+      if (!OpenSourceRootfs(install_plan_->source_path)) {
+        LOG(ERROR) << "Unable to open source rootfs partition file "
+                   << install_plan_->source_path;
+        Close();
+        return false;
+      }
+      if (!OpenSourceKernel(install_plan_->kernel_source_path)) {
+        LOG(ERROR) << "Unable to open source kernel partition file "
+                   << install_plan_->kernel_source_path;
+        Close();
+        return false;
+      }
+    }
+
     num_rootfs_operations_ = manifest_.install_operations_size();
     num_total_operations_ =
         num_rootfs_operations_ + manifest_.kernel_install_operations_size();
@@ -590,7 +643,7 @@
     const chromeos_update_engine::DeltaArchiveManifest_InstallOperation&
     operation) {
   // Move operations don't require any data blob, so they can always
-  // be performed
+  // be performed.
   if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE)
     return true;
 
@@ -982,11 +1035,11 @@
       return ErrorCode::kUnsupportedMinorPayloadVersion;
     }
   } else {
-    if (manifest_.minor_version() != kSupportedMinorPayloadVersion) {
+    if (manifest_.minor_version() != supported_minor_version_) {
       LOG(ERROR) << "Manifest contains minor version "
                  << manifest_.minor_version()
                  << " not the supported "
-                 << kSupportedMinorPayloadVersion;
+                 << supported_minor_version_;
       return ErrorCode::kUnsupportedMinorPayloadVersion;
     }
   }
diff --git a/delta_performer.h b/delta_performer.h
index eed24cb..fd234a2 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -24,6 +24,12 @@
 
 namespace chromeos_update_engine {
 
+// The minor version used by the in-place delta generator algorithm.
+extern const uint32_t kInPlaceMinorPayloadVersion;
+
+// The minor version used by the A to B delta generator algorithm.
+extern const uint32_t kSourceMinorPayloadVersion;
+
 class PrefsInterface;
 
 // This class performs the actions in a delta update synchronously. The delta
@@ -65,6 +71,8 @@
         install_plan_(install_plan),
         fd_(nullptr),
         kernel_fd_(nullptr),
+        source_fd_(nullptr),
+        source_kernel_fd_(nullptr),
         manifest_parsed_(false),
         manifest_valid_(false),
         metadata_size_(0),
@@ -79,12 +87,19 @@
         overall_progress_(0),
         last_progress_chunk_(0),
         forced_progress_log_wait_(
-            base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)) {}
+            base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)),
+        supported_minor_version_(kSupportedMinorPayloadVersion) {}
 
   // Opens the kernel. Should be called before or after Open(), but before
   // Write(). The kernel file will be close()d when Close() is called.
   bool OpenKernel(const char* kernel_path);
 
+  // Opens the source partition. The file will be closed when Close() is called.
+  bool OpenSourceRootfs(const std::string& kernel_path);
+
+  // Opens the source kernel. The file will be closed when Close() is called.
+  bool OpenSourceKernel(const std::string& source_kernel_path);
+
   // flags and mode ignored. Once Close()d, a DeltaPerformer can't be
   // Open()ed again.
   int Open(const char* path, int flags, mode_t mode) override;
@@ -186,6 +201,10 @@
   // Returns true on success.
   bool GetManifest(DeltaArchiveManifest* out_manifest_p) const;
 
+  // Returns the delta minor version. If this value is defined in the manifest,
+  // it returns that value, otherwise it returns the default value.
+  uint32_t GetMinorVersion() const;
+
  private:
   friend class DeltaPerformerTest;
   FRIEND_TEST(DeltaPerformerTest, IsIdempotentOperationTest);
@@ -300,9 +319,15 @@
   // File descriptor of open device.
   FileDescriptorPtr fd_;
 
-  // File descriptor of the kernel device
+  // File descriptor of the kernel device.
   FileDescriptorPtr kernel_fd_;
 
+  // File descriptor of the source device.
+  FileDescriptorPtr source_fd_;
+
+  // File descriptor of the source kernel device.
+  FileDescriptorPtr source_kernel_fd_;
+
   std::string path_;  // Path that fd_ refers to.
   std::string kernel_path_;  // Path that kernel_fd_ refers to.
 
@@ -359,6 +384,9 @@
   const base::TimeDelta forced_progress_log_wait_;
   base::Time forced_progress_log_time_;
 
+  // The delta minor payload version supported by DeltaPerformer.
+  uint32_t supported_minor_version_;
+
   DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
 };
 
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 5109fa0..fc2ac7e 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -107,8 +107,37 @@
   kValidOperationData,
 };
 
+// Chuck size used for full payloads during test.
+size_t kDefaultFullChunkSize = 1024 * 1024;
+
 }  // namespace
 
+class DeltaPerformerTest : public ::testing::Test {
+ public:
+  // Test helper placed where it can easily be friended from DeltaPerformer.
+  static void RunManifestValidation(const DeltaArchiveManifest& manifest,
+                                    bool full_payload,
+                                    ErrorCode expected) {
+    MockPrefs prefs;
+    InstallPlan install_plan;
+    FakeSystemState fake_system_state;
+    DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
+
+    // The install plan is for Full or Delta.
+    install_plan.is_full_update = full_payload;
+
+    // The Manifest we are validating.
+    performer.manifest_.CopyFrom(manifest);
+
+    EXPECT_EQ(expected, performer.ValidateManifest());
+  }
+
+  static void SetSupportedVersion(DeltaPerformer* performer,
+                                  uint64_t minor_version) {
+    performer->supported_minor_version_ = minor_version;
+  }
+};
+
 static void CompareFilesByBlock(const string& a_file, const string& b_file) {
   chromeos::Blob a_data, b_data;
   EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
@@ -281,7 +310,8 @@
                               bool noop,
                               off_t chunk_size,
                               SignatureTest signature_test,
-                              DeltaState *state) {
+                              DeltaState *state,
+                              uint32_t minor_version) {
   EXPECT_TRUE(utils::MakeTempFile("a_img.XXXXXX", &state->a_img, nullptr));
   EXPECT_TRUE(utils::MakeTempFile("b_img.XXXXXX", &state->b_img, nullptr));
   test_utils::CreateExtImageAtPath(state->a_img, nullptr);
@@ -474,6 +504,7 @@
     payload_config.is_delta = !full_rootfs;
     payload_config.chunk_size = chunk_size;
     payload_config.rootfs_partition_size = kRootFSPartitionSize;
+    payload_config.minor_version = minor_version;
     if (!full_rootfs) {
       payload_config.source.rootfs_part = state->a_img;
       payload_config.source.rootfs_mountpt = a_mnt;
@@ -482,10 +513,9 @@
       payload_config.source.image_info = old_image_info;
       EXPECT_TRUE(payload_config.source.LoadImageSize());
 
-      payload_config.minor_version =
-          DeltaPerformer::kSupportedMinorPayloadVersion;
     } else {
-      payload_config.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
+      if (payload_config.chunk_size == -1)
+        payload_config.chunk_size = kDefaultFullChunkSize;
     }
     payload_config.target.rootfs_part = state->b_img;
     payload_config.target.rootfs_mountpt = b_mnt;
@@ -538,7 +568,8 @@
                            SignatureTest signature_test, DeltaState* state,
                            bool hash_checks_mandatory,
                            OperationHashTest op_hash_test,
-                           DeltaPerformer** performer) {
+                           DeltaPerformer** performer,
+                           uint32_t minor_version) {
   // Check the metadata.
   {
     DeltaArchiveManifest manifest;
@@ -665,6 +696,8 @@
   install_plan.hash_checks_mandatory = hash_checks_mandatory;
   install_plan.metadata_size = state->metadata_size;
   install_plan.is_full_update = full_kernel && full_rootfs;
+  install_plan.source_path = state->a_img.c_str();
+  install_plan.kernel_source_path = state->old_kernel.c_str();
 
   LOG(INFO) << "Setting payload metadata size in Omaha  = "
             << state->metadata_size;
@@ -680,6 +713,7 @@
                                   &install_plan);
   EXPECT_TRUE(utils::FileExists(kUnittestPublicKeyPath));
   (*performer)->set_public_key_path(kUnittestPublicKeyPath);
+  DeltaPerformerTest::SetSupportedVersion(*performer, minor_version);
 
   EXPECT_EQ(state->image_size,
             OmahaHashCalculator::RawHashOfFile(state->a_img,
@@ -713,6 +747,12 @@
       break;
   }
 
+  // For now, source operations are not implemented, so we expect an error.
+  if (minor_version == kSourceMinorPayloadVersion) {
+    expected_error = ErrorCode::kDownloadOperationExecutionError;
+    continue_writing = false;
+  }
+
   // Write at some number of bytes per operation. Arbitrarily chose 5.
   const size_t kBytesPerWrite = 5;
   for (size_t i = 0; i < state->delta.size(); i += kBytesPerWrite) {
@@ -823,11 +863,11 @@
 void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
                       off_t chunk_size,
                       SignatureTest signature_test,
-                      bool hash_checks_mandatory) {
+                      bool hash_checks_mandatory, uint32_t minor_version) {
   DeltaState state;
   DeltaPerformer *performer = nullptr;
   GenerateDeltaFile(full_kernel, full_rootfs, noop, chunk_size,
-                    signature_test, &state);
+                    signature_test, &state, minor_version);
 
   ScopedPathUnlinker a_img_unlinker(state.a_img);
   ScopedPathUnlinker b_img_unlinker(state.b_img);
@@ -836,7 +876,7 @@
   ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
   ApplyDeltaFile(full_kernel, full_rootfs, noop, signature_test,
                  &state, hash_checks_mandatory, kValidOperationData,
-                 &performer);
+                 &performer, minor_version);
   VerifyPayload(performer, &state, signature_test);
   delete performer;
 }
@@ -889,7 +929,8 @@
   // Using kSignatureNone since it doesn't affect the results of our test.
   // If we've to use other signature options, then we'd have to get the
   // metadata size again after adding the signing operation to the manifest.
-  GenerateDeltaFile(true, true, false, -1, signature_test, &state);
+  GenerateDeltaFile(true, true, false, -1, signature_test, &state,
+                    DeltaPerformer::kFullPayloadMinorVersion);
 
   ScopedPathUnlinker a_img_unlinker(state.a_img);
   ScopedPathUnlinker b_img_unlinker(state.b_img);
@@ -972,40 +1013,21 @@
 void DoOperationHashMismatchTest(OperationHashTest op_hash_test,
                                  bool hash_checks_mandatory) {
   DeltaState state;
-  GenerateDeltaFile(true, true, false, -1, kSignatureGenerated, &state);
+  uint64_t minor_version = DeltaPerformer::kFullPayloadMinorVersion;
+  GenerateDeltaFile(true, true, false, -1, kSignatureGenerated, &state,
+                    minor_version);
   ScopedPathUnlinker a_img_unlinker(state.a_img);
   ScopedPathUnlinker b_img_unlinker(state.b_img);
   ScopedPathUnlinker delta_unlinker(state.delta_path);
   ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
   ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
   DeltaPerformer *performer = nullptr;
-  ApplyDeltaFile(true, true, false, kSignatureGenerated,
-                 &state, hash_checks_mandatory, op_hash_test, &performer);
+  ApplyDeltaFile(true, true, false, kSignatureGenerated, &state,
+                 hash_checks_mandatory, op_hash_test, &performer,
+                 minor_version);
   delete performer;
 }
 
-
-class DeltaPerformerTest : public ::testing::Test {
- public:
-  // Test helper placed where it can easily be friended from DeltaPerformer.
-  static void RunManifestValidation(const DeltaArchiveManifest& manifest,
-                                    bool full_payload,
-                                    ErrorCode expected) {
-    MockPrefs prefs;
-    InstallPlan install_plan;
-    FakeSystemState fake_system_state;
-    DeltaPerformer performer(&prefs, &fake_system_state, &install_plan);
-
-    // The install plan is for Full or Delta.
-    install_plan.is_full_update = full_payload;
-
-    // The Manifest we are validating.
-    performer.manifest_.CopyFrom(manifest);
-
-    EXPECT_EQ(expected, performer.ValidateManifest());
-  }
-};
-
 TEST(DeltaPerformerTest, ExtentsToByteStringTest) {
   uint64_t test[] = {1, 1, 4, 2, kSparseHole, 1, 0, 1};
   COMPILE_ASSERT(arraysize(test) % 2 == 0, array_size_uneven);
@@ -1110,68 +1132,69 @@
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageTest) {
   DoSmallImageTest(false, false, false, -1, kSignatureGenerator,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageSignaturePlaceholderTest) {
   DoSmallImageTest(false, false, false, -1, kSignatureGeneratedPlaceholder,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageSignaturePlaceholderMismatchTest) {
   DeltaState state;
   GenerateDeltaFile(false, false, false, -1,
-                    kSignatureGeneratedPlaceholderMismatch, &state);
+                    kSignatureGeneratedPlaceholderMismatch, &state,
+                    kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageChunksTest) {
   DoSmallImageTest(false, false, false, kBlockSize, kSignatureGenerator,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootFullKernelSmallImageTest) {
   DoSmallImageTest(true, false, false, -1, kSignatureGenerator,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootFullSmallImageTest) {
   DoSmallImageTest(true, true, false, -1, kSignatureGenerator,
-                   true);
+                   true, DeltaPerformer::kFullPayloadMinorVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootNoopSmallImageTest) {
   DoSmallImageTest(false, false, true, -1, kSignatureGenerator,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageSignNoneTest) {
   DoSmallImageTest(false, false, false, -1, kSignatureNone,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedTest) {
   DoSmallImageTest(false, false, false, -1, kSignatureGenerated,
-                   true);
+                   true, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellTest) {
   DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShell,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
   DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellBadKey,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
   DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl1,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
   DoSmallImageTest(false, false, false, -1, kSignatureGeneratedShellRotateCl2,
-                   false);
+                   false, kInPlaceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerTest, BadDeltaMagicTest) {
@@ -1354,6 +1377,24 @@
   EXPECT_TRUE(test_utils::RecursiveUnlinkDir(temp_dir));
 }
 
+TEST(DeltaPerformerTest, RunAsRootSourceOperationsTest) {
+  // Make sure we can generate a payload with the new source ops and minor
+  // version 2. For now, we expect ApplyDeltaFile to fail because the ops are
+  // not yet implemented, but eventually we can verify the resulting payload.
+  DeltaState state;
+  DeltaPerformer* performer = nullptr;
+  GenerateDeltaFile(false, false, false, -1, kSignatureNone, &state,
+                    kSourceMinorPayloadVersion);
+  ScopedPathUnlinker a_img_unlinker(state.a_img);
+  ScopedPathUnlinker b_img_unlinker(state.b_img);
+  ScopedPathUnlinker delta_unlinker(state.delta_path);
+  ScopedPathUnlinker old_kernel_unlinker(state.old_kernel);
+  ScopedPathUnlinker new_kernel_unlinker(state.new_kernel);
+  ApplyDeltaFile(false, false, false, kSignatureNone, &state, false,
+                 kValidOperationData, &performer, kSourceMinorPayloadVersion);
+  delete performer;
+}
+
 TEST(DeltaPerformerTest, MinorVersionsMatch) {
   // Test that the minor version in update_engine.conf that is installed to
   // the image matches the supported delta minor version in the update engine.
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index 4ecf377..1dc1beb 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -149,6 +149,8 @@
                            "",
                            output_temp_file.GetPath(),
                            "",
+                           "",
+                           "",
                            "");
   ObjectFeederAction<InstallPlan> feeder_action;
   feeder_action.set_obj(install_plan);
@@ -266,7 +268,7 @@
     // takes ownership of passed in HttpFetcher
     ObjectFeederAction<InstallPlan> feeder_action;
     InstallPlan install_plan(false, false, "", 0, "", 0, "",
-                             temp_file.GetPath(), "", "");
+                             temp_file.GetPath(), "", "", "", "");
     feeder_action.set_obj(install_plan);
     MockPrefs prefs;
     DownloadAction download_action(&prefs, nullptr,
@@ -376,6 +378,8 @@
                            "",
                            "/dev/null",
                            "/dev/null",
+                           "/dev/null",
+                           "/dev/null",
                            "");
   ObjectFeederAction<InstallPlan> feeder_action;
   feeder_action.set_obj(install_plan);
@@ -411,7 +415,8 @@
   DirectFileWriter writer;
 
   // takes ownership of passed in HttpFetcher
-  InstallPlan install_plan(false, false, "", 0, "", 0, "", path, "", "");
+  InstallPlan install_plan(
+      false, false, "", 0, "", 0, "", path, "", "", "", "");
   ObjectFeederAction<InstallPlan> feeder_action;
   feeder_action.set_obj(install_plan);
   MockPrefs prefs;
@@ -493,6 +498,8 @@
                              "",
                              output_temp_file.GetPath(),
                              "",
+                             "",
+                             "",
                              "");
     ObjectFeederAction<InstallPlan> feeder_action;
     feeder_action.set_obj(install_plan);
diff --git a/filesystem_copier_action_unittest.cc b/filesystem_copier_action_unittest.cc
index 51eb318..1aa1065 100644
--- a/filesystem_copier_action_unittest.cc
+++ b/filesystem_copier_action_unittest.cc
@@ -330,7 +330,7 @@
 
   ObjectFeederAction<InstallPlan> feeder_action;
   const char* kUrl = "http://some/url";
-  InstallPlan install_plan(false, true, kUrl, 0, "", 0, "", "", "", "");
+  InstallPlan install_plan(false, true, kUrl, 0, "", 0, "", "", "", "", "", "");
   feeder_action.set_obj(install_plan);
   FilesystemCopierAction copier_action(&fake_system_state_, false, false);
   ObjectCollectorAction<InstallPlan> collector_action;
@@ -364,6 +364,8 @@
                            "",
                            "/no/such/file",
                            "/no/such/file",
+                           "/no/such/file",
+                           "/no/such/file",
                            "");
   feeder_action.set_obj(install_plan);
   FilesystemCopierAction copier_action(&fake_system_state_, false, false);
diff --git a/install_plan.cc b/install_plan.cc
index d991a35..7b97876 100644
--- a/install_plan.cc
+++ b/install_plan.cc
@@ -21,6 +21,8 @@
                          const string& metadata_signature,
                          const string& install_path,
                          const string& kernel_install_path,
+                         const string& source_path,
+                         const string& kernel_source_path,
                          const string& public_key_rsa)
     : is_resume(is_resume),
       is_full_update(is_full_update),
@@ -31,6 +33,8 @@
       metadata_signature(metadata_signature),
       install_path(install_path),
       kernel_install_path(kernel_install_path),
+      source_path(source_path),
+      kernel_source_path(kernel_source_path),
       kernel_size(0),
       rootfs_size(0),
       hash_checks_mandatory(false),
@@ -56,7 +60,9 @@
           (metadata_size == that.metadata_size) &&
           (metadata_signature == that.metadata_signature) &&
           (install_path == that.install_path) &&
-          (kernel_install_path == that.kernel_install_path));
+          (kernel_install_path == that.kernel_install_path) &&
+          (source_path == that.source_path) &&
+          (kernel_source_path == that.kernel_source_path));
 }
 
 bool InstallPlan::operator!=(const InstallPlan& that) const {
@@ -74,6 +80,8 @@
             << ", metadata signature: " << metadata_signature
             << ", install_path: " << install_path
             << ", kernel_install_path: " << kernel_install_path
+            << ", source_path: " << source_path
+            << ", kernel_source_path: " << kernel_source_path
             << ", hash_checks_mandatory: " << utils::ToString(
                 hash_checks_mandatory)
             << ", powerwash_required: " << utils::ToString(
diff --git a/install_plan.h b/install_plan.h
index 782f2ab..1772a47 100644
--- a/install_plan.h
+++ b/install_plan.h
@@ -27,6 +27,8 @@
               const std::string& metadata_signature,
               const std::string& install_path,
               const std::string& kernel_install_path,
+              const std::string& source_path,
+              const std::string& kernel_source_path,
               const std::string& public_key_rsa);
 
   // Default constructor: Initialize all members which don't have a class
@@ -49,6 +51,8 @@
   std::string metadata_signature;        // signature of the  metadata
   std::string install_path;              // path to install device
   std::string kernel_install_path;       // path to kernel install device
+  std::string source_path;               // path to source device
+  std::string kernel_source_path;        // path to source kernel device
 
   // The fields below are used for kernel and rootfs verification. The flow is:
   //
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 1813abe..6527bff 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -85,9 +85,6 @@
 const char* const kEmptyPath = "";
 const char* const kBsdiffPath = "bsdiff";
 
-const uint32_t kInPlaceMinorPayloadVersion = 1;
-const uint32_t kSourceMinorPayloadVersion = 2;
-
 // Needed for testing purposes, in case we can't use actual filesystem objects.
 // TODO(garnold) (chromium:331965) Replace this hack with a properly injected
 // parameter in form of a mockable abstract class.
diff --git a/payload_generator/delta_diff_generator.h b/payload_generator/delta_diff_generator.h
index 3629afc..299b793 100644
--- a/payload_generator/delta_diff_generator.h
+++ b/payload_generator/delta_diff_generator.h
@@ -31,12 +31,6 @@
 extern const size_t kBlockSize;
 extern const size_t kRootFSPartitionSize;
 
-// The minor version used by the in-place delta generator algorithm.
-extern const uint32_t kInPlaceMinorPayloadVersion;
-
-// The minor version used by the A to B delta generator algorithm.
-extern const uint32_t kSourceMinorPayloadVersion;
-
 // The payload generation strategy prototype. This is the function that does
 // all the work to generate the operations for the rootfs and the kernel.
 // Given the |config|, generates the payload by populating the |graph| with
diff --git a/update_attempter.cc b/update_attempter.cc
index ae7f89b..139aaf2 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -720,6 +720,9 @@
 
   install_plan.kernel_install_path =
       utils::KernelDeviceOfBootDevice(install_plan.install_path);
+  install_plan.source_path = system_state_->hardware()->BootDevice();
+  install_plan.kernel_source_path =
+      utils::KernelDeviceOfBootDevice(install_plan.source_path);
   install_plan.powerwash_required = powerwash;
 
   LOG(INFO) << "Using this install plan:";