update_engine: Load minor_version from the FilesystemInterface.

In the normal use case, cros_generate_update_payload doesn't pass the
requested minor_version to delta_generator which means we need to detect
it from the old image. To detect it, we where looking at the
update_engine.conf file in the mounted source directory. This patch uses
the recently added FilesystemInterface::LoadSettings() method to read
the update_engine.conf settings from the source rootfs partition.

In order to make this information available when autodetecting the
minor_version and to help future FilesystemInterface mocking for
testing, we move the FilesystemInterface instance from inside the
OperationsGenerator class to the PartitionConfig, so the filesystems are
opened before calling GenerateUpdatePayloadFile.

This patch then deprecates the --old_dir flag removing the requirement
to run delta_generator and cros_generate_update_payload as root when
generating deltas.

BUG=chromium:305832
TEST=Ran cros_generate_update_payload to generate deltas from old (link
FSI) and new (ToT link) images.

Change-Id: I915679d62aae2d1a2acee68cc8c1ec691cc363ad
Reviewed-on: https://chromium-review.googlesource.com/283643
Trybot-Ready: Alex Deymo <deymo@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
Reviewed-by: Don Garrett <dgarrett@chromium.org>
Commit-Queue: Alex Deymo <deymo@chromium.org>
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index bc47b0b..5737422 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -532,7 +532,8 @@
         payload_config.source.kernel.path = state->old_kernel;
       payload_config.source.image_info = old_image_info;
       EXPECT_TRUE(payload_config.source.LoadImageSize());
