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;
     }
   }