AU: Handle moving of sparse block extents correctly in DeltaPerformer.

Rather than reading from sparse holes, just reset the buffer with zeros. Drop
writes to sparse holes (and DCHECK that the data to be written is all zeros).

This patch also adds the following tests:

- Move a file of zeros into a sparse file.
- Move a sparse file into a file of zeros.
- Move a partially sparse file.

BUG=chromium:196378
TEST=unit tests

Change-Id: Idab8d44b391fbe7714c7fd53f7f383c9bf81edc1
Reviewed-on: https://gerrit.chromium.org/gerrit/49172
Commit-Queue: Darin Petkov <petkov@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/delta_performer.cc b/delta_performer.cc
index 0a2a943..ae1f7e5 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -580,14 +580,19 @@
   for (int i = 0; i < operation.src_extents_size(); i++) {
     ssize_t bytes_read_this_iteration = 0;
     const Extent& extent = operation.src_extents(i);
-    TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
-                                          &buf[bytes_read],
-                                          extent.num_blocks() * block_size_,
-                                          extent.start_block() * block_size_,
-                                          &bytes_read_this_iteration));
+    const size_t bytes = extent.num_blocks() * block_size_;
+    if (extent.start_block() == kSparseHole) {
+      bytes_read_this_iteration = bytes;
+      memset(&buf[bytes_read], 0, bytes);
+    } else {
+      TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
+                                            &buf[bytes_read],
+                                            bytes,
+                                            extent.start_block() * block_size_,
+                                            &bytes_read_this_iteration));
+    }
     TEST_AND_RETURN_FALSE(
-        bytes_read_this_iteration ==
-        static_cast<ssize_t>(extent.num_blocks() * block_size_));
+        bytes_read_this_iteration == static_cast<ssize_t>(bytes));
     bytes_read += bytes_read_this_iteration;
   }
 
@@ -603,11 +608,19 @@
   ssize_t bytes_written = 0;
   for (int i = 0; i < operation.dst_extents_size(); i++) {
     const Extent& extent = operation.dst_extents(i);
-    TEST_AND_RETURN_FALSE(utils::PWriteAll(fd,
-                                           &buf[bytes_written],
-                                           extent.num_blocks() * block_size_,
-                                           extent.start_block() * block_size_));
-    bytes_written += extent.num_blocks() * block_size_;
+    const size_t bytes = extent.num_blocks() * block_size_;
+    if (extent.start_block() == kSparseHole) {
+      DCHECK_EQ(&buf[bytes_written],
+                std::search_n(&buf[bytes_written], &buf[bytes_written + bytes],
+                              bytes, 0));
+    } else {
+      TEST_AND_RETURN_FALSE(
+          utils::PWriteAll(fd,
+                           &buf[bytes_written],
+                           bytes,
+                           extent.start_block() * block_size_));
+    }
+    bytes_written += bytes;
   }
   DCHECK_EQ(bytes_written, bytes_read);
   DCHECK_EQ(bytes_written, static_cast<ssize_t>(buf.size()));
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 0c2c588..83f3863 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -285,6 +285,20 @@
                                  &hardtocompress[0],
                                  hardtocompress.size()));
 
+    vector<char> zeros(16 * 1024, 0);
+    EXPECT_EQ(zeros.size(),
+              file_util::WriteFile(
+                  FilePath(StringPrintf("%s/move-to-sparse", a_mnt.c_str())),
+                  &zeros[0], zeros.size()));
+
+    EXPECT_TRUE(
+        WriteSparseFile(StringPrintf("%s/move-from-sparse", a_mnt.c_str()),
+                        16 * 1024));
+
+    EXPECT_EQ(0, system(StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse "
+                                     "bs=1 seek=4096 count=1",
+                                     a_mnt.c_str()).c_str()));
+
     // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
     // patch fails to zero out the final block.
     vector<char> ones(1024 * 1024, 0xff);
@@ -320,7 +334,22 @@
                                      b_mnt.c_str()).c_str()));
     EXPECT_TRUE(WriteSparseFile(StringPrintf("%s/fullsparse", b_mnt.c_str()),
                                 1024 * 1024));
-    EXPECT_EQ(0, system(StringPrintf("dd if=/dev/zero of=%s/partsparese bs=1 "
+
+    EXPECT_TRUE(
+        WriteSparseFile(StringPrintf("%s/move-to-sparse", b_mnt.c_str()),
+                        16 * 1024));
+
+    vector<char> zeros(16 * 1024, 0);
+    EXPECT_EQ(zeros.size(),
+              file_util::WriteFile(
+                  FilePath(StringPrintf("%s/move-from-sparse", b_mnt.c_str())),
+                  &zeros[0], zeros.size()));
+
+    EXPECT_EQ(0, system(StringPrintf("dd if=/dev/zero of=%s/move-semi-sparse "
+                                     "bs=1 seek=4096 count=1",
+                                     b_mnt.c_str()).c_str()));
+
+    EXPECT_EQ(0, system(StringPrintf("dd if=/dev/zero of=%s/partsparse bs=1 "
                                      "seek=4096 count=1",
                                      b_mnt.c_str()).c_str()));
     EXPECT_EQ(0, system(StringPrintf("cp %s/srchardlink0 %s/tmp && "