Implement ZERO and DISCARD operations in update_engine.

ZERO and DISCARD operations are implemented using the specific ioctl()
that allow SSD to do a faster operation when writing zeros. In case of
failure, such as when they are not supported, the operation will fall
back to write the zeros directly.

Bug: 23180264
Test: Added a unittest.

Change-Id: I0f12ab9864620b50250157c1de17b7fc58c7ea1c
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index d2ec5b0..12dd1d3 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -138,14 +138,29 @@
   // Apply |payload_data| on partition specified in |source_path|.
   chromeos::Blob ApplyPayload(const chromeos::Blob& payload_data,
                               const string& source_path) {
-    install_plan_.source_path = source_path;
-    install_plan_.kernel_source_path = "/dev/null";
+    return ApplyPayloadToData(payload_data, source_path, chromeos::Blob());
+  }
 
+  // Apply the payload provided in |payload_data| reading from the |source_path|
+  // file and writing the contents to a new partition. The existing data in the
+  // new target file are set to |target_data| before applying the payload.
+  // Returns the result of the payload application.
+  chromeos::Blob ApplyPayloadToData(const chromeos::Blob& payload_data,
+                                    const string& source_path,
+                                    const chromeos::Blob& target_data) {
     string new_part;
     EXPECT_TRUE(utils::MakeTempFile("Partition-XXXXXX", &new_part, nullptr));
     ScopedPathUnlinker partition_unlinker(new_part);
+    EXPECT_TRUE(utils::WriteFile(new_part.c_str(), target_data.data(),
+                                 target_data.size()));
+
+    install_plan_.source_path = source_path;
+    install_plan_.kernel_source_path = "/dev/null";
+    install_plan_.install_path = new_part;
+    install_plan_.kernel_install_path = "/dev/null";
 
     EXPECT_EQ(0, performer_.Open(new_part.c_str(), 0, 0));
+    EXPECT_TRUE(performer_.OpenSourceRootfs(source_path.c_str()));
     EXPECT_TRUE(performer_.Write(payload_data.data(), payload_data.size()));
     EXPECT_EQ(0, performer_.Close());
 
@@ -285,7 +300,7 @@
   chromeos::Blob payload_data = GeneratePayload(expected_data, aops, false,
       kFullPayloadMinorVersion);
 
-  EXPECT_EQ(expected_data, ApplyPayload(payload_data, ""));
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
 }
 
 TEST_F(DeltaPerformerTest, ReplaceOperationTest) {
@@ -348,6 +363,29 @@
   EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
 }
 
+TEST_F(DeltaPerformerTest, ZeroOperationTest) {
+  chromeos::Blob existing_data = chromeos::Blob(4096 * 10, 'a');
+  chromeos::Blob expected_data = existing_data;
+  // Blocks 4, 5 and 7 should have zeros instead of 'a' after the operation is
+  // applied.
+  std::fill(expected_data.data() + 4096 * 4, expected_data.data() + 4096 * 6,
+            0);
+  std::fill(expected_data.data() + 4096 * 7, expected_data.data() + 4096 * 8,
+            0);
+
+  AnnotatedOperation aop;
+  *(aop.op.add_dst_extents()) = ExtentForRange(4, 2);
+  *(aop.op.add_dst_extents()) = ExtentForRange(7, 1);
+  aop.op.set_type(InstallOperation::ZERO);
+  vector<AnnotatedOperation> aops = {aop};
+
+  chromeos::Blob payload_data = GeneratePayload(chromeos::Blob(), aops, false,
+                                                kSourceMinorPayloadVersion);
+
+  EXPECT_EQ(expected_data,
+            ApplyPayloadToData(payload_data, "/dev/null", existing_data));
+}
+
 TEST_F(DeltaPerformerTest, SourceCopyOperationTest) {
   chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
                                                 std::end(kRandomString));