-
+      EXPECT_TRUE(payload_config.source.rootfs.OpenFilesystem());
+      EXPECT_TRUE(payload_config.source.kernel.OpenFilesystem());
     } else {
       if (payload_config.chunk_size == -1)
         payload_config.chunk_size = kDefaultChunkSize;
@@ -541,6 +542,8 @@
     payload_config.target.kernel.path = state->new_kernel;
     payload_config.target.image_info = new_image_info;
     EXPECT_TRUE(payload_config.target.LoadImageSize());
+    EXPECT_TRUE(payload_config.target.rootfs.OpenFilesystem());
+    EXPECT_TRUE(payload_config.target.kernel.OpenFilesystem());
 
     EXPECT_TRUE(payload_config.Validate());
     EXPECT_TRUE(
@@ -1404,9 +1407,10 @@
   // 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.
   uint32_t minor_version;
-  base::FilePath conf_path("update_engine.conf");
-  EXPECT_TRUE(utils::GetMinorVersion(conf_path, &minor_version));
-  ASSERT_EQ(DeltaPerformer::kSupportedMinorPayloadVersion, minor_version);
+  chromeos::KeyValueStore store;
+  EXPECT_TRUE(store.Load(base::FilePath("update_engine.conf")));
+  EXPECT_TRUE(utils::GetMinorVersion(store, &minor_version));
+  EXPECT_EQ(DeltaPerformer::kSupportedMinorPayloadVersion, minor_version);
 }
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/ab_generator.cc b/payload_generator/ab_generator.cc
index 7a9c748..67163eb 100644
--- a/payload_generator/ab_generator.cc
+++ b/payload_generator/ab_generator.cc
@@ -13,7 +13,6 @@
 #include "update_engine/payload_generator/annotated_operation.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/delta_diff_utils.h"
-#include "update_engine/payload_generator/ext2_filesystem.h"
 #include "update_engine/utils.h"
 
 using std::string;
@@ -45,21 +44,15 @@
     off_t* data_file_size,
     vector<AnnotatedOperation>* rootfs_ops,
     vector<AnnotatedOperation>* kernel_ops) {
-  unique_ptr<Ext2Filesystem> old_fs = Ext2Filesystem::CreateFromFile(
-      config.source.rootfs.path);
-  unique_ptr<Ext2Filesystem> new_fs = Ext2Filesystem::CreateFromFile(
-      config.target.rootfs.path);
 
   off_t chunk_blocks = (config.chunk_size == -1 ? -1 :
                         config.chunk_size / config.block_size);
 
   rootfs_ops->clear();
-  TEST_AND_RETURN_FALSE(diff_utils::DeltaReadFilesystem(
+  TEST_AND_RETURN_FALSE(diff_utils::DeltaReadPartition(
       rootfs_ops,
-      config.source.rootfs.path,
-      config.target.rootfs.path,
-      old_fs.get(),
-      new_fs.get(),
+      config.source.rootfs,
+      config.target.rootfs,
       chunk_blocks,
       data_file_fd,
       data_file_size,
@@ -68,15 +61,14 @@
   LOG(INFO) << "done reading normal files";
 
   // Read kernel partition
-  TEST_AND_RETURN_FALSE(diff_utils::DeltaCompressKernelPartition(
-      config.source.kernel.path,
-      config.target.kernel.path,
-      config.source.kernel.size,
-      config.target.kernel.size,
-      config.block_size,
+  TEST_AND_RETURN_FALSE(diff_utils::DeltaReadPartition(
       kernel_ops,
+      config.source.kernel,
+      config.target.kernel,
+      chunk_blocks,
       data_file_fd,
       data_file_size,
+      false,  // skip_block_0
       true));  // src_ops_allowed
   LOG(INFO) << "done reading kernel";
 
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index bedfbbb..dbb63a8 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -58,6 +58,9 @@
   LOG(INFO) << "Block count: "
             << config.target.rootfs.size / config.block_size;
 
+  LOG_IF(INFO, config.source.kernel.path.empty())
+      << "Will generate full kernel update.";
+
   const string kTempFileTemplate("CrAU_temp_data.XXXXXX");
   string temp_file_path;
   off_t data_file_size = 0;
@@ -99,11 +102,10 @@
     }
   } else {
     // Full update.
-    LOG(INFO) << "Using generator FullUpdateGenerator::Run";
+    LOG(INFO) << "Using generator FullUpdateGenerator()";
     strategy.reset(new FullUpdateGenerator());
   }
 
-
   int data_file_fd;
   TEST_AND_RETURN_FALSE(
       utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &data_file_fd));
diff --git a/payload_generator/delta_diff_generator.h b/payload_generator/delta_diff_generator.h
index 8ba1dc0..405f451 100644
--- a/payload_generator/delta_diff_generator.h
+++ b/payload_generator/delta_diff_generator.h
@@ -9,13 +9,6 @@
 
 #include "update_engine/payload_generator/payload_generation_config.h"
 
-// There is one function in DeltaDiffGenerator of importance to users
-// of the class: GenerateDeltaUpdateFile(). Before calling it,
-// the old and new images must be mounted. Call GenerateDeltaUpdateFile()
-// with both the mount-points of the images in addition to the paths of
-// the images (both old and new). A delta from old to new will be
-// generated and stored in output_path.
-
 namespace chromeos_update_engine {
 
 extern const size_t kBlockSize;
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index d53c5e0..6c15919 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -16,7 +16,6 @@
 #include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/extent_utils.h"
-#include "update_engine/payload_generator/raw_filesystem.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/utils.h"
 
@@ -139,12 +138,10 @@
 
 namespace diff_utils {
 
-bool DeltaReadFilesystem(
+bool DeltaReadPartition(
     vector<AnnotatedOperation>* aops,
-    const string& old_part,
-    const string& new_part,
-    FilesystemInterface* old_fs,
-    FilesystemInterface* new_fs,
+    const PartitionConfig& old_part,
+    const PartitionConfig& new_part,
     off_t chunk_blocks,
     int data_fd,
     off_t* data_file_size,
@@ -164,15 +161,16 @@
   }
 
   map<string, vector<Extent>> old_files_map;
-  if (old_fs) {
+  if (old_part.fs_interface) {
     vector<FilesystemInterface::File> old_files;
-    old_fs->GetFiles(&old_files);
+    old_part.fs_interface->GetFiles(&old_files);
     for (const FilesystemInterface::File& file : old_files)
       old_files_map[file.name] = file.extents;
   }
 
+  TEST_AND_RETURN_FALSE(new_part.fs_interface);
   vector<FilesystemInterface::File> new_files;
-  new_fs->GetFiles(&new_files);
+  new_part.fs_interface->GetFiles(&new_files);
 
   // The processing is very straightforward here, we generate operations for
   // every file (and pseudo-file such as the metadata) in the new filesystem
@@ -210,8 +208,8 @@
 
     TEST_AND_RETURN_FALSE(DeltaReadFile(
         aops,
-        old_part,
-        new_part,
+        old_part.path,
+        new_part.path,
         old_file_extents,
         new_file_extents,
         new_file.name,  // operation name
@@ -222,14 +220,15 @@
   }
   // Process all the blocks not included in any file. We provided all the unused
   // blocks in the old partition as available data.
-  vector<Extent> new_unvisited = { ExtentForRange(0, new_fs->GetBlockCount()) };
+  vector<Extent> new_unvisited = {
+      ExtentForRange(0, new_part.size / kBlockSize)};
   new_unvisited = FilterExtentRanges(new_unvisited, new_visited_blocks);
   if (new_unvisited.empty())
     return true;
 
   vector<Extent> old_unvisited;
-  if (old_fs) {
-    old_unvisited.push_back(ExtentForRange(0, old_fs->GetBlockCount()));
+  if (old_part.fs_interface) {
+    old_unvisited.push_back(ExtentForRange(0, old_part.size / kBlockSize));
     old_unvisited = FilterExtentRanges(old_unvisited, old_visited_blocks);
   }
 
@@ -237,8 +236,8 @@
             << " unwritten blocks";
   TEST_AND_RETURN_FALSE(DeltaReadFile(
       aops,
-      old_part,
-      new_part,
+      old_part.path,
+      new_part.path,
       old_unvisited,
       new_unvisited,
       "<non-file-data>",  // operation name
@@ -460,44 +459,6 @@
   return true;
 }
 
-bool DeltaCompressKernelPartition(
-    const string& old_kernel_part,
-    const string& new_kernel_part,
-    uint64_t old_kernel_size,
-    uint64_t new_kernel_size,
-    uint64_t block_size,
-    vector<AnnotatedOperation>* aops,
-    int blobs_fd,
-    off_t* blobs_length,
-    bool src_ops_allowed) {
-  LOG(INFO) << "Delta compressing kernel partition...";
-  LOG_IF(INFO, old_kernel_part.empty()) << "Generating full kernel update...";
-
-  unique_ptr<RawFilesystem> old_kernel_fs;
-  if (!old_kernel_part.empty())
-    old_kernel_fs = RawFilesystem::Create("<kernel-delta-operation>",
-                                          block_size,
-                                          old_kernel_size / block_size);
-  unique_ptr<RawFilesystem> new_kernel_fs = RawFilesystem::Create(
-      "<kernel-delta-operation>",
-      block_size,
-      new_kernel_size / block_size);
-
-  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;
-}
-
 // Runs the bsdiff tool on two files and returns the resulting delta in
 // 'out'. Returns true on success.
 bool BsdiffFiles(const string& old_file,
diff --git a/payload_generator/delta_diff_utils.h b/payload_generator/delta_diff_utils.h
index aa18f16..0be8666 100644
--- a/payload_generator/delta_diff_utils.h
+++ b/payload_generator/delta_diff_utils.h
@@ -11,7 +11,6 @@
 #include <chromeos/secure_blob.h>
 
 #include "update_engine/payload_generator/annotated_operation.h"
-#include "update_engine/payload_generator/filesystem_interface.h"
 #include "update_engine/payload_generator/payload_generation_config.h"
 #include "update_engine/update_metadata.pb.h"
 
@@ -19,22 +18,20 @@
 
 namespace diff_utils {
 
-// Create operations in |aops| to produce all the files reported by |new_fs|,
-// including all the blocks not reported by any file.
-// It uses the files reported by |old_fs| and the data in |old_part| to
-// determine the best way to compress the new files (REPLACE, REPLACE_BZ,
-// COPY, BSDIFF) and writes any necessary data to the end of data_fd updating
-// data_file_size accordingly.
-bool DeltaReadFilesystem(std::vector<AnnotatedOperation>* aops,
-                         const std::string& old_part,
-                         const std::string& new_part,
-                         FilesystemInterface* old_fs,
-                         FilesystemInterface* new_fs,
-                         off_t chunk_blocks,
-                         int data_fd,
-                         off_t* data_file_size,
-                         bool skip_block_0,
-                         bool src_ops_allowed);
+// Create operations in |aops| to produce all the blocks in the |new_part|
+// partition using the filesystem opened in that PartitionConfig.
+// It uses the files reported by the filesystem in |old_part| and the data
+// blocks in that partition (if available) to determine the best way to compress
+// the new files (REPLACE, REPLACE_BZ, COPY, BSDIFF) and writes any necessary
+// data to the end of |data_fd| updating |data_file_size| accordingly.
+bool DeltaReadPartition(std::vector<AnnotatedOperation>* aops,
+                        const PartitionConfig& old_part,
+                        const PartitionConfig& new_part,
+                        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
 // |new_part|. The file will be split in chunks of |chunk_blocks| blocks each
@@ -73,25 +70,6 @@
                        DeltaArchiveManifest_InstallOperation* out_op,
                        bool src_ops_allowed);
 
-// Delta compresses a kernel partition |new_kernel_part| with knowledge of the
-// old kernel partition |old_kernel_part|. If |old_kernel_part| is an empty
-// string, generates a full update of the partition. The size of the old and
-// new kernel is passed in |old_kernel_size| and |new_kernel_size|. The
-// operations used to generate the new kernel are stored in the |aops|
-// vector, and the blob associated to those operations is written at the end
-// of the |blobs_fd| file, adding to the value pointed by |blobs_length| the
-// bytes written to |blobs_fd|.
-bool DeltaCompressKernelPartition(
-    const std::string& old_kernel_part,
-    const std::string& new_kernel_part,
-    uint64_t old_kernel_size,
-    uint64_t new_kernel_size,
-    uint64_t block_size,
-    std::vector<AnnotatedOperation>* aops,
-    int blobs_fd,
-    off_t* blobs_length,
-    bool src_ops_allowed);
-
 // Runs the bsdiff tool on two files and returns the resulting delta in
 // |out|. Returns true on success.
 bool BsdiffFiles(const std::string& old_file,
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
index bc75393..c28f7e8 100644
--- a/payload_generator/full_update_generator.cc
+++ b/payload_generator/full_update_generator.cc
@@ -135,17 +135,19 @@
   size_t max_threads = std::max(sysconf(_SC_NPROCESSORS_ONLN), 4L);
   LOG(INFO) << "Max threads: " << max_threads;
 
-  PartitionConfig partitions[] = { config.target.rootfs, config.target.kernel };
+  const PartitionConfig* partitions[] = {
+      &config.target.rootfs,
+      &config.target.kernel};
 
   for (int part_id = 0; part_id < 2; ++part_id) {
-    const PartitionConfig& partition = partitions[part_id];
-    LOG(INFO) << "compressing " << partition.path;
-    int in_fd = open(partition.path.c_str(), O_RDONLY, 0);
+    const PartitionConfig* partition = partitions[part_id];
+    LOG(INFO) << "compressing " << partition->path;
+    int in_fd = open(partition->path.c_str(), O_RDONLY, 0);
     TEST_AND_RETURN_FALSE(in_fd >= 0);
     ScopedFdCloser in_fd_closer(&in_fd);
     deque<shared_ptr<ChunkProcessor>> threads;
     int last_progress_update = INT_MIN;
-    size_t bytes_left = partition.size, counter = 0, offset = 0;
+    size_t bytes_left = partition->size, counter = 0, offset = 0;
     while (bytes_left > 0 || !threads.empty()) {
       // Check and start new chunk processors if possible.
       while (threads.size() < max_threads && bytes_left > 0) {
@@ -195,7 +197,7 @@
 
       int progress = static_cast<int>(
           (processor->offset() + processor->buffer_in().size()) * 100.0 /
-          partition.size);
+          partition->size);
       if (last_progress_update < progress &&
           (last_progress_update + 10 <= progress || progress == 100)) {
         LOG(INFO) << progress << "% complete (output size: "
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 662c1f4..170a612 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -218,7 +218,8 @@
 
 int Main(int argc, char** argv) {
   DEFINE_string(old_dir, "",
-                "Directory where the old rootfs is loop mounted read-only");
+                "[DEPRECATED] Directory where the old rootfs is loop mounted "
+                "read-only. Not required anymore.");
   DEFINE_string(new_dir, "",
                 "[DEPRECATED] Directory where the new rootfs is loop mounted "
                 "read-only. Not required anymore.");
@@ -255,8 +256,9 @@
   DEFINE_uint64(rootfs_partition_size,
                chromeos_update_engine::kRootFSPartitionSize,
                "RootFS partition size for the image once installed");
-  DEFINE_int32(minor_version, DeltaPerformer::kFullPayloadMinorVersion,
-               "The minor version of the payload being generated");
+  DEFINE_int32(minor_version, -1,
+               "The minor version of the payload being generated "
+               "(-1 means autodetect).");
 
   DEFINE_string(old_channel, "",
                 "The channel for the old image. 'dev-channel', 'npo-channel', "
@@ -311,6 +313,9 @@
   logging::InitLogging(log_settings);
 
   // Check flags.
+  if (!FLAGS_old_dir.empty()) {
+    LOG(INFO) << "--old_dir flag is deprecated and ignored.";
+  }
   if (!FLAGS_new_dir.empty()) {
     LOG(INFO) << "--new_dir flag is deprecated and ignored.";
   }
@@ -389,21 +394,6 @@
                  &payload_config.source.image_info);
 
   payload_config.rootfs_partition_size = FLAGS_rootfs_partition_size;
-  payload_config.minor_version = FLAGS_minor_version;
-  // Look for the minor version in the old image if it was not given as an
-  // argument.
-  if (payload_config.is_delta &&
-      !base::CommandLine::ForCurrentProcess()->HasSwitch("minor_version")) {
-    uint32_t minor_version;
-    base::FilePath image_path(FLAGS_old_dir);
-    base::FilePath conf_loc("etc/update_engine.conf");
-    base::FilePath conf_path = image_path.Append(conf_loc);
-    if (utils::GetMinorVersion(conf_path, &minor_version)) {
-      payload_config.minor_version = minor_version;
-    } else {
-      payload_config.minor_version = kInPlaceMinorPayloadVersion;
-    }
-  }
 
   // Load the rootfs size from verity's kernel command line if rootfs
   // verification is enabled.
@@ -411,6 +401,36 @@
   payload_config.target.LoadVerityRootfsSize();
 
   if (payload_config.is_delta) {
+    // Avoid opening the filesystem interface for full payloads.
+    CHECK(payload_config.target.rootfs.OpenFilesystem());
+    CHECK(payload_config.target.kernel.OpenFilesystem());
+    CHECK(payload_config.source.rootfs.OpenFilesystem());
+    CHECK(payload_config.source.kernel.OpenFilesystem());
+  }
+
+  if (FLAGS_minor_version == -1) {
+    // Autodetect minor_version by looking at the update_engine.conf in the old
+    // image.
+    if (payload_config.is_delta) {
+      CHECK(payload_config.source.rootfs.fs_interface);
+      chromeos::KeyValueStore store;
+      uint32_t minor_version;
+      if (payload_config.source.rootfs.fs_interface->LoadSettings(&store) &&
+          utils::GetMinorVersion(store, &minor_version)) {
+        payload_config.minor_version = minor_version;
+      } else {
+        payload_config.minor_version = kInPlaceMinorPayloadVersion;
+      }
+    } else {
+      payload_config.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
+    }
+    LOG(INFO) << "Auto-detected minor_version=" << payload_config.minor_version;
+  } else {
+    payload_config.minor_version = FLAGS_minor_version;
+    LOG(INFO) << "Using provided minor_version=" << FLAGS_minor_version;
+  }
+
+  if (payload_config.is_delta) {
     LOG(INFO) << "Generating delta update";
   } else {
     LOG(INFO) << "Generating full update";
diff --git a/payload_generator/inplace_generator.cc b/payload_generator/inplace_generator.cc
index 2ada2f3..2e0dbaf 100644
--- a/payload_generator/inplace_generator.cc
+++ b/payload_generator/inplace_generator.cc
@@ -15,7 +15,6 @@
 #include "update_engine/payload_generator/cycle_breaker.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/delta_diff_utils.h"
-#include "update_engine/payload_generator/ext2_filesystem.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/graph_types.h"
 #include "update_engine/payload_generator/graph_utils.h"
@@ -712,27 +711,20 @@
     off_t* data_file_size,
     vector<AnnotatedOperation>* rootfs_ops,
     vector<AnnotatedOperation>* kernel_ops) {
-  unique_ptr<Ext2Filesystem> old_fs = Ext2Filesystem::CreateFromFile(
-      config.source.rootfs.path);
-  unique_ptr<Ext2Filesystem> new_fs = Ext2Filesystem::CreateFromFile(
-      config.target.rootfs.path);
-
   off_t chunk_blocks = (config.chunk_size == -1 ? -1 :
                         config.chunk_size / config.block_size);
 
   // Temporary list of operations used to construct the dependency graph.
   vector<AnnotatedOperation> aops;
   TEST_AND_RETURN_FALSE(
-      diff_utils::DeltaReadFilesystem(&aops,
-                                      config.source.rootfs.path,
-                                      config.target.rootfs.path,
-                                      old_fs.get(),
-                                      new_fs.get(),
-                                      chunk_blocks,
-                                      data_file_fd,
-                                      data_file_size,
-                                      true,  // skip_block_0
-                                      false));  // src_ops_allowed
+      diff_utils::DeltaReadPartition(&aops,
+                                     config.source.rootfs,
+                                     config.target.rootfs,
+                                     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;
   CheckGraph(graph);
@@ -756,16 +748,18 @@
   }
 
   // Read kernel partition
-  TEST_AND_RETURN_FALSE(diff_utils::DeltaCompressKernelPartition(
-      config.source.kernel.path,
-      config.target.kernel.path,
-      config.source.kernel.size,
-      config.target.kernel.size,
-      config.block_size,
-      kernel_ops,
-      data_file_fd,
-      data_file_size,
-      false));  // src_ops_allowed
+  LOG(INFO) << "Delta compressing kernel partition...";
+  // It is safe to not skip the block 0 since we will not be using the cycle
+  // breaking algorithm on this list of operations as we expect no cycles here.
+  TEST_AND_RETURN_FALSE(
+      diff_utils::DeltaReadPartition(kernel_ops,
+                                     config.source.kernel,
+                                     config.target.kernel,
+                                     chunk_blocks,
+                                     data_file_fd,
+                                     data_file_size,
+                                     false,  // skip_block_0
+                                     false));  // src_ops_allowed
   LOG(INFO) << "done reading kernel";
   CheckGraph(graph);
 
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index 3af9b4f..d58c9a1 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -8,6 +8,8 @@
 
 #include "update_engine/delta_performer.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
+#include "update_engine/payload_generator/ext2_filesystem.h"
+#include "update_engine/payload_generator/raw_filesystem.h"
 #include "update_engine/payload_generator/verity_utils.h"
 #include "update_engine/utils.h"
 
@@ -23,6 +25,34 @@
   return true;
 }
 
+bool PartitionConfig::OpenFilesystem() {
+  if (path.empty())
+    return true;
+  fs_interface.reset();
+  if (name == PartitionName::kRootfs) {
+    fs_interface = Ext2Filesystem::CreateFromFile(path);
+  }
+
+  if (!fs_interface) {
+    // Fall back to a RAW filesystem.
+    TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
+    std::string str_name = "other";
+    switch (name) {
+      case PartitionName::kKernel:
+        str_name = "kernel";
+        break;
+      case PartitionName::kRootfs:
+        str_name = "rootfs";
+        break;
+    }
+    fs_interface = RawFilesystem::Create(
+      "<" + str_name + "-partition>",
+      kBlockSize,
+      size / kBlockSize);
+  }
+  return true;
+}
+
 bool ImageConfig::ValidateIsEmpty() const {
   TEST_AND_RETURN_FALSE(ImageInfoIsEmpty());
 
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index 0f002e1..f18055f 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -7,9 +7,11 @@
 
 #include <cstddef>
 
+#include <memory>
 #include <string>
 #include <vector>
 
+#include "update_engine/payload_generator/filesystem_interface.h"
 #include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
@@ -27,6 +29,10 @@
   // fields are set correctly to a valid image file.
   bool ValidateExists() const;
 
+  // Open then filesystem stored in this partition and stores it in
+  // |fs_interface|. Returns whether opening the filesystem worked.
+  bool OpenFilesystem();
+
   // The path to the partition file. This can be a regular file or a block
   // device such as a loop device.
   std::string path;
@@ -39,6 +45,10 @@
   // target image.
   uint64_t size = 0;
 
+  // The FilesystemInterface implementation used to access this partition's
+  // files.
+  std::unique_ptr<FilesystemInterface> fs_interface;
+
   PartitionName name;
 };
 
diff --git a/utils.cc b/utils.cc
index f36cbec..97ce712 100644
--- a/utils.cc
+++ b/utils.cc
@@ -38,7 +38,6 @@
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <chromeos/data_encoding.h>
-#include <chromeos/key_value_store.h>
 #include <chromeos/message_loops/message_loop.h>
 
 #include "update_engine/clock_interface.h"
@@ -1628,11 +1627,10 @@
   return ret;
 }
 
-bool GetMinorVersion(base::FilePath path, uint32_t* minor_version) {
-  chromeos::KeyValueStore store;
+bool GetMinorVersion(const chromeos::KeyValueStore& store,
+                     uint32_t* minor_version) {
   string result;
-  if (base::PathExists(path) && store.Load(path) &&
-      store.GetString("PAYLOAD_MINOR_VERSION", &result)) {
+  if (store.GetString("PAYLOAD_MINOR_VERSION", &result)) {
     if (!base::StringToUint(result, minor_version)) {
       LOG(ERROR) << "StringToUint failed when parsing delta minor version.";
       return false;
diff --git a/utils.h b/utils.h
index 6a9e065..b661b43 100644
--- a/utils.h
+++ b/utils.h
@@ -19,6 +19,7 @@
 #include <base/posix/eintr_wrapper.h>
 #include <base/time/time.h>
 #include <chromeos/secure_blob.h>
+#include <chromeos/key_value_store.h>
 #include "metrics/metrics_library.h"
 
 #include "update_engine/action.h"
@@ -424,13 +425,10 @@
                              int64_t* storage,
                              base::TimeDelta* out_duration);
 
-// This function looks for a configuration file at |path|. If it finds that
-// file, it will try get the PAYLOAD_MINOR_VERSION value from it and set
-// |minor_version| to that value.
-//
-// The function will return |true| if it succeeds at finding the file and
-// value and setting it, and |false| otherwise.
-bool GetMinorVersion(base::FilePath path, uint32_t* minor_version);
+// Look for the minor version value in the passed |store| and set
+// |minor_version| to that value. Return whether the value was found and valid.
+bool GetMinorVersion(const chromeos::KeyValueStore& store,
+                     uint32_t* minor_version);
 
 // This function reads the specified data in |extents| into |out_data|. The
 // extents are read from the file at |path|. |out_data_size| is the size of
diff --git a/utils_unittest.cc b/utils_unittest.cc
index 0d82f61..4787e84 100644
--- a/utils_unittest.cc
+++ b/utils_unittest.cc
@@ -732,18 +732,17 @@
 TEST(UtilsTest, GetMinorVersion) {
   // Test GetMinorVersion by verifying that it parses the conf file and returns
   // the correct value.
-  string contents = "PAYLOAD_MINOR_VERSION=1\n";
   uint32_t minor_version;
 
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  chromeos::KeyValueStore store;
+  EXPECT_FALSE(utils::GetMinorVersion(store, &minor_version));
 
-  base::FilePath temp_file("update_engine.conf");
-  base::FilePath filepath = temp_dir.path().Append(temp_file);
+  EXPECT_TRUE(store.LoadFromString("PAYLOAD_MINOR_VERSION=one-two-three\n"));
+  EXPECT_FALSE(utils::GetMinorVersion(store, &minor_version));
 
-  ASSERT_TRUE(test_utils::WriteFileString(filepath.value(), contents.c_str()));
-  ASSERT_TRUE(utils::GetMinorVersion(filepath, &minor_version));
-  ASSERT_EQ(minor_version, 1);
+  EXPECT_TRUE(store.LoadFromString("PAYLOAD_MINOR_VERSION=123\n"));
+  EXPECT_TRUE(utils::GetMinorVersion(store, &minor_version));
+  EXPECT_EQ(minor_version, 123);
 }
 
 static bool BoolMacroTestHelper() {