update_engine: Avoid emitting a MOVE to 0.

Workaround the bug where we can't emit a MOVE to the block 0 by excluding
it from the inplace generator graph and re-adding the operation later.

BUG=chromium:500423
TEST=delta_generator from lulu-R43-6946.31.0 to R45-7174.0.0, test image.

Change-Id: I3f16c2ae58b0f31869a39af6a45f7798960c722b
Reviewed-on: https://chromium-review.googlesource.com/278020
Tested-by: Gilad Arnold <garnold@chromium.org>
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Trybot-Ready: Filipe Brandenburger <filbranden@chromium.org>
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index 8676086..61ac4ad 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -297,10 +297,21 @@
     off_t chunk_blocks,
     int data_fd,
     off_t* data_file_size,
+    bool skip_block_0,
     bool src_ops_allowed) {
   ExtentRanges old_visited_blocks;
   ExtentRanges new_visited_blocks;
 
+  // We can't produce a MOVE operation with a 0 block as neither source nor
+  // destination, so we avoid generating an operation for the block 0 here, and
+  // we will add an operation for it in the InplaceGenerator. Excluding both
+  // old and new blocks ensures that identical images would still produce empty
+  // deltas.
+  if (skip_block_0) {
+    old_visited_blocks.AddBlock(0);
+    new_visited_blocks.AddBlock(0);
+  }
+
   map<string, vector<Extent>> old_files_map;
   if (old_fs) {
     vector<FilesystemInterface::File> old_files;
@@ -622,15 +633,16 @@
       block_size,
       new_kernel_size / block_size);
 
-  DeltaReadFilesystem(aops,
-                      old_kernel_part,
-                      new_kernel_part,
-                      old_kernel_fs.get(),
-                      new_kernel_fs.get(),
-                      -1,  // chunk_blocks
-                      blobs_fd,
-                      blobs_length,
-                      src_ops_allowed);
+  TEST_AND_RETURN_FALSE(DeltaReadFilesystem(aops,
+                                            old_kernel_part,
+                                            new_kernel_part,
+                                            old_kernel_fs.get(),
+                                            new_kernel_fs.get(),
+                                            -1,  // chunk_blocks
+                                            blobs_fd,
+                                            blobs_length,
+                                            false,  // skip_block_0
+                                            src_ops_allowed));
 
   LOG(INFO) << "Done delta compressing kernel partition.";
   return true;
@@ -782,6 +794,7 @@
                                             chunk_blocks,
                                             data_file_fd,
                                             data_file_size,
+                                            false,  // skip_block_0
                                             true));  // src_ops_allowed
   LOG(INFO) << "done reading normal files";
 
diff --git a/payload_generator/delta_diff_generator.h b/payload_generator/delta_diff_generator.h
index 53a2968..b3b5cee 100644
--- a/payload_generator/delta_diff_generator.h
+++ b/payload_generator/delta_diff_generator.h
@@ -69,6 +69,7 @@
                                   off_t chunk_blocks,
                                   int data_fd,
                                   off_t* data_file_size,
+                                  bool skip_block_0,
                                   bool src_ops_allowed);
 
   // For a given file |name| append operations to |aops| to produce it in the
diff --git a/payload_generator/inplace_generator.cc b/payload_generator/inplace_generator.cc
index 64c8226..9494d73 100644
--- a/payload_generator/inplace_generator.cc
+++ b/payload_generator/inplace_generator.cc
@@ -726,6 +726,7 @@
                                               chunk_blocks,
                                               data_file_fd,
                                               data_file_size,
+                                              true,  // skip_block_0
                                               false));  // src_ops_allowed
   // Convert the rootfs operations to the graph.
   Graph graph;
@@ -788,6 +789,19 @@
     rootfs_ops->back().name = vertex.file_name;
   }
 
+  // Re-add the operation for the block 0.
+  TEST_AND_RETURN_FALSE(DeltaDiffGenerator::DeltaReadFile(
+      rootfs_ops,
+      config.source.rootfs.path,
+      config.target.rootfs.path,
+      vector<Extent>{ExtentForRange(0, 1)},
+      vector<Extent>{ExtentForRange(0, 1)},
+      "<block-0>",  // operation name
+      -1,  // chunk_blocks
+      data_file_fd,
+      data_file_size,
+      false));  // src_ops_allowed
+
   return true;
 }