Merge remote-tracking branch 'aosp/upstream-master' into merge

It's a merge from chrome OS with some reverts.
1. the fd watcher change, because the libbrillo version isn't
compatible in aosp.
commit 6955bcc4ffe4cc9d62a88186b9a7e75d095a7897
commit 493fecb3f48c8478fd3ef244d631d857730dd14d
2. two libcurl unittest. Because the RunOnce() of the fake message
loop seems to have different behavior in aosp.
commit d3d84218cafbc1a95e7d6bbb775b495d1bebf4d2

Put preprocessor guards to use the old code in aosp. And we can
switch to the new code in the other path after adopting the new
libbrillo & libchrome.

Test: unit tests pass, apply an OTA
Change-Id: Id613599834b0f44f92841dbeae6303601db5490d
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index d9b739d..af1baa4 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -57,9 +57,6 @@
 #endif  // USE_FEC
 #include "update_engine/payload_consumer/file_descriptor_utils.h"
 #include "update_engine/payload_consumer/mount_history.h"
-#if USE_MTD
-#include "update_engine/payload_consumer/mtd_file_descriptor.h"
-#endif  // USE_MTD
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_consumer/payload_verifier.h"
 #include "update_engine/payload_consumer/xz_extent_writer.h"
@@ -79,40 +76,9 @@
 namespace {
 const int kUpdateStateOperationInvalid = -1;
 const int kMaxResumedUpdateFailures = 10;
-#if USE_MTD
-const int kUbiVolumeAttachTimeout = 5 * 60;
-#endif
 
 const uint64_t kCacheSize = 1024 * 1024;  // 1MB
 
-FileDescriptorPtr CreateFileDescriptor(const char* path) {
-  FileDescriptorPtr ret;
-#if USE_MTD
-  if (strstr(path, "/dev/ubi") == path) {
-    if (!UbiFileDescriptor::IsUbi(path)) {
-      // The volume might not have been attached at boot time.
-      int volume_no;
-      if (utils::SplitPartitionName(path, nullptr, &volume_no)) {
-        utils::TryAttachingUbiVolume(volume_no, kUbiVolumeAttachTimeout);
-      }
-    }
-    if (UbiFileDescriptor::IsUbi(path)) {
-      LOG(INFO) << path << " is a UBI device.";
-      ret.reset(new UbiFileDescriptor);
-    }
-  } else if (MtdFileDescriptor::IsMtd(path)) {
-    LOG(INFO) << path << " is an MTD device.";
-    ret.reset(new MtdFileDescriptor);
-  } else {
-    LOG(INFO) << path << " is not an MTD nor a UBI device.";
-#endif
-    ret.reset(new EintrSafeFileDescriptor);
-#if USE_MTD
-  }
-#endif
-  return ret;
-}
-
 // Opens path for read/write. On success returns an open FileDescriptor
 // and sets *err to 0. On failure, sets *err to errno and returns nullptr.
 FileDescriptorPtr OpenFile(const char* path,
@@ -124,18 +90,11 @@
   bool read_only = (mode & O_ACCMODE) == O_RDONLY;
   utils::SetBlockDeviceReadOnly(path, read_only);
 
-  FileDescriptorPtr fd = CreateFileDescriptor(path);
+  FileDescriptorPtr fd(new EintrSafeFileDescriptor());
   if (cache_writes && !read_only) {
     fd = FileDescriptorPtr(new CachedFileDescriptor(fd, kCacheSize));
     LOG(INFO) << "Caching writes.";
   }
-#if USE_MTD
-  // On NAND devices, we can either read, or write, but not both. So here we
-  // use O_WRONLY.
-  if (UbiFileDescriptor::IsUbi(path) || MtdFileDescriptor::IsMtd(path)) {
-    mode = O_WRONLY;
-  }
-#endif
   if (!fd->Open(path, mode, 000)) {
     *err = errno;
     PLOG(ERROR) << "Unable to open file " << path;
@@ -359,11 +318,10 @@
       install_plan_->partitions.size() - partitions_.size();
   const InstallPlan::Partition& install_part =
       install_plan_->partitions[num_previous_partitions + current_partition_];
-  // Open source fds if we have a delta payload with minor version >= 2, or for
-  // partitions in the partial update.
+  // Open source fds if we have a delta payload, or for partitions in the
+  // partial update.
   bool source_may_exist = manifest_.partial_update() ||
-                          (payload_->type == InstallPayloadType::kDelta &&
-                           GetMinorVersion() != kInPlaceMinorPayloadVersion);
+                          payload_->type == InstallPayloadType::kDelta;
   // We shouldn't open the source partition in certain cases, e.g. some dynamic
   // partitions in delta payload, partitions included in the full payload for
   // partial updates. Use the source size as the indicator.
@@ -419,9 +377,8 @@
   if (current_partition_ >= partitions_.size())
     return false;
 
-  // No support for ECC in minor version 1 or full payloads.
-  if (payload_->type == InstallPayloadType::kFull ||
-      GetMinorVersion() == kInPlaceMinorPayloadVersion)
+  // No support for ECC for full payloads.
+  if (payload_->type == InstallPayloadType::kFull)
     return false;
 
 #if USE_FEC
@@ -510,6 +467,21 @@
         return MetadataParseResult::kError;
       }
     }
+
+    // Check that the |metadata signature size_| and |metadata_size_| are not
+    // very big numbers. This is necessary since |update_engine| needs to write
+    // these values into the buffer before being able to use them, and if an
+    // attacker sets these values to a very big number, the buffer will overflow
+    // and |update_engine| will crash. A simple way of solving this is to check
+    // that the size of both values is smaller than the payload itself.
+    if (metadata_size_ + metadata_signature_size_ > payload_->size) {
+      LOG(ERROR) << "The size of the metadata_size(" << metadata_size_ << ")"
+                 << " or metadata signature(" << metadata_signature_size_ << ")"
+                 << " is greater than the size of the payload"
+                 << "(" << payload_->size << ")";
+      *error = ErrorCode::kDownloadInvalidMetadataSize;
+      return MetadataParseResult::kError;
+    }
   }
 
   // Now that we have validated the metadata size, we should wait for the full
@@ -572,7 +544,7 @@
 #define OP_DURATION_HISTOGRAM(_op_name, _start_time)                         \
   LOCAL_HISTOGRAM_CUSTOM_TIMES(                                              \
       "UpdateEngine.DownloadAction.InstallOperation::" _op_name ".Duration", \
-      base::TimeTicks::Now() - _start_time,                                  \
+      (base::TimeTicks::Now() - _start_time),                                \
       base::TimeDelta::FromMilliseconds(10),                                 \
       base::TimeDelta::FromMinutes(5),                                       \
       20);
@@ -737,14 +709,6 @@
         op_result = PerformZeroOrDiscardOperation(op);
         OP_DURATION_HISTOGRAM("ZERO_OR_DISCARD", op_start_time);
         break;
-      case InstallOperation::MOVE:
-        op_result = PerformMoveOperation(op);
-        OP_DURATION_HISTOGRAM("MOVE", op_start_time);
-        break;
-      case InstallOperation::BSDIFF:
-        op_result = PerformBsdiffOperation(op);
-        OP_DURATION_HISTOGRAM("BSDIFF", op_start_time);
-        break;
       case InstallOperation::SOURCE_COPY:
         op_result = PerformSourceCopyOperation(op, error);
         OP_DURATION_HISTOGRAM("SOURCE_COPY", op_start_time);
@@ -775,8 +739,7 @@
 
   // In major version 2, we don't add dummy operation to the payload.
   // If we already extracted the signature we should skip this step.
-  if (major_payload_version_ == kBrilloMajorPayloadVersion &&
-      manifest_.has_signatures_offset() && manifest_.has_signatures_size() &&
+  if (manifest_.has_signatures_offset() && manifest_.has_signatures_size() &&
       signatures_message_data_.empty()) {
     if (manifest_.signatures_offset() != buffer_offset_) {
       LOG(ERROR) << "Payload signatures offset points to blob offset "
@@ -811,49 +774,9 @@
 }
 
 bool DeltaPerformer::ParseManifestPartitions(ErrorCode* error) {
-  if (major_payload_version_ == kBrilloMajorPayloadVersion) {
-    partitions_.clear();
-    for (const PartitionUpdate& partition : manifest_.partitions()) {
-      partitions_.push_back(partition);
-    }
-  } else if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
-    LOG(INFO) << "Converting update information from old format.";
-    PartitionUpdate root_part;
-    root_part.set_partition_name(kPartitionNameRoot);
-#ifdef __ANDROID__
-    LOG(WARNING) << "Legacy payload major version provided to an Android "
-                    "build. Assuming no post-install. Please use major version "
-                    "2 or newer.";
-    root_part.set_run_postinstall(false);
-#else
-    root_part.set_run_postinstall(true);
-#endif  // __ANDROID__
-    if (manifest_.has_old_rootfs_info()) {
-      *root_part.mutable_old_partition_info() = manifest_.old_rootfs_info();
-      manifest_.clear_old_rootfs_info();
-    }
-    if (manifest_.has_new_rootfs_info()) {
-      *root_part.mutable_new_partition_info() = manifest_.new_rootfs_info();
-      manifest_.clear_new_rootfs_info();
-    }
-    *root_part.mutable_operations() = manifest_.install_operations();
-    manifest_.clear_install_operations();
-    partitions_.push_back(std::move(root_part));
-
-    PartitionUpdate kern_part;
-    kern_part.set_partition_name(kPartitionNameKernel);
-    kern_part.set_run_postinstall(false);
-    if (manifest_.has_old_kernel_info()) {
-      *kern_part.mutable_old_partition_info() = manifest_.old_kernel_info();
-      manifest_.clear_old_kernel_info();
-    }
-    if (manifest_.has_new_kernel_info()) {
-      *kern_part.mutable_new_partition_info() = manifest_.new_kernel_info();
-      manifest_.clear_new_kernel_info();
-    }
-    *kern_part.mutable_operations() = manifest_.kernel_install_operations();
-    manifest_.clear_kernel_install_operations();
-    partitions_.push_back(std::move(kern_part));
+  partitions_.clear();
+  for (const PartitionUpdate& partition : manifest_.partitions()) {
+    partitions_.push_back(partition);
   }
 
   // For VAB and partial updates, the partition preparation will copy the
@@ -871,6 +794,8 @@
     }
   }
 
+  // Partitions in manifest are no longer needed after preparing partitions.
+  manifest_.clear_partitions();
   // TODO(xunchang) TBD: allow partial update only on devices with dynamic
   // partition.
   if (manifest_.partial_update()) {
@@ -965,10 +890,6 @@
     install_plan_->partitions.push_back(install_part);
   }
 
-  if (major_payload_version_ == kBrilloMajorPayloadVersion) {
-    manifest_.clear_partitions();
-  }
-
   // TODO(xunchang) only need to load the partitions for those in payload.
   // Because we have already loaded the other once when generating SOURCE_COPY
   // operations.
@@ -1063,14 +984,6 @@
   TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
   TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
 
-  // Extract the signature message if it's in this operation.
-  if (ExtractSignatureMessageFromOperation(operation)) {
-    // If this is dummy replace operation, we ignore it after extracting the
-    // signature.
-    DiscardBuffer(true, 0);
-    return true;
-  }
-
   // Setup the ExtentWriter stack based on the operation type.
   std::unique_ptr<ExtentWriter> writer = std::make_unique<DirectExtentWriter>();
 
@@ -1129,57 +1042,6 @@
   return true;
 }
 
-bool DeltaPerformer::PerformMoveOperation(const InstallOperation& operation) {
-  // Calculate buffer size. Note, this function doesn't do a sliding
-  // window to copy in case the source and destination blocks overlap.
-  // If we wanted to do a sliding window, we could program the server
-  // to generate deltas that effectively did a sliding window.
-
-  uint64_t blocks_to_read = 0;
-  for (int i = 0; i < operation.src_extents_size(); i++)
-    blocks_to_read += operation.src_extents(i).num_blocks();
-
-  uint64_t blocks_to_write = 0;
-  for (int i = 0; i < operation.dst_extents_size(); i++)
-    blocks_to_write += operation.dst_extents(i).num_blocks();
-
-  DCHECK_EQ(blocks_to_write, blocks_to_read);
-  brillo::Blob buf(blocks_to_write * block_size_);
-
-  // Read in bytes.
-  ssize_t bytes_read = 0;
-  for (int i = 0; i < operation.src_extents_size(); i++) {
-    ssize_t bytes_read_this_iteration = 0;
-    const Extent& extent = operation.src_extents(i);
-    const size_t bytes = extent.num_blocks() * block_size_;
-    TEST_AND_RETURN_FALSE(extent.start_block() != kSparseHole);
-    TEST_AND_RETURN_FALSE(utils::PReadAll(target_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>(bytes));
-    bytes_read += bytes_read_this_iteration;
-  }
-
-  // Write bytes out.
-  ssize_t bytes_written = 0;
-  for (int i = 0; i < operation.dst_extents_size(); i++) {
-    const Extent& extent = operation.dst_extents(i);
-    const size_t bytes = extent.num_blocks() * block_size_;
-    TEST_AND_RETURN_FALSE(extent.start_block() != kSparseHole);
-    TEST_AND_RETURN_FALSE(utils::PWriteAll(target_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()));
-  return true;
-}
-
 bool DeltaPerformer::ValidateSourceHash(const brillo::Blob& calculated_hash,
                                         const InstallOperation& operation,
                                         const FileDescriptorPtr source_fd,
@@ -1411,47 +1273,6 @@
   return true;
 }
 
-bool DeltaPerformer::PerformBsdiffOperation(const InstallOperation& operation) {
-  // Since we delete data off the beginning of the buffer as we use it,
-  // the data we need should be exactly at the beginning of the buffer.
-  TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
-  TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
-
-  string input_positions;
-  TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(),
-                                                       block_size_,
-                                                       operation.src_length(),
-                                                       &input_positions));
-  string output_positions;
-  TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.dst_extents(),
-                                                       block_size_,
-                                                       operation.dst_length(),
-                                                       &output_positions));
-
-  TEST_AND_RETURN_FALSE(bsdiff::bspatch(target_path_.c_str(),
-                                        target_path_.c_str(),
-                                        buffer_.data(),
-                                        buffer_.size(),
-                                        input_positions.c_str(),
-                                        output_positions.c_str()) == 0);
-  DiscardBuffer(true, buffer_.size());
-
-  if (operation.dst_length() % block_size_) {
-    // Zero out rest of final block.
-    // TODO(adlr): build this into bspatch; it's more efficient that way.
-    const Extent& last_extent =
-        operation.dst_extents(operation.dst_extents_size() - 1);
-    const uint64_t end_byte =
-        (last_extent.start_block() + last_extent.num_blocks()) * block_size_;
-    const uint64_t begin_byte =
-        end_byte - (block_size_ - operation.dst_length() % block_size_);
-    brillo::Blob zeros(end_byte - begin_byte);
-    TEST_AND_RETURN_FALSE(utils::PWriteAll(
-        target_fd_, zeros.data(), end_byte - begin_byte, begin_byte));
-  }
-  return true;
-}
-
 namespace {
 
 class BsdiffExtentFile : public bsdiff::FileInterface {
@@ -1660,19 +1481,6 @@
   return true;
 }
 
-bool DeltaPerformer::ExtractSignatureMessageFromOperation(
-    const InstallOperation& operation) {
-  if (operation.type() != InstallOperation::REPLACE ||
-      !manifest_.has_signatures_offset() ||
-      manifest_.signatures_offset() != operation.data_offset()) {
-    return false;
-  }
-  TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() &&
-                        manifest_.signatures_size() == operation.data_length());
-  TEST_AND_RETURN_FALSE(ExtractSignatureMessage());
-  return true;
-}
-
 bool DeltaPerformer::ExtractSignatureMessage() {
   TEST_AND_RETURN_FALSE(signatures_message_data_.empty());
   TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset());
@@ -1744,11 +1552,11 @@
 ErrorCode DeltaPerformer::ValidateManifest() {
   // Perform assorted checks to sanity check the manifest, make sure it
   // matches data from other sources, and that it is a supported version.
-  bool has_old_fields =
-      (manifest_.has_old_kernel_info() || manifest_.has_old_rootfs_info());
-  for (const PartitionUpdate& partition : manifest_.partitions()) {
-    has_old_fields = has_old_fields || partition.has_old_partition_info();
-  }
+  bool has_old_fields = std::any_of(manifest_.partitions().begin(),
+                                    manifest_.partitions().end(),
+                                    [](const PartitionUpdate& partition) {
+                                      return partition.has_old_partition_info();
+                                    });
 
   // The presence of an old partition hash is the sole indicator for a delta
   // update.
@@ -1790,16 +1598,12 @@
     }
   }
 
-  if (major_payload_version_ != kChromeOSMajorPayloadVersion) {
-    if (manifest_.has_old_rootfs_info() || manifest_.has_new_rootfs_info() ||
-        manifest_.has_old_kernel_info() || manifest_.has_new_kernel_info() ||
-        manifest_.install_operations_size() != 0 ||
-        manifest_.kernel_install_operations_size() != 0) {
-      LOG(ERROR) << "Manifest contains deprecated field only supported in "
-                 << "major payload version 1, but the payload major version is "
-                 << major_payload_version_;
-      return ErrorCode::kPayloadMismatchedType;
-    }
+  if (manifest_.has_old_rootfs_info() || manifest_.has_new_rootfs_info() ||
+      manifest_.has_old_kernel_info() || manifest_.has_new_kernel_info() ||
+      manifest_.install_operations_size() != 0 ||
+      manifest_.kernel_install_operations_size() != 0) {
+    LOG(ERROR) << "Manifest contains deprecated fields.";
+    return ErrorCode::kPayloadMismatchedType;
   }
 
   if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) {
@@ -1814,18 +1618,8 @@
                  " the payload with an older timestamp.";
   }
 
-  if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
-    if (manifest_.has_dynamic_partition_metadata()) {
-      LOG(ERROR)
-          << "Should not contain dynamic_partition_metadata for major version "
-          << kChromeOSMajorPayloadVersion
-          << ". Please use major version 2 or above.";
-      return ErrorCode::kPayloadMismatchedType;
-    }
-  }
-
-  // TODO(garnold) we should be adding more and more manifest checks, such as
-  // partition boundaries etc (see chromium-os:37661).
+  // TODO(crbug.com/37661) we should be adding more and more manifest checks,
+  // such as partition boundaries, etc.
 
   return ErrorCode::kSuccess;
 }
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 01fcc5c..7b30a83 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -254,8 +254,6 @@
   // set even if it fails.
   bool PerformReplaceOperation(const InstallOperation& operation);
   bool PerformZeroOrDiscardOperation(const InstallOperation& operation);
-  bool PerformMoveOperation(const InstallOperation& operation);
-  bool PerformBsdiffOperation(const InstallOperation& operation);
   bool PerformSourceCopyOperation(const InstallOperation& operation,
                                   ErrorCode* error);
   bool PerformSourceBsdiffOperation(const InstallOperation& operation,
@@ -270,11 +268,6 @@
   FileDescriptorPtr ChooseSourceFD(const InstallOperation& operation,
                                    ErrorCode* error);
 
-  // Extracts the payload signature message from the blob on the |operation| if
-  // the offset matches the one specified by the manifest. Returns whether the
-  // signature was extracted.
-  bool ExtractSignatureMessageFromOperation(const InstallOperation& operation);
-
   // Extracts the payload signature message from the current |buffer_| if the
   // offset matches the one specified by the manifest. Returns whether the
   // signature was extracted.
diff --git a/payload_consumer/delta_performer_fuzzer.cc b/payload_consumer/delta_performer_fuzzer.cc
new file mode 100644
index 0000000..73082c4
--- /dev/null
+++ b/payload_consumer/delta_performer_fuzzer.cc
@@ -0,0 +1,105 @@
+//
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+
+#include <base/logging.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "update_engine/common/fake_boot_control.h"
+#include "update_engine/common/fake_hardware.h"
+#include "update_engine/common/prefs.h"
+#include "update_engine/payload_consumer/delta_performer.h"
+#include "update_engine/payload_consumer/download_action.h"
+#include "update_engine/payload_consumer/install_plan.h"
+
+namespace chromeos_update_engine {
+
+class FakeDownloadActionDelegate : public DownloadActionDelegate {
+ public:
+  FakeDownloadActionDelegate() = default;
+  ~FakeDownloadActionDelegate() = default;
+
+  // DownloadActionDelegate overrides;
+  void BytesReceived(uint64_t bytes_progressed,
+                     uint64_t bytes_received,
+                     uint64_t total) override{};
+
+  bool ShouldCancel(ErrorCode* cancel_reason) override { return false; };
+
+  void DownloadComplete() override{};
+
+  DISALLOW_COPY_AND_ASSIGN(FakeDownloadActionDelegate);
+};
+
+void FuzzDeltaPerformer(const uint8_t* data, size_t size) {
+  MemoryPrefs prefs;
+  FakeBootControl boot_control;
+  FakeHardware hardware;
+  FakeDownloadActionDelegate download_action_delegate;
+
+  FuzzedDataProvider data_provider(data, size);
+
+  InstallPlan install_plan{
+      .target_slot = 1,
+      .partitions = {InstallPlan::Partition{
+          .source_path = "/dev/zero",
+          .source_size = 4096,
+          .target_path = "/dev/null",
+          .target_size = 4096,
+      }},
+      .hash_checks_mandatory = true,
+  };
+
+  InstallPlan::Payload payload{
+      .size = data_provider.ConsumeIntegralInRange<uint64_t>(0, 10000),
+      .metadata_size = data_provider.ConsumeIntegralInRange<uint64_t>(0, 1000),
+      .hash = data_provider.ConsumeBytes<uint8_t>(32),
+      .type = static_cast<InstallPayloadType>(
+          data_provider.ConsumeIntegralInRange(0, 3)),
+      .already_applied = data_provider.ConsumeBool(),
+  };
+
+  DeltaPerformer performer(&prefs,
+                           &boot_control,
+                           &hardware,
+                           &download_action_delegate,
+                           &install_plan,
+                           &payload,
+                           data_provider.ConsumeBool());
+  do {
+    auto chunk_size = data_provider.ConsumeIntegralInRange<size_t>(0, 100);
+    auto data = data_provider.ConsumeBytes<uint8_t>(chunk_size);
+    if (!performer.Write(data.data(), data.size()))
+      break;
+  } while (data_provider.remaining_bytes() > 0);
+}
+
+}  // namespace chromeos_update_engine
+
+class Environment {
+ public:
+  Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size > 1000000) {
+    return 0;
+  }
+
+  static Environment env;
+  chromeos_update_engine::FuzzDeltaPerformer(data, size);
+  return 0;
+}
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index 4797137..16641c6 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -25,6 +25,7 @@
 
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
+#include <base/stl_util.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <google/protobuf/repeated_field.h>
@@ -78,6 +79,7 @@
 
   string delta_path;
   uint64_t metadata_size;
+  uint32_t metadata_signature_size;
 
   string old_kernel;
   brillo::Blob old_kernel_data;
@@ -186,16 +188,19 @@
   string private_key_path = GetBuildArtifactsPath(kUnittestPrivateKeyPath);
   size_t signature_size;
   ASSERT_TRUE(PayloadSigner::GetMaximumSignatureSize(private_key_path,
-                                                     &signature_size));
-  brillo::Blob hash;
+                                                   &signature_size));
+  brillo::Blob metadata_hash, payload_hash;
   ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
-      payload_path, {signature_size}, &hash, nullptr));
-  brillo::Blob signature;
-  ASSERT_TRUE(PayloadSigner::SignHash(hash, private_key_path, &signature));
+      payload_path, {signature_size}, &payload_hash, &metadata_hash));
+  brillo::Blob metadata_signature, payload_signature;
+  ASSERT_TRUE(PayloadSigner::SignHash(
+      payload_hash, private_key_path, &payload_signature));
+  ASSERT_TRUE(PayloadSigner::SignHash(
+      metadata_hash, private_key_path, &metadata_signature));
   ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(payload_path,
                                                    {signature_size},
-                                                   {signature},
-                                                   {},
+                                                   {payload_signature},
+                                                   {metadata_signature},
                                                    payload_path,
                                                    out_metadata_size));
   EXPECT_TRUE(PayloadSigner::VerifySignedPayload(
@@ -216,19 +221,21 @@
   }
   string signature_size_string = base::JoinString(signature_size_strings, ":");
 
-  test_utils::ScopedTempFile hash_file("hash.XXXXXX");
+  test_utils::ScopedTempFile hash_file("hash.XXXXXX"),
+      metadata_hash_file("hash.XXXXXX");
   string delta_generator_path = GetBuildArtifactsPath("delta_generator");
   ASSERT_EQ(0,
             System(base::StringPrintf(
-                "%s -in_file=%s -signature_size=%s -out_hash_file=%s",
+                 "%s -in_file=%s -signature_size=%s -out_hash_file=%s "
+                 "-out_metadata_hash_file=%s",
                 delta_generator_path.c_str(),
                 payload_path.c_str(),
                 signature_size_string.c_str(),
-                hash_file.path().c_str())));
+                hash_file.path().c_str(), metadata_hash_file.path().c_str())));
 
   // Sign the hash with all private keys.
-  vector<test_utils::ScopedTempFile> sig_files;
-  vector<string> sig_file_paths;
+  vector<test_utils::ScopedTempFile> sig_files, metadata_sig_files;
+  vector<string> sig_file_paths, metadata_sig_file_paths;
   for (const auto& key_path : private_key_paths) {
     brillo::Blob hash, signature;
     ASSERT_TRUE(utils::ReadFile(hash_file.path(), &hash));
@@ -238,17 +245,31 @@
     ASSERT_TRUE(test_utils::WriteFileVector(sig_file.path(), signature));
     sig_file_paths.push_back(sig_file.path());
     sig_files.push_back(std::move(sig_file));
+
+    brillo::Blob metadata_hash, metadata_signature;
+    ASSERT_TRUE(utils::ReadFile(metadata_hash_file.path(), &metadata_hash));
+    ASSERT_TRUE(PayloadSigner::SignHash(metadata_hash, key_path, &metadata_signature));
+
+    test_utils::ScopedTempFile metadata_sig_file("signature.XXXXXX");
+    ASSERT_TRUE(test_utils::WriteFileVector(metadata_sig_file.path(), metadata_signature));
+
+    metadata_sig_file_paths.push_back(metadata_sig_file.path());
+    metadata_sig_files.push_back(std::move(metadata_sig_file));
   }
   string sig_files_string = base::JoinString(sig_file_paths, ":");
+  string metadata_sig_files_string = base::JoinString(metadata_sig_file_paths, ":");
 
   // Add the signature to the payload.
   ASSERT_EQ(0,
             System(base::StringPrintf("%s --signature_size=%s -in_file=%s "
-                                      "-payload_signature_file=%s -out_file=%s",
+                                      "-payload_signature_file=%s "
+                                      "-metadata_signature_file=%s "
+                                      "-out_file=%s",
                                       delta_generator_path.c_str(),
                                       signature_size_string.c_str(),
                                       payload_path.c_str(),
                                       sig_files_string.c_str(),
+                                      metadata_sig_files_string.c_str(),
                                       payload_path.c_str())));
 
   int verify_result = System(base::StringPrintf("%s -in_file=%s -public_key=%s",
@@ -330,7 +351,6 @@
 
 static void GenerateDeltaFile(bool full_kernel,
                               bool full_rootfs,
-                              bool noop,
                               ssize_t chunk_size,
                               SignatureTest signature_test,
                               DeltaState* state,
@@ -407,24 +427,16 @@
                          ones.size()));
   }
 
-  if (noop) {
-    EXPECT_TRUE(base::CopyFile(base::FilePath(state->a_img),
-                               base::FilePath(state->b_img)));
-    old_image_info = new_image_info;
-  } else {
-    if (minor_version == kSourceMinorPayloadVersion) {
-      // Create a result image with image_size bytes of garbage.
-      brillo::Blob ones(state->image_size, 0xff);
-      EXPECT_TRUE(utils::WriteFile(
-          state->result_img.c_str(), ones.data(), ones.size()));
-      EXPECT_EQ(utils::FileSize(state->a_img),
-                utils::FileSize(state->result_img));
-    }
+  // Create a result image with image_size bytes of garbage.
+  brillo::Blob ones(state->image_size, 0xff);
+  EXPECT_TRUE(
+      utils::WriteFile(state->result_img.c_str(), ones.data(), ones.size()));
+  EXPECT_EQ(utils::FileSize(state->a_img), utils::FileSize(state->result_img));
 
-    EXPECT_TRUE(
-        base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
-                       base::FilePath(state->b_img)));
-
+  EXPECT_TRUE(
+      base::CopyFile(GetBuildArtifactsPath().Append("gen/disk_ext2_4k.img"),
+                     base::FilePath(state->b_img)));
+  {
     // Make some changes to the B image.
     string b_mnt;
     ScopedLoopMounter b_mounter(state->b_img, &b_mnt, 0);
@@ -499,10 +511,6 @@
   std::copy(
       std::begin(kNewData), std::end(kNewData), state->new_kernel_data.begin());
 
-  if (noop) {
-    state->old_kernel_data = state->new_kernel_data;
-  }
-
   // Write kernels to disk
   EXPECT_TRUE(utils::WriteFile(state->old_kernel.c_str(),
                                state->old_kernel_data.data(),
@@ -526,7 +534,7 @@
     payload_config.is_delta = !full_rootfs;
     payload_config.hard_chunk_size = chunk_size;
     payload_config.rootfs_partition_size = kRootFSPartitionSize;
-    payload_config.version.major = kChromeOSMajorPayloadVersion;
+    payload_config.version.major = kBrilloMajorPayloadVersion;
     payload_config.version.minor = minor_version;
     if (!full_rootfs) {
       payload_config.source.partitions.emplace_back(kPartitionNameRoot);
@@ -605,7 +613,6 @@
 
 static void ApplyDeltaFile(bool full_kernel,
                            bool full_rootfs,
-                           bool noop,
                            SignatureTest signature_test,
                            DeltaState* state,
                            bool hash_checks_mandatory,
@@ -619,6 +626,9 @@
     EXPECT_TRUE(payload_metadata.ParsePayloadHeader(state->delta));
     state->metadata_size = payload_metadata.GetMetadataSize();
     LOG(INFO) << "Metadata size: " << state->metadata_size;
+    state->metadata_signature_size =
+        payload_metadata.GetMetadataSignatureSize();
+    LOG(INFO) << "Metadata signature size: " << state->metadata_signature_size;
 
     DeltaArchiveManifest manifest;
     EXPECT_TRUE(payload_metadata.GetManifest(state->delta, &manifest));
@@ -630,7 +640,8 @@
       EXPECT_TRUE(manifest.has_signatures_size());
       Signatures sigs_message;
       EXPECT_TRUE(sigs_message.ParseFromArray(
-          &state->delta[state->metadata_size + manifest.signatures_offset()],
+          &state->delta[state->metadata_size + state->metadata_signature_size +
+                        manifest.signatures_offset()],
           manifest.signatures_size()));
       if (signature_test == kSignatureGeneratedShellRotateCl1 ||
           signature_test == kSignatureGeneratedShellRotateCl2)
@@ -653,18 +664,38 @@
       EXPECT_FALSE(signature.data().empty());
     }
 
-    if (noop) {
-      EXPECT_EQ(0, manifest.install_operations_size());
-      EXPECT_EQ(1, manifest.kernel_install_operations_size());
-    }
-
+    // TODO(ahassani): Make |DeltaState| into a partition list kind of struct
+    // instead of hardcoded kernel/rootfs so its cleaner and we can make the
+    // following code into a helper function instead.
+    const auto& kernel_part = *std::find_if(
+        manifest.partitions().begin(),
+        manifest.partitions().end(),
+        [](const PartitionUpdate& partition) {
+          return partition.partition_name() == kPartitionNameKernel;
+        });
     if (full_kernel) {
-      EXPECT_FALSE(manifest.has_old_kernel_info());
+      EXPECT_FALSE(kernel_part.has_old_partition_info());
     } else {
       EXPECT_EQ(state->old_kernel_data.size(),
-                manifest.old_kernel_info().size());
-      EXPECT_FALSE(manifest.old_kernel_info().hash().empty());
+                kernel_part.old_partition_info().size());
+      EXPECT_FALSE(kernel_part.old_partition_info().hash().empty());
     }
+    EXPECT_EQ(state->new_kernel_data.size(),
+              kernel_part.new_partition_info().size());
+    EXPECT_FALSE(kernel_part.new_partition_info().hash().empty());
+
+    const auto& rootfs_part =
+        *std::find_if(manifest.partitions().begin(),
+                      manifest.partitions().end(),
+                      [](const PartitionUpdate& partition) {
+                        return partition.partition_name() == kPartitionNameRoot;
+                      });
+    if (full_rootfs) {
+      EXPECT_FALSE(rootfs_part.has_old_partition_info());
+    } else {
+      EXPECT_FALSE(rootfs_part.old_partition_info().hash().empty());
+    }
+    EXPECT_FALSE(rootfs_part.new_partition_info().hash().empty());
 
     EXPECT_EQ(manifest.new_image_info().channel(), "test-channel");
     EXPECT_EQ(manifest.new_image_info().board(), "test-board");
@@ -674,47 +705,21 @@
     EXPECT_EQ(manifest.new_image_info().build_version(), "test-build-version");
 
     if (!full_rootfs) {
-      if (noop) {
-        EXPECT_EQ(manifest.old_image_info().channel(), "test-channel");
-        EXPECT_EQ(manifest.old_image_info().board(), "test-board");
-        EXPECT_EQ(manifest.old_image_info().version(), "test-version");
-        EXPECT_EQ(manifest.old_image_info().key(), "test-key");
-        EXPECT_EQ(manifest.old_image_info().build_channel(),
-                  "test-build-channel");
-        EXPECT_EQ(manifest.old_image_info().build_version(),
-                  "test-build-version");
-      } else {
-        EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
-        EXPECT_EQ(manifest.old_image_info().board(), "src-board");
-        EXPECT_EQ(manifest.old_image_info().version(), "src-version");
-        EXPECT_EQ(manifest.old_image_info().key(), "src-key");
-        EXPECT_EQ(manifest.old_image_info().build_channel(),
-                  "src-build-channel");
-        EXPECT_EQ(manifest.old_image_info().build_version(),
-                  "src-build-version");
-      }
+      EXPECT_EQ(manifest.old_image_info().channel(), "src-channel");
+      EXPECT_EQ(manifest.old_image_info().board(), "src-board");
+      EXPECT_EQ(manifest.old_image_info().version(), "src-version");
+      EXPECT_EQ(manifest.old_image_info().key(), "src-key");
+      EXPECT_EQ(manifest.old_image_info().build_channel(), "src-build-channel");
+      EXPECT_EQ(manifest.old_image_info().build_version(), "src-build-version");
     }
-
-    if (full_rootfs) {
-      EXPECT_FALSE(manifest.has_old_rootfs_info());
-      EXPECT_FALSE(manifest.has_old_image_info());
-      EXPECT_TRUE(manifest.has_new_image_info());
-    } else {
-      EXPECT_EQ(state->image_size, manifest.old_rootfs_info().size());
-      EXPECT_FALSE(manifest.old_rootfs_info().hash().empty());
-    }
-
-    EXPECT_EQ(state->new_kernel_data.size(), manifest.new_kernel_info().size());
-    EXPECT_EQ(state->image_size, manifest.new_rootfs_info().size());
-
-    EXPECT_FALSE(manifest.new_kernel_info().hash().empty());
-    EXPECT_FALSE(manifest.new_rootfs_info().hash().empty());
   }
 
   MockPrefs prefs;
   EXPECT_CALL(prefs, SetInt64(kPrefsManifestMetadataSize, state->metadata_size))
       .WillOnce(Return(true));
-  EXPECT_CALL(prefs, SetInt64(kPrefsManifestSignatureSize, 0))
+  EXPECT_CALL(
+      prefs,
+      SetInt64(kPrefsManifestSignatureSize, state->metadata_signature_size))
       .WillOnce(Return(true));
   EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
       .WillRepeatedly(Return(true));
@@ -741,7 +746,8 @@
   // Update the A image in place.
   InstallPlan* install_plan = &state->install_plan;
   install_plan->hash_checks_mandatory = hash_checks_mandatory;
-  install_plan->payloads = {{.metadata_size = state->metadata_size,
+  install_plan->payloads = {{.size = state->delta.size(),
+                             .metadata_size = state->metadata_size,
                              .type = (full_kernel && full_rootfs)
                                          ? InstallPayloadType::kFull
                                          : InstallPayloadType::kDelta}};
@@ -788,25 +794,14 @@
   // The partitions should be empty before DeltaPerformer.
   install_plan->partitions.clear();
 
-  // With minor version 2, we want the target to be the new image, result_img,
-  // but with version 1, we want to update A in place.
-  string target_root, target_kernel;
-  if (minor_version == kSourceMinorPayloadVersion) {
-    target_root = state->result_img;
-    target_kernel = state->result_kernel;
-  } else {
-    target_root = state->a_img;
-    target_kernel = state->old_kernel;
-  }
-
   state->fake_boot_control_.SetPartitionDevice(
       kPartitionNameRoot, install_plan->source_slot, state->a_img);
   state->fake_boot_control_.SetPartitionDevice(
       kPartitionNameKernel, install_plan->source_slot, state->old_kernel);
   state->fake_boot_control_.SetPartitionDevice(
-      kPartitionNameRoot, install_plan->target_slot, target_root);
+      kPartitionNameRoot, install_plan->target_slot, state->result_img);
   state->fake_boot_control_.SetPartitionDevice(
-      kPartitionNameKernel, install_plan->target_slot, target_kernel);
+      kPartitionNameKernel, install_plan->target_slot, state->result_kernel);
 
   ErrorCode expected_error, actual_error;
   bool continue_writing;
@@ -885,21 +880,13 @@
     return;
   }
 
-  brillo::Blob updated_kernel_partition;
-  if (minor_version == kSourceMinorPayloadVersion) {
-    CompareFilesByBlock(
-        state->result_kernel, state->new_kernel, state->kernel_size);
-    CompareFilesByBlock(state->result_img, state->b_img, state->image_size);
-    EXPECT_TRUE(
-        utils::ReadFile(state->result_kernel, &updated_kernel_partition));
-  } else {
-    CompareFilesByBlock(
-        state->old_kernel, state->new_kernel, state->kernel_size);
-    CompareFilesByBlock(state->a_img, state->b_img, state->image_size);
-    EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
-  }
+  CompareFilesByBlock(
+      state->result_kernel, state->new_kernel, state->kernel_size);
+  CompareFilesByBlock(state->result_img, state->b_img, state->image_size);
 
-  ASSERT_GE(updated_kernel_partition.size(), arraysize(kNewData));
+  brillo::Blob updated_kernel_partition;
+  EXPECT_TRUE(utils::ReadFile(state->result_kernel, &updated_kernel_partition));
+  ASSERT_GE(updated_kernel_partition.size(), base::size(kNewData));
   EXPECT_TRUE(std::equal(std::begin(kNewData),
                          std::end(kNewData),
                          updated_kernel_partition.begin()));
@@ -944,7 +931,6 @@
 
 void DoSmallImageTest(bool full_kernel,
                       bool full_rootfs,
-                      bool noop,
                       ssize_t chunk_size,
                       SignatureTest signature_test,
                       bool hash_checks_mandatory,
@@ -953,7 +939,6 @@
   DeltaPerformer* performer = nullptr;
   GenerateDeltaFile(full_kernel,
                     full_rootfs,
-                    noop,
                     chunk_size,
                     signature_test,
                     &state,
@@ -968,7 +953,6 @@
   ScopedPathUnlinker result_kernel_unlinker(state.result_kernel);
   ApplyDeltaFile(full_kernel,
                  full_rootfs,
-                 noop,
                  signature_test,
                  &state,
                  hash_checks_mandatory,
@@ -983,8 +967,7 @@
                                  bool hash_checks_mandatory) {
   DeltaState state;
   uint64_t minor_version = kFullPayloadMinorVersion;
-  GenerateDeltaFile(
-      true, true, false, -1, kSignatureGenerated, &state, minor_version);
+  GenerateDeltaFile(true, true, -1, kSignatureGenerated, &state, minor_version);
   ScopedPathUnlinker a_img_unlinker(state.a_img);
   ScopedPathUnlinker b_img_unlinker(state.b_img);
   ScopedPathUnlinker delta_unlinker(state.delta_path);
@@ -993,7 +976,6 @@
   DeltaPerformer* performer = nullptr;
   ApplyDeltaFile(true,
                  true,
-                 false,
                  kSignatureGenerated,
                  &state,
                  hash_checks_mandatory,
@@ -1004,24 +986,18 @@
 }
 
 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageTest) {
-  DoSmallImageTest(false,
-                   false,
-                   false,
-                   -1,
-                   kSignatureGenerator,
-                   false,
-                   kInPlaceMinorPayloadVersion);
+  DoSmallImageTest(
+      false, false, -1, kSignatureGenerator, false, kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest,
      RunAsRootSmallImageSignaturePlaceholderTest) {
   DoSmallImageTest(false,
                    false,
-                   false,
                    -1,
                    kSignatureGeneratedPlaceholder,
                    false,
-                   kInPlaceMinorPayloadVersion);
+                   kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest,
@@ -1029,131 +1005,97 @@
   DeltaState state;
   GenerateDeltaFile(false,
                     false,
-                    false,
                     -1,
                     kSignatureGeneratedPlaceholderMismatch,
                     &state,
-                    kInPlaceMinorPayloadVersion);
+                    kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageChunksTest) {
   DoSmallImageTest(false,
                    false,
-                   false,
                    kBlockSize,
                    kSignatureGenerator,
                    false,
-                   kInPlaceMinorPayloadVersion);
+                   kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest, RunAsRootFullKernelSmallImageTest) {
-  DoSmallImageTest(true,
-                   false,
-                   false,
-                   -1,
-                   kSignatureGenerator,
-                   false,
-                   kInPlaceMinorPayloadVersion);
+  DoSmallImageTest(
+      true, false, -1, kSignatureGenerator, false, kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest, RunAsRootFullSmallImageTest) {
   DoSmallImageTest(true,
                    true,
-                   false,
                    -1,
                    kSignatureGenerator,
                    true,
                    kFullPayloadMinorVersion);
 }
 
-TEST(DeltaPerformerIntegrationTest, RunAsRootNoopSmallImageTest) {
-  DoSmallImageTest(false,
-                   false,
-                   true,
-                   -1,
-                   kSignatureGenerator,
-                   false,
-                   kInPlaceMinorPayloadVersion);
-}
-
 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignNoneTest) {
-  DoSmallImageTest(false,
-                   false,
-                   false,
-                   -1,
-                   kSignatureNone,
-                   false,
-                   kInPlaceMinorPayloadVersion);
+  DoSmallImageTest(
+      false, false, -1, kSignatureNone, false, kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedTest) {
-  DoSmallImageTest(false,
-                   false,
-                   false,
-                   -1,
-                   kSignatureGenerated,
-                   true,
-                   kInPlaceMinorPayloadVersion);
+  DoSmallImageTest(
+      false, false, -1, kSignatureGenerated, true, kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSignGeneratedShellTest) {
   DoSmallImageTest(false,
                    false,
-                   false,
                    -1,
                    kSignatureGeneratedShell,
                    false,
-                   kInPlaceMinorPayloadVersion);
+                   kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest,
      RunAsRootSmallImageSignGeneratedShellECKeyTest) {
   DoSmallImageTest(false,
                    false,
-                   false,
                    -1,
                    kSignatureGeneratedShellECKey,
                    false,
-                   kInPlaceMinorPayloadVersion);
+                   kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest,
      RunAsRootSmallImageSignGeneratedShellBadKeyTest) {
   DoSmallImageTest(false,
                    false,
-                   false,
                    -1,
                    kSignatureGeneratedShellBadKey,
                    false,
-                   kInPlaceMinorPayloadVersion);
+                   kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest,
      RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
   DoSmallImageTest(false,
                    false,
-                   false,
                    -1,
                    kSignatureGeneratedShellRotateCl1,
                    false,
-                   kInPlaceMinorPayloadVersion);
+                   kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest,
      RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
   DoSmallImageTest(false,
                    false,
-                   false,
                    -1,
                    kSignatureGeneratedShellRotateCl2,
                    false,
-                   kInPlaceMinorPayloadVersion);
+                   kSourceMinorPayloadVersion);
 }
 
 TEST(DeltaPerformerIntegrationTest, RunAsRootSmallImageSourceOpsTest) {
   DoSmallImageTest(false,
                    false,
-                   false,
                    -1,
                    kSignatureGenerator,
                    false,
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index e9022ba..44107cd 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -27,6 +27,7 @@
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
+#include <base/stl_util.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
@@ -43,6 +44,7 @@
 #include "update_engine/payload_consumer/fake_file_descriptor.h"
 #include "update_engine/payload_consumer/mock_download_action.h"
 #include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_metadata.h"
 #include "update_engine/payload_generator/bzip.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/payload_file.h"
@@ -286,6 +288,7 @@
     test_utils::ScopedTempFile new_part("Partition-XXXXXX");
     EXPECT_TRUE(test_utils::WriteFileVector(new_part.path(), target_data));
 
+    payload_.size = payload_data.size();
     // We installed the operations only in the rootfs partition, but the
     // delta performer needs to access all the partitions.
     fake_boot_control_.SetPartitionDevice(
@@ -318,14 +321,17 @@
 
     // Set a valid magic string and version number 1.
     EXPECT_TRUE(performer_.Write("CrAU", 4));
-    uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
+    uint64_t version = htobe64(kBrilloMajorPayloadVersion);
     EXPECT_TRUE(performer_.Write(&version, 8));
 
     payload_.metadata_size = expected_metadata_size;
+    payload_.size = actual_metadata_size + 1;
     ErrorCode error_code;
-    // When filling in size in manifest, exclude the size of the 20-byte header.
-    uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
-    bool result = performer_.Write(&size_in_manifest, 8, &error_code);
+    // When filling in size in manifest, exclude the size of the 24-byte header.
+    uint64_t size_in_manifest = htobe64(actual_metadata_size - 24);
+    performer_.Write(&size_in_manifest, 8, &error_code);
+    auto signature_size = htobe64(10);
+    bool result = performer_.Write(&signature_size, 4, &error_code);
     if (expected_metadata_size == actual_metadata_size ||
         !hash_checks_mandatory) {
       EXPECT_TRUE(result);
@@ -337,7 +343,7 @@
     EXPECT_LT(performer_.Close(), 0);
   }
 
-  // Generates a valid delta file but tests the delta performer by suppling
+  // Generates a valid delta file but tests the delta performer by supplying
   // different metadata signatures as per metadata_signature_test flag and
   // sees if the result of the parsing are as per hash_checks_mandatory flag.
   void DoMetadataSignatureTest(MetadataSignatureTest metadata_signature_test,
@@ -347,9 +353,10 @@
     brillo::Blob payload = GeneratePayload(brillo::Blob(),
                                            vector<AnnotatedOperation>(),
                                            sign_payload,
-                                           kChromeOSMajorPayloadVersion,
+                                           kBrilloMajorPayloadVersion,
                                            kFullPayloadMinorVersion);
 
+    payload_.size = payload.size();
     LOG(INFO) << "Payload size: " << payload.size();
 
     install_plan_.hash_checks_mandatory = hash_checks_mandatory;
@@ -361,6 +368,9 @@
     switch (metadata_signature_test) {
       case kEmptyMetadataSignature:
         payload_.metadata_signature.clear();
+        // We need to set the signature size in a signed payload to zero.
+        std::fill(
+            std::next(payload.begin(), 20), std::next(payload.begin(), 24), 0);
         expected_result = MetadataParseResult::kError;
         expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
         break;
@@ -455,7 +465,7 @@
   brillo::Blob payload_data = GeneratePayload(expected_data,
                                               aops,
                                               false,
-                                              kChromeOSMajorPayloadVersion,
+                                              kBrilloMajorPayloadVersion,
                                               kFullPayloadMinorVersion);
 
   EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
@@ -477,7 +487,7 @@
   brillo::Blob payload_data = GeneratePayload(expected_data,
                                               aops,
                                               false,
-                                              kChromeOSMajorPayloadVersion,
+                                              kBrilloMajorPayloadVersion,
                                               kFullPayloadMinorVersion);
 
   testing::Mock::VerifyAndClearExpectations(&mock_delegate_);
@@ -739,12 +749,12 @@
 
 TEST_F(DeltaPerformerTest, ExtentsToByteStringTest) {
   uint64_t test[] = {1, 1, 4, 2, 0, 1};
-  static_assert(arraysize(test) % 2 == 0, "Array size uneven");
+  static_assert(base::size(test) % 2 == 0, "Array size uneven");
   const uint64_t block_size = 4096;
   const uint64_t file_length = 4 * block_size - 13;
 
   google::protobuf::RepeatedPtrField<Extent> extents;
-  for (size_t i = 0; i < arraysize(test); i += 2) {
+  for (size_t i = 0; i < base::size(test); i += 2) {
     *(extents.Add()) = ExtentForRange(test[i], test[i + 1]);
   }
 
@@ -758,27 +768,32 @@
 TEST_F(DeltaPerformerTest, ValidateManifestFullGoodTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  manifest.mutable_new_kernel_info();
-  manifest.mutable_new_rootfs_info();
+  for (const auto& part_name : {"kernel", "rootfs"}) {
+    auto part = manifest.add_partitions();
+    part->set_partition_name(part_name);
+    part->mutable_new_partition_info();
+  }
   manifest.set_minor_version(kFullPayloadMinorVersion);
 
   RunManifestValidation(manifest,
-                        kChromeOSMajorPayloadVersion,
+                        kBrilloMajorPayloadVersion,
                         InstallPayloadType::kFull,
                         ErrorCode::kSuccess);
 }
 
-TEST_F(DeltaPerformerTest, ValidateManifestDeltaGoodTest) {
+TEST_F(DeltaPerformerTest, ValidateManifestDeltaMaxGoodTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  manifest.mutable_old_kernel_info();
-  manifest.mutable_old_rootfs_info();
-  manifest.mutable_new_kernel_info();
-  manifest.mutable_new_rootfs_info();
+  for (const auto& part_name : {"kernel", "rootfs"}) {
+    auto part = manifest.add_partitions();
+    part->set_partition_name(part_name);
+    part->mutable_old_partition_info();
+    part->mutable_new_partition_info();
+  }
   manifest.set_minor_version(kMaxSupportedMinorPayloadVersion);
 
   RunManifestValidation(manifest,
-                        kChromeOSMajorPayloadVersion,
+                        kBrilloMajorPayloadVersion,
                         InstallPayloadType::kDelta,
                         ErrorCode::kSuccess);
 }
@@ -786,14 +801,16 @@
 TEST_F(DeltaPerformerTest, ValidateManifestDeltaMinGoodTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  manifest.mutable_old_kernel_info();
-  manifest.mutable_old_rootfs_info();
-  manifest.mutable_new_kernel_info();
-  manifest.mutable_new_rootfs_info();
+  for (const auto& part_name : {"kernel", "rootfs"}) {
+    auto part = manifest.add_partitions();
+    part->set_partition_name(part_name);
+    part->mutable_old_partition_info();
+    part->mutable_new_partition_info();
+  }
   manifest.set_minor_version(kMinSupportedMinorPayloadVersion);
 
   RunManifestValidation(manifest,
-                        kChromeOSMajorPayloadVersion,
+                        kBrilloMajorPayloadVersion,
                         InstallPayloadType::kDelta,
                         ErrorCode::kSuccess);
 }
@@ -811,9 +828,11 @@
 TEST_F(DeltaPerformerTest, ValidateManifestDeltaUnsetMinorVersion) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  // Add an empty old_rootfs_info() to trick the DeltaPerformer into think that
-  // this is a delta payload manifest with a missing minor version.
-  manifest.mutable_old_rootfs_info();
+  // Add an empty rootfs partition info to trick the DeltaPerformer into think
+  // that this is a delta payload manifest with a missing minor version.
+  auto rootfs = manifest.add_partitions();
+  rootfs->set_partition_name("rootfs");
+  rootfs->mutable_old_partition_info();
 
   RunManifestValidation(manifest,
                         kMaxSupportedMajorPayloadVersion,
@@ -824,27 +843,15 @@
 TEST_F(DeltaPerformerTest, ValidateManifestFullOldKernelTest) {
   // The Manifest we are validating.
   DeltaArchiveManifest manifest;
-  manifest.mutable_old_kernel_info();
-  manifest.mutable_new_kernel_info();
-  manifest.mutable_new_rootfs_info();
-  manifest.set_minor_version(kMaxSupportedMinorPayloadVersion);
-
+  for (const auto& part_name : {"kernel", "rootfs"}) {
+    auto part = manifest.add_partitions();
+    part->set_partition_name(part_name);
+    part->mutable_old_partition_info();
+    part->mutable_new_partition_info();
+  }
+  manifest.mutable_partitions(0)->clear_old_partition_info();
   RunManifestValidation(manifest,
-                        kChromeOSMajorPayloadVersion,
-                        InstallPayloadType::kFull,
-                        ErrorCode::kPayloadMismatchedType);
-}
-
-TEST_F(DeltaPerformerTest, ValidateManifestFullOldRootfsTest) {
-  // The Manifest we are validating.
-  DeltaArchiveManifest manifest;
-  manifest.mutable_old_rootfs_info();
-  manifest.mutable_new_kernel_info();
-  manifest.mutable_new_rootfs_info();
-  manifest.set_minor_version(kMaxSupportedMinorPayloadVersion);
-
-  RunManifestValidation(manifest,
-                        kChromeOSMajorPayloadVersion,
+                        kBrilloMajorPayloadVersion,
                         InstallPayloadType::kFull,
                         ErrorCode::kPayloadMismatchedType);
 }
@@ -869,8 +876,8 @@
 
   // Generate a bad version number.
   manifest.set_minor_version(kMaxSupportedMinorPayloadVersion + 10000);
-  // Mark the manifest as a delta payload by setting old_rootfs_info.
-  manifest.mutable_old_rootfs_info();
+  // Mark the manifest as a delta payload by setting |old_partition_info|.
+  manifest.add_partitions()->mutable_old_partition_info();
 
   RunManifestValidation(manifest,
                         kMaxSupportedMajorPayloadVersion,
@@ -897,15 +904,27 @@
   EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
 
   uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
-  EXPECT_TRUE(performer_.Write(&major_version, 8));
+  EXPECT_TRUE(
+      performer_.Write(&major_version, PayloadMetadata::kDeltaVersionSize));
 
   uint64_t manifest_size = rand_r(&seed) % 256;
-  uint64_t manifest_size_be = htobe64(manifest_size);
-  EXPECT_TRUE(performer_.Write(&manifest_size_be, 8));
-
   uint32_t metadata_signature_size = rand_r(&seed) % 256;
+
+  // The payload size has to be bigger than the |metadata_size| and
+  // |metadata_signature_size|
+  payload_.size = PayloadMetadata::kDeltaManifestSizeOffset +
+                  PayloadMetadata::kDeltaManifestSizeSize +
+                  PayloadMetadata::kDeltaMetadataSignatureSizeSize +
+                  manifest_size + metadata_signature_size + 1;
+
+  uint64_t manifest_size_be = htobe64(manifest_size);
+  EXPECT_TRUE(performer_.Write(&manifest_size_be,
+                               PayloadMetadata::kDeltaManifestSizeSize));
+
   uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
-  EXPECT_TRUE(performer_.Write(&metadata_signature_size_be, 4));
+  EXPECT_TRUE(
+      performer_.Write(&metadata_signature_size_be,
+                       PayloadMetadata::kDeltaMetadataSignatureSizeSize));
 
   EXPECT_LT(performer_.Close(), 0);
 
@@ -915,10 +934,74 @@
   EXPECT_EQ(metadata_signature_size, performer_.metadata_signature_size_);
 }
 
+TEST_F(DeltaPerformerTest, BrilloMetadataSizeNOKTest) {
+  unsigned int seed = time(nullptr);
+  EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
+
+  uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
+  EXPECT_TRUE(
+      performer_.Write(&major_version, PayloadMetadata::kDeltaVersionSize));
+
+  uint64_t manifest_size = UINT64_MAX - 600;  // Subtract to avoid wrap around.
+  uint64_t manifest_offset = PayloadMetadata::kDeltaManifestSizeOffset +
+                             PayloadMetadata::kDeltaManifestSizeSize +
+                             PayloadMetadata::kDeltaMetadataSignatureSizeSize;
+  payload_.metadata_size = manifest_offset + manifest_size;
+  uint32_t metadata_signature_size = rand_r(&seed) % 256;
+
+  // The payload size is greater than the payload header but smaller than
+  // |metadata_signature_size| + |metadata_size|
+  payload_.size = manifest_offset + metadata_signature_size + 1;
+
+  uint64_t manifest_size_be = htobe64(manifest_size);
+  EXPECT_TRUE(performer_.Write(&manifest_size_be,
+                               PayloadMetadata::kDeltaManifestSizeSize));
+  uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
+
+  ErrorCode error;
+  EXPECT_FALSE(
+      performer_.Write(&metadata_signature_size_be,
+                       PayloadMetadata::kDeltaMetadataSignatureSizeSize + 1,
+                       &error));
+
+  EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error);
+}
+
+TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeNOKTest) {
+  unsigned int seed = time(nullptr);
+  EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
+
+  uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
+  EXPECT_TRUE(
+      performer_.Write(&major_version, PayloadMetadata::kDeltaVersionSize));
+
+  uint64_t manifest_size = rand_r(&seed) % 256;
+  // Subtract from UINT32_MAX to avoid wrap around.
+  uint32_t metadata_signature_size = UINT32_MAX - 600;
+
+  // The payload size is greater than |manifest_size| but smaller than
+  // |metadata_signature_size|
+  payload_.size = manifest_size + 1;
+
+  uint64_t manifest_size_be = htobe64(manifest_size);
+  EXPECT_TRUE(performer_.Write(&manifest_size_be,
+                               PayloadMetadata::kDeltaManifestSizeSize));
+
+  uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
+  ErrorCode error;
+  EXPECT_FALSE(
+      performer_.Write(&metadata_signature_size_be,
+                       PayloadMetadata::kDeltaMetadataSignatureSizeSize + 1,
+                       &error));
+
+  EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error);
+}
+
 TEST_F(DeltaPerformerTest, BrilloParsePayloadMetadataTest) {
   brillo::Blob payload_data = GeneratePayload(
       {}, {}, true, kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion);
   install_plan_.hash_checks_mandatory = true;
+  payload_.size = payload_data.size();
   ErrorCode error;
   EXPECT_EQ(MetadataParseResult::kSuccess,
             performer_.ParsePayloadMetadata(payload_data, &error));
diff --git a/payload_consumer/download_action.cc b/payload_consumer/download_action.cc
index 09afc42..45df5a9 100644
--- a/payload_consumer/download_action.cc
+++ b/payload_consumer/download_action.cc
@@ -56,9 +56,6 @@
       delegate_(nullptr),
       p2p_sharing_fd_(-1),
       p2p_visible_(true) {
-#if BASE_VER < 576279
-  base::StatisticsRecorder::Initialize();
-#endif
 }
 
 DownloadAction::~DownloadAction() {}
diff --git a/payload_consumer/filesystem_verifier_action.cc b/payload_consumer/filesystem_verifier_action.cc
index 36e5a35..f9e7f81 100644
--- a/payload_consumer/filesystem_verifier_action.cc
+++ b/payload_consumer/filesystem_verifier_action.cc
@@ -57,6 +57,7 @@
     abort_action_completer.set_code(ErrorCode::kSuccess);
     return;
   }
+  install_plan_.Dump();
 
   StartPartitionHashing();
   abort_action_completer.set_should_complete(false);
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 766b27c..a313627 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -29,6 +29,11 @@
 
 namespace chromeos_update_engine {
 
+string PayloadUrlsToString(
+    const decltype(InstallPlan::Payload::payload_urls)& payload_urls) {
+  return "(" + base::JoinString(payload_urls, ",") + ")";
+}
+
 string InstallPayloadTypeToString(InstallPayloadType type) {
   switch (type) {
     case InstallPayloadType::kUnknown:
@@ -66,8 +71,9 @@
   string payloads_str;
   for (const auto& payload : payloads) {
     payloads_str += base::StringPrintf(
-        ", payload: (size: %" PRIu64 ", metadata_size: %" PRIu64
+        ", payload: (urls: %s, size: %" PRIu64 ", metadata_size: %" PRIu64
         ", metadata signature: %s, hash: %s, payload type: %s)",
+        PayloadUrlsToString(payload.payload_urls).c_str(),
         payload.size,
         payload.metadata_size,
         payload.metadata_signature.c_str(),
@@ -92,8 +98,8 @@
             << version_str
             << ", source_slot: " << BootControlInterface::SlotName(source_slot)
             << ", target_slot: " << BootControlInterface::SlotName(target_slot)
-            << ", url: " << url_str << payloads_str << partitions_str
-            << ", hash_checks_mandatory: "
+            << ", initial url: " << url_str << payloads_str
+            << partitions_str << ", hash_checks_mandatory: "
             << utils::ToString(hash_checks_mandatory)
             << ", powerwash_required: " << utils::ToString(powerwash_required)
             << ", switch_slot_on_reboot: "
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index ede36b3..7a95ab4 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -58,6 +58,7 @@
   std::string system_version;
 
   struct Payload {
+    std::vector<std::string> payload_urls;  // URLs to download the payload
     uint64_t size = 0;               // size of the payload
     uint64_t metadata_size = 0;      // size of the metadata
     std::string metadata_signature;  // signature of the metadata in base64
@@ -69,7 +70,8 @@
     bool already_applied = false;
 
     bool operator==(const Payload& that) const {
-      return size == that.size && metadata_size == that.metadata_size &&
+      return payload_urls == that.payload_urls && size == that.size &&
+             metadata_size == that.metadata_size &&
              metadata_signature == that.metadata_signature &&
              hash == that.hash && type == that.type &&
              already_applied == that.already_applied;
@@ -146,6 +148,9 @@
   // True if this update is a rollback.
   bool is_rollback{false};
 
+  // True if this rollback should preserve some system data.
+  bool rollback_data_save_requested{false};
+
   // True if the update should write verity.
   // False otherwise.
   bool write_verity{true};
diff --git a/payload_consumer/mtd_file_descriptor.cc b/payload_consumer/mtd_file_descriptor.cc
deleted file mode 100644
index 5d940cb..0000000
--- a/payload_consumer/mtd_file_descriptor.cc
+++ /dev/null
@@ -1,263 +0,0 @@
-//
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "update_engine/payload_consumer/mtd_file_descriptor.h"
-
-#include <fcntl.h>
-#include <mtd/ubi-user.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <memory>
-#include <string>
-
-#include <base/files/file_path.h>
-#include <base/strings/string_number_conversions.h>
-#include <base/strings/string_util.h>
-#include <base/strings/stringprintf.h>
-
-#include "update_engine/common/subprocess.h"
-#include "update_engine/common/utils.h"
-
-using std::string;
-
-namespace {
-
-static const char kSysfsClassUbi[] = "/sys/class/ubi/";
-static const char kUsableEbSize[] = "/usable_eb_size";
-static const char kReservedEbs[] = "/reserved_ebs";
-
-using chromeos_update_engine::UbiVolumeInfo;
-using chromeos_update_engine::utils::ReadFile;
-
-// Return a UbiVolumeInfo pointer if |path| is a UBI volume. Otherwise, return
-// a null unique pointer.
-std::unique_ptr<UbiVolumeInfo> GetUbiVolumeInfo(const string& path) {
-  base::FilePath device_node(path);
-  base::FilePath ubi_name(device_node.BaseName());
-
-  string sysfs_node(kSysfsClassUbi);
-  sysfs_node.append(ubi_name.MaybeAsASCII());
-
-  std::unique_ptr<UbiVolumeInfo> ret;
-
-  // Obtain volume info from sysfs.
-  string s_reserved_ebs;
-  if (!ReadFile(sysfs_node + kReservedEbs, &s_reserved_ebs)) {
-    LOG(ERROR) << "Cannot read " << sysfs_node + kReservedEbs;
-    return ret;
-  }
-  string s_eb_size;
-  if (!ReadFile(sysfs_node + kUsableEbSize, &s_eb_size)) {
-    LOG(ERROR) << "Cannot read " << sysfs_node + kUsableEbSize;
-    return ret;
-  }
-
-  base::TrimWhitespaceASCII(
-      s_reserved_ebs, base::TRIM_TRAILING, &s_reserved_ebs);
-  base::TrimWhitespaceASCII(s_eb_size, base::TRIM_TRAILING, &s_eb_size);
-
-  uint64_t reserved_ebs, eb_size;
-  if (!base::StringToUint64(s_reserved_ebs, &reserved_ebs)) {
-    LOG(ERROR) << "Cannot parse reserved_ebs: " << s_reserved_ebs;
-    return ret;
-  }
-  if (!base::StringToUint64(s_eb_size, &eb_size)) {
-    LOG(ERROR) << "Cannot parse usable_eb_size: " << s_eb_size;
-    return ret;
-  }
-
-  ret.reset(new UbiVolumeInfo);
-  ret->reserved_ebs = reserved_ebs;
-  ret->eraseblock_size = eb_size;
-  return ret;
-}
-
-}  // namespace
-
-namespace chromeos_update_engine {
-
-MtdFileDescriptor::MtdFileDescriptor()
-    : read_ctx_(nullptr, &mtd_read_close),
-      write_ctx_(nullptr, &mtd_write_close) {}
-
-bool MtdFileDescriptor::IsMtd(const char* path) {
-  uint64_t size;
-  return mtd_node_info(path, &size, nullptr, nullptr) == 0;
-}
-
-bool MtdFileDescriptor::Open(const char* path, int flags, mode_t mode) {
-  // This File Descriptor does not support read and write.
-  TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
-  // But we need to open the underlying file descriptor in O_RDWR mode because
-  // during write, we need to read back to verify the write actually sticks or
-  // we have to skip the block. That job is done by mtdutils library.
-  if ((flags & O_ACCMODE) == O_WRONLY) {
-    flags &= ~O_ACCMODE;
-    flags |= O_RDWR;
-  }
-  TEST_AND_RETURN_FALSE(
-      EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
-
-  if ((flags & O_ACCMODE) == O_RDWR) {
-    write_ctx_.reset(mtd_write_descriptor(fd_, path));
-    nr_written_ = 0;
-  } else {
-    read_ctx_.reset(mtd_read_descriptor(fd_, path));
-  }
-
-  if (!read_ctx_ && !write_ctx_) {
-    Close();
-    return false;
-  }
-
-  return true;
-}
-
-bool MtdFileDescriptor::Open(const char* path, int flags) {
-  mode_t cur = umask(022);
-  umask(cur);
-  return Open(path, flags, 0777 & ~cur);
-}
-
-ssize_t MtdFileDescriptor::Read(void* buf, size_t count) {
-  CHECK(read_ctx_);
-  return mtd_read_data(read_ctx_.get(), static_cast<char*>(buf), count);
-}
-
-ssize_t MtdFileDescriptor::Write(const void* buf, size_t count) {
-  CHECK(write_ctx_);
-  ssize_t result =
-      mtd_write_data(write_ctx_.get(), static_cast<const char*>(buf), count);
-  if (result > 0) {
-    nr_written_ += result;
-  }
-  return result;
-}
-
-off64_t MtdFileDescriptor::Seek(off64_t offset, int whence) {
-  if (write_ctx_) {
-    // Ignore seek in write mode.
-    return nr_written_;
-  }
-  return EintrSafeFileDescriptor::Seek(offset, whence);
-}
-
-bool MtdFileDescriptor::Close() {
-  read_ctx_.reset();
-  write_ctx_.reset();
-  return EintrSafeFileDescriptor::Close();
-}
-
-bool UbiFileDescriptor::IsUbi(const char* path) {
-  base::FilePath device_node(path);
-  base::FilePath ubi_name(device_node.BaseName());
-  TEST_AND_RETURN_FALSE(base::StartsWith(
-      ubi_name.MaybeAsASCII(), "ubi", base::CompareCase::SENSITIVE));
-
-  return static_cast<bool>(GetUbiVolumeInfo(path));
-}
-
-bool UbiFileDescriptor::Open(const char* path, int flags, mode_t mode) {
-  std::unique_ptr<UbiVolumeInfo> info = GetUbiVolumeInfo(path);
-  if (!info) {
-    return false;
-  }
-
-  // This File Descriptor does not support read and write.
-  TEST_AND_RETURN_FALSE((flags & O_ACCMODE) != O_RDWR);
-  TEST_AND_RETURN_FALSE(
-      EintrSafeFileDescriptor::Open(path, flags | O_CLOEXEC, mode));
-
-  usable_eb_blocks_ = info->reserved_ebs;
-  eraseblock_size_ = info->eraseblock_size;
-  volume_size_ = usable_eb_blocks_ * eraseblock_size_;
-
-  if ((flags & O_ACCMODE) == O_WRONLY) {
-    // It's best to use volume update ioctl so that UBI layer will mark the
-    // volume as being updated, and only clear that mark if the update is
-    // successful. We will need to pad to the whole volume size at close.
-    uint64_t vsize = volume_size_;
-    if (ioctl(fd_, UBI_IOCVOLUP, &vsize) != 0) {
-      PLOG(ERROR) << "Cannot issue volume update ioctl";
-      EintrSafeFileDescriptor::Close();
-      return false;
-    }
-    mode_ = kWriteOnly;
-    nr_written_ = 0;
-  } else {
-    mode_ = kReadOnly;
-  }
-
-  return true;
-}
-
-bool UbiFileDescriptor::Open(const char* path, int flags) {
-  mode_t cur = umask(022);
-  umask(cur);
-  return Open(path, flags, 0777 & ~cur);
-}
-
-ssize_t UbiFileDescriptor::Read(void* buf, size_t count) {
-  CHECK(mode_ == kReadOnly);
-  return EintrSafeFileDescriptor::Read(buf, count);
-}
-
-ssize_t UbiFileDescriptor::Write(const void* buf, size_t count) {
-  CHECK(mode_ == kWriteOnly);
-  ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, count);
-  if (nr_chunk >= 0) {
-    nr_written_ += nr_chunk;
-  }
-  return nr_chunk;
-}
-
-off64_t UbiFileDescriptor::Seek(off64_t offset, int whence) {
-  if (mode_ == kWriteOnly) {
-    // Ignore seek in write mode.
-    return nr_written_;
-  }
-  return EintrSafeFileDescriptor::Seek(offset, whence);
-}
-
-bool UbiFileDescriptor::Close() {
-  bool pad_ok = true;
-  if (IsOpen() && mode_ == kWriteOnly) {
-    char buf[1024];
-    memset(buf, 0xFF, sizeof(buf));
-    while (nr_written_ < volume_size_) {
-      // We have written less than the whole volume. In order for us to clear
-      // the update marker, we need to fill the rest. It is recommended to fill
-      // UBI writes with 0xFF.
-      uint64_t to_write = volume_size_ - nr_written_;
-      if (to_write > sizeof(buf)) {
-        to_write = sizeof(buf);
-      }
-      ssize_t nr_chunk = EintrSafeFileDescriptor::Write(buf, to_write);
-      if (nr_chunk < 0) {
-        LOG(ERROR) << "Cannot 0xFF-pad before closing.";
-        // There is an error, but we can't really do any meaningful thing here.
-        pad_ok = false;
-        break;
-      }
-      nr_written_ += nr_chunk;
-    }
-  }
-  return EintrSafeFileDescriptor::Close() && pad_ok;
-}
-
-}  // namespace chromeos_update_engine
diff --git a/payload_consumer/mtd_file_descriptor.h b/payload_consumer/mtd_file_descriptor.h
deleted file mode 100644
index c0170b7..0000000
--- a/payload_consumer/mtd_file_descriptor.h
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_MTD_FILE_DESCRIPTOR_H_
-#define UPDATE_ENGINE_PAYLOAD_CONSUMER_MTD_FILE_DESCRIPTOR_H_
-
-// This module defines file descriptors that deal with NAND media. We are
-// concerned with raw NAND access (as MTD device), and through UBI layer.
-
-#include <memory>
-
-#include <mtdutils.h>
-
-#include "update_engine/payload_consumer/file_descriptor.h"
-
-namespace chromeos_update_engine {
-
-// A class defining the file descriptor API for raw MTD device. This file
-// descriptor supports either random read, or sequential write but not both at
-// once.
-class MtdFileDescriptor : public EintrSafeFileDescriptor {
- public:
-  MtdFileDescriptor();
-
-  static bool IsMtd(const char* path);
-
-  bool Open(const char* path, int flags, mode_t mode) override;
-  bool Open(const char* path, int flags) override;
-  ssize_t Read(void* buf, size_t count) override;
-  ssize_t Write(const void* buf, size_t count) override;
-  off64_t Seek(off64_t offset, int whence) override;
-  uint64_t BlockDevSize() override { return 0; }
-  bool BlkIoctl(int request,
-                uint64_t start,
-                uint64_t length,
-                int* result) override {
-    return false;
-  }
-  bool Close() override;
-
- private:
-  std::unique_ptr<MtdReadContext, decltype(&mtd_read_close)> read_ctx_;
-  std::unique_ptr<MtdWriteContext, decltype(&mtd_write_close)> write_ctx_;
-  uint64_t nr_written_;
-};
-
-struct UbiVolumeInfo {
-  // Number of eraseblocks.
-  uint64_t reserved_ebs;
-  // Size of each eraseblock.
-  uint64_t eraseblock_size;
-};
-
-// A file descriptor to update a UBI volume, similar to MtdFileDescriptor.
-// Once the file descriptor is opened for write, the volume is marked as being
-// updated. The volume will not be usable until an update is completed. See
-// UBI_IOCVOLUP ioctl operation.
-class UbiFileDescriptor : public EintrSafeFileDescriptor {
- public:
-  // Perform some queries about |path| to see if it is a UBI volume.
-  static bool IsUbi(const char* path);
-
-  bool Open(const char* path, int flags, mode_t mode) override;
-  bool Open(const char* path, int flags) override;
-  ssize_t Read(void* buf, size_t count) override;
-  ssize_t Write(const void* buf, size_t count) override;
-  off64_t Seek(off64_t offset, int whence) override;
-  uint64_t BlockDevSize() override { return 0; }
-  bool BlkIoctl(int request,
-                uint64_t start,
-                uint64_t length,
-                int* result) override {
-    return false;
-  }
-  bool Close() override;
-
- private:
-  enum Mode { kReadOnly, kWriteOnly };
-
-  uint64_t usable_eb_blocks_;
-  uint64_t eraseblock_size_;
-  uint64_t volume_size_;
-  uint64_t nr_written_;
-
-  Mode mode_;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_PAYLOAD_CONSUMER_MTD_FILE_DESCRIPTOR_H_
diff --git a/payload_consumer/payload_constants.cc b/payload_consumer/payload_constants.cc
index a2368a4..1c987bd 100644
--- a/payload_consumer/payload_constants.cc
+++ b/payload_consumer/payload_constants.cc
@@ -16,24 +16,26 @@
 
 #include "update_engine/payload_consumer/payload_constants.h"
 
+#include <base/logging.h>
+
 namespace chromeos_update_engine {
 
-const uint64_t kChromeOSMajorPayloadVersion = 1;
+// const uint64_t kChromeOSMajorPayloadVersion = 1;  DEPRECATED
 const uint64_t kBrilloMajorPayloadVersion = 2;
 
-const uint32_t kMinSupportedMinorPayloadVersion = 1;
-const uint32_t kMaxSupportedMinorPayloadVersion = 6;
+const uint64_t kMinSupportedMajorPayloadVersion = kBrilloMajorPayloadVersion;
+const uint64_t kMaxSupportedMajorPayloadVersion = kBrilloMajorPayloadVersion;
 
 const uint32_t kFullPayloadMinorVersion = 0;
-const uint32_t kInPlaceMinorPayloadVersion = 1;
+// const uint32_t kInPlaceMinorPayloadVersion = 1;  DEPRECATED
 const uint32_t kSourceMinorPayloadVersion = 2;
 const uint32_t kOpSrcHashMinorPayloadVersion = 3;
 const uint32_t kBrotliBsdiffMinorPayloadVersion = 4;
 const uint32_t kPuffdiffMinorPayloadVersion = 5;
 const uint32_t kVerityMinorPayloadVersion = 6;
 
-const uint64_t kMinSupportedMajorPayloadVersion = 1;
-const uint64_t kMaxSupportedMajorPayloadVersion = 2;
+const uint32_t kMinSupportedMinorPayloadVersion = kSourceMinorPayloadVersion;
+const uint32_t kMaxSupportedMinorPayloadVersion = kVerityMinorPayloadVersion;
 
 const uint64_t kMaxPayloadHeaderSize = 24;
 
@@ -44,10 +46,6 @@
 
 const char* InstallOperationTypeName(InstallOperation::Type op_type) {
   switch (op_type) {
-    case InstallOperation::BSDIFF:
-      return "BSDIFF";
-    case InstallOperation::MOVE:
-      return "MOVE";
     case InstallOperation::REPLACE:
       return "REPLACE";
     case InstallOperation::REPLACE_BZ:
@@ -66,6 +64,10 @@
       return "PUFFDIFF";
     case InstallOperation::BROTLI_BSDIFF:
       return "BROTLI_BSDIFF";
+
+    case InstallOperation::BSDIFF:
+    case InstallOperation::MOVE:
+      NOTREACHED();
   }
   return "<unknown_op>";
 }
diff --git a/payload_consumer/payload_constants.h b/payload_consumer/payload_constants.h
index 1642488..5c2d17c 100644
--- a/payload_consumer/payload_constants.h
+++ b/payload_consumer/payload_constants.h
@@ -26,7 +26,7 @@
 namespace chromeos_update_engine {
 
 // The major version used by Chrome OS.
-extern const uint64_t kChromeOSMajorPayloadVersion;
+// extern const uint64_t kChromeOSMajorPayloadVersion;  DEPRECATED
 
 // The major version used by Brillo.
 extern const uint64_t kBrilloMajorPayloadVersion;
@@ -39,7 +39,7 @@
 extern const uint32_t kFullPayloadMinorVersion;
 
 // The minor version used by the in-place delta generator algorithm.
-extern const uint32_t kInPlaceMinorPayloadVersion;
+// extern const uint32_t kInPlaceMinorPayloadVersion;  DEPRECATED
 
 // The minor version used by the A to B delta generator algorithm.
 extern const uint32_t kSourceMinorPayloadVersion;
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc
index 0952646..01f3b62 100644
--- a/payload_consumer/payload_metadata.cc
+++ b/payload_consumer/payload_metadata.cc
@@ -37,34 +37,18 @@
 const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
 const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
 
-bool PayloadMetadata::GetMetadataSignatureSizeOffset(
-    uint64_t* out_offset) const {
-  if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
-    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
-    return true;
-  }
-  return false;
+uint64_t PayloadMetadata::GetMetadataSignatureSizeOffset() const {
+  return kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
 }
 
-bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const {
-  // Actual manifest begins right after the manifest size field or
-  // metadata signature size field if major version >= 2.
-  if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
-    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
-    return true;
-  }
-  if (major_payload_version_ == kBrilloMajorPayloadVersion) {
-    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
-                  kDeltaMetadataSignatureSizeSize;
-    return true;
-  }
-  LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
-  return false;
+uint64_t PayloadMetadata::GetManifestOffset() const {
+  // Actual manifest begins right after the metadata signature size field.
+  return kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
+         kDeltaMetadataSignatureSizeSize;
 }
 
 MetadataParseResult PayloadMetadata::ParsePayloadHeader(
     const brillo::Blob& payload, ErrorCode* error) {
-  uint64_t manifest_offset;
   // Ensure we have data to cover the major payload version.
   if (payload.size() < kDeltaManifestSizeOffset)
     return MetadataParseResult::kInsufficientData;
@@ -76,6 +60,11 @@
     return MetadataParseResult::kError;
   }
 
+  uint64_t manifest_offset = GetManifestOffset();
+  // Check again with the manifest offset.
+  if (payload.size() < manifest_offset)
+    return MetadataParseResult::kInsufficientData;
+
   // Extract the payload version from the metadata.
   static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
                 "Major payload version size mismatch");
@@ -93,15 +82,6 @@
     return MetadataParseResult::kError;
   }
 
-  // Get the manifest offset now that we have payload version.
-  if (!GetManifestOffset(&manifest_offset)) {
-    *error = ErrorCode::kUnsupportedMajorPayloadVersion;
-    return MetadataParseResult::kError;
-  }
-  // Check again with the manifest offset.
-  if (payload.size() < manifest_offset)
-    return MetadataParseResult::kInsufficientData;
-
   // Next, parse the manifest size.
   static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
                 "manifest_size size mismatch");
@@ -113,30 +93,26 @@
   metadata_size_ = manifest_offset + manifest_size_;
   if (metadata_size_ < manifest_size_) {
     // Overflow detected.
+    LOG(ERROR) << "Overflow detected on manifest size.";
     *error = ErrorCode::kDownloadInvalidMetadataSize;
     return MetadataParseResult::kError;
   }
 
-  if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
-    // Parse the metadata signature size.
-    static_assert(
-        sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
-        "metadata_signature_size size mismatch");
-    uint64_t metadata_signature_size_offset;
-    if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
-      *error = ErrorCode::kError;
-      return MetadataParseResult::kError;
-    }
-    memcpy(&metadata_signature_size_,
-           &payload[metadata_signature_size_offset],
-           kDeltaMetadataSignatureSizeSize);
-    metadata_signature_size_ = be32toh(metadata_signature_size_);
+  // Parse the metadata signature size.
+  static_assert(
+      sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
+      "metadata_signature_size size mismatch");
+  uint64_t metadata_signature_size_offset = GetMetadataSignatureSizeOffset();
+  memcpy(&metadata_signature_size_,
+         &payload[metadata_signature_size_offset],
+         kDeltaMetadataSignatureSizeSize);
+  metadata_signature_size_ = be32toh(metadata_signature_size_);
 
-    if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
-      // Overflow detected.
-      *error = ErrorCode::kDownloadInvalidMetadataSize;
-      return MetadataParseResult::kError;
-    }
+  if (metadata_size_ + metadata_signature_size_ < metadata_size_) {
+    // Overflow detected.
+    LOG(ERROR) << "Overflow detected on metadata and signature size.";
+    *error = ErrorCode::kDownloadInvalidMetadataSize;
+    return MetadataParseResult::kError;
   }
   return MetadataParseResult::kSuccess;
 }
@@ -148,9 +124,7 @@
 
 bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
                                   DeltaArchiveManifest* out_manifest) const {
-  uint64_t manifest_offset;
-  if (!GetManifestOffset(&manifest_offset))
-    return false;
+  uint64_t manifest_offset = GetManifestOffset();
   CHECK_GE(payload.size(), manifest_offset + manifest_size_);
   return out_manifest->ParseFromArray(&payload[manifest_offset],
                                       manifest_size_);
@@ -176,7 +150,7 @@
                  << metadata_signature;
       return ErrorCode::kDownloadMetadataSignatureError;
     }
-  } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+  } else {
     metadata_signature_protobuf.assign(
         payload.begin() + metadata_size_,
         payload.begin() + metadata_size_ + metadata_signature_size_);
@@ -225,4 +199,32 @@
   return ErrorCode::kSuccess;
 }
 
+bool PayloadMetadata::ParsePayloadFile(const string& payload_path,
+                                       DeltaArchiveManifest* manifest,
+                                       Signatures* metadata_signatures) {
+  brillo::Blob payload;
+  TEST_AND_RETURN_FALSE(
+      utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload));
+  TEST_AND_RETURN_FALSE(ParsePayloadHeader(payload));
+
+  if (manifest != nullptr) {
+    TEST_AND_RETURN_FALSE(
+        utils::ReadFileChunk(payload_path,
+                             kMaxPayloadHeaderSize,
+                             GetMetadataSize() - kMaxPayloadHeaderSize,
+                             &payload));
+    TEST_AND_RETURN_FALSE(GetManifest(payload, manifest));
+  }
+
+  if (metadata_signatures != nullptr) {
+    payload.clear();
+    TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
+        payload_path, GetMetadataSize(), GetMetadataSignatureSize(), &payload));
+    TEST_AND_RETURN_FALSE(
+        metadata_signatures->ParseFromArray(payload.data(), payload.size()));
+  }
+
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/payload_metadata.h b/payload_consumer/payload_metadata.h
index 75ef8f9..cc42253 100644
--- a/payload_consumer/payload_metadata.h
+++ b/payload_consumer/payload_metadata.h
@@ -88,15 +88,20 @@
   bool GetManifest(const brillo::Blob& payload,
                    DeltaArchiveManifest* out_manifest) const;
 
- private:
-  // Set |*out_offset| to the byte offset at which the manifest protobuf begins
-  // in a payload. Return true on success, false if the offset is unknown.
-  bool GetManifestOffset(uint64_t* out_offset) const;
+  // Parses a payload file |payload_path| and prepares the metadata properties,
+  // manifest and metadata signatures. Can be used as an easy to use utility to
+  // get the payload information without manually the process.
+  bool ParsePayloadFile(const std::string& payload_path,
+                        DeltaArchiveManifest* manifest,
+                        Signatures* metadata_signatures);
 
-  // Set |*out_offset| to the byte offset where the size of the metadata
-  // signature is stored in a payload. Return true on success, if this field is
-  // not present in the payload, return false.
-  bool GetMetadataSignatureSizeOffset(uint64_t* out_offset) const;
+ private:
+  // Returns the byte offset at which the manifest protobuf begins in a payload.
+  uint64_t GetManifestOffset() const;
+
+  // Returns the byte offset where the size of the metadata signature is stored
+  // in a payload.
+  uint64_t GetMetadataSignatureSizeOffset() const;
 
   uint64_t metadata_size_{0};
   uint64_t manifest_size_{0};
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index c08cfc2..c520c7e 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -28,6 +28,7 @@
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/logging.h>
+#include <base/stl_util.h>
 #include <base/strings/string_split.h>
 #include <base/strings/string_util.h>
 
@@ -57,9 +58,14 @@
   CHECK(HasInputObject());
   install_plan_ = GetInputObject();
 
-  // Currently we're always powerwashing when rolling back.
+  // We always powerwash when rolling back, however policy can determine
+  // if this is a full/normal powerwash, or a special rollback powerwash
+  // that retains a small amount of system state such as enrollment and
+  // network configuration. In both cases all user accounts are deleted.
   if (install_plan_.powerwash_required || install_plan_.is_rollback) {
-    if (hardware_->SchedulePowerwash(install_plan_.is_rollback)) {
+    bool save_rollback_data =
+        install_plan_.is_rollback && install_plan_.rollback_data_save_requested;
+    if (hardware_->SchedulePowerwash(save_rollback_data)) {
       powerwash_scheduled_ = true;
     } else {
       return CompletePostinstall(ErrorCode::kPostinstallPowerwashError);
@@ -108,8 +114,7 @@
   const InstallPlan::Partition& partition =
       install_plan_.partitions[current_partition_];
 
-  const string mountable_device =
-      utils::MakePartitionNameForMount(partition.target_path);
+  const string mountable_device = partition.target_path;
   if (mountable_device.empty()) {
     LOG(ERROR) << "Cannot make mountable device from " << partition.target_path;
     return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
@@ -215,6 +220,7 @@
     PLOG(ERROR) << "Unable to set non-blocking I/O mode on fd " << progress_fd_;
   }
 
+#ifdef __ANDROID__
   progress_task_ = MessageLoop::current()->WatchFileDescriptor(
       FROM_HERE,
       progress_fd_,
@@ -222,6 +228,13 @@
       true,
       base::Bind(&PostinstallRunnerAction::OnProgressFdReady,
                  base::Unretained(this)));
+#else
+  progress_controller_ = base::FileDescriptorWatcher::WatchReadable(
+      progress_fd_,
+      base::BindRepeating(&PostinstallRunnerAction::OnProgressFdReady,
+                          base::Unretained(this)));
+#endif  // __ANDROID__
+
 }
 
 void PostinstallRunnerAction::OnProgressFdReady() {
@@ -231,7 +244,7 @@
     bytes_read = 0;
     bool eof;
     bool ok =
-        utils::ReadAll(progress_fd_, buf, arraysize(buf), &bytes_read, &eof);
+        utils::ReadAll(progress_fd_, buf, base::size(buf), &bytes_read, &eof);
     progress_buffer_.append(buf, bytes_read);
     // Process every line.
     vector<string> lines = base::SplitString(
@@ -246,8 +259,12 @@
     if (!ok || eof) {
       // There was either an error or an EOF condition, so we are done watching
       // the file descriptor.
+#ifdef __ANDROID__
       MessageLoop::current()->CancelTask(progress_task_);
       progress_task_ = MessageLoop::kTaskIdNull;
+#else
+      progress_controller_.reset();
+#endif  // __ANDROID__
       return;
     }
   } while (bytes_read);
@@ -291,10 +308,15 @@
   fs_mount_dir_.clear();
 
   progress_fd_ = -1;
+#ifdef __ANDROID__
   if (progress_task_ != MessageLoop::kTaskIdNull) {
     MessageLoop::current()->CancelTask(progress_task_);
     progress_task_ = MessageLoop::kTaskIdNull;
   }
+#else
+  progress_controller_.reset();
+#endif  // __ANDROID__
+
   progress_buffer_.clear();
 }
 
diff --git a/payload_consumer/postinstall_runner_action.h b/payload_consumer/postinstall_runner_action.h
index b9b7069..e5dfc40 100644
--- a/payload_consumer/postinstall_runner_action.h
+++ b/payload_consumer/postinstall_runner_action.h
@@ -17,9 +17,11 @@
 #ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_POSTINSTALL_RUNNER_ACTION_H_
 #define UPDATE_ENGINE_PAYLOAD_CONSUMER_POSTINSTALL_RUNNER_ACTION_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
+#include <base/files/file_descriptor_watcher_posix.h>
 #include <brillo/message_loops/message_loop.h>
 #include <gtest/gtest_prod.h>
 
@@ -139,7 +141,12 @@
   // The parent progress file descriptor used to watch for progress reports from
   // the postinstall program and the task watching for them.
   int progress_fd_{-1};
+
+#ifdef __ANDROID__
   brillo::MessageLoop::TaskId progress_task_{brillo::MessageLoop::kTaskIdNull};
+#else
+  std::unique_ptr<base::FileDescriptorWatcher::Controller> progress_controller_;
+#endif  // __ANDROID__
 
   // A buffer of a partial read line from the progress file descriptor.
   std::string progress_buffer_;
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index e9313f1..0041d31 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -100,7 +100,8 @@
   void RunPostinstallAction(const string& device_path,
                             const string& postinstall_program,
                             bool powerwash_required,
-                            bool is_rollback);
+                            bool is_rollback,
+                            bool save_rollback_data);
 
   void RunPostinstallActionWithInstallPlan(const InstallPlan& install_plan);
 
@@ -143,7 +144,14 @@
           base::TimeDelta::FromMilliseconds(10));
     } else {
       CHECK(processor_);
-      processor_->StopProcessing();
+      // Must |PostDelayedTask()| here to be safe that |FileDescriptorWatcher|
+      // doesn't leak memory, do not directly call |StopProcessing()|.
+      loop_.PostDelayedTask(
+          FROM_HERE,
+          base::Bind(
+              [](ActionProcessor* processor) { processor->StopProcessing(); },
+              base::Unretained(processor_)),
+          base::TimeDelta::FromMilliseconds(100));
     }
   }
 
@@ -172,7 +180,8 @@
     const string& device_path,
     const string& postinstall_program,
     bool powerwash_required,
-    bool is_rollback) {
+    bool is_rollback,
+    bool save_rollback_data) {
   InstallPlan::Partition part;
   part.name = "part";
   part.target_path = device_path;
@@ -183,6 +192,7 @@
   install_plan.download_url = "http://127.0.0.1:8080/update";
   install_plan.powerwash_required = powerwash_required;
   install_plan.is_rollback = is_rollback;
+  install_plan.rollback_data_save_requested = save_rollback_data;
   RunPostinstallActionWithInstallPlan(install_plan);
 }
 
@@ -256,7 +266,8 @@
 TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
 
-  RunPostinstallAction(loop.dev(), kPostinstallDefaultScript, false, false);
+  RunPostinstallAction(
+      loop.dev(), kPostinstallDefaultScript, false, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
   EXPECT_TRUE(processor_delegate_.processing_done_called_);
 
@@ -267,7 +278,7 @@
 
 TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_link", false, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 
@@ -277,6 +288,7 @@
   RunPostinstallAction(loop.dev(),
                        "bin/postinst_example",
                        /*powerwash_required=*/true,
+                       false,
                        false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 
@@ -285,14 +297,31 @@
   EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
 }
 
-TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTest) {
+TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTestNoDataSave) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
 
   // Run a simple postinstall program, rollback happened.
   RunPostinstallAction(loop.dev(),
                        "bin/postinst_example",
                        false,
-                       /*is_rollback=*/true);
+                       /*is_rollback=*/true,
+                       /*save_rollback_data=*/false);
+  EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
+
+  // Check that powerwash was scheduled and that it's NOT a rollback powerwash.
+  EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled());
+  EXPECT_FALSE(fake_hardware_.GetIsRollbackPowerwashScheduled());
+}
+
+TEST_F(PostinstallRunnerActionTest, RunAsRootRollbackTestWithDataSave) {
+  ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
+
+  // Run a simple postinstall program, rollback happened.
+  RunPostinstallAction(loop.dev(),
+                       "bin/postinst_example",
+                       false,
+                       /*is_rollback=*/true,
+                       /*save_rollback_data=*/true);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 
   // Check that powerwash was scheduled and that it's a rollback powerwash.
@@ -303,7 +332,8 @@
 // Runs postinstall from a partition file that doesn't mount, so it should
 // fail.
 TEST_F(PostinstallRunnerActionTest, RunAsRootCantMountTest) {
-  RunPostinstallAction("/dev/null", kPostinstallDefaultScript, false, false);
+  RunPostinstallAction(
+      "/dev/null", kPostinstallDefaultScript, false, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 
   // In case of failure, Postinstall should not signal a powerwash even if it
@@ -337,7 +367,7 @@
 // fail.
 TEST_F(PostinstallRunnerActionTest, RunAsRootErrScriptTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_fail1", false, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 }
 
@@ -345,7 +375,7 @@
 // UMA with a different error code. Test those cases are properly detected.
 TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareBErrScriptTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_fail3", false, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallBootedFromFirmwareB,
             processor_delegate_.code_);
 }
@@ -353,7 +383,7 @@
 // Check that you can't specify an absolute path.
 TEST_F(PostinstallRunnerActionTest, RunAsRootAbsolutePathNotAllowedTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false);
+  RunPostinstallAction(loop.dev(), "/etc/../bin/sh", false, false, false);
   EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_);
 }
 
@@ -362,7 +392,8 @@
 // SElinux labels are only set on Android.
 TEST_F(PostinstallRunnerActionTest, RunAsRootCheckFileContextsTest) {
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
-  RunPostinstallAction(loop.dev(), "bin/self_check_context", false, false);
+  RunPostinstallAction(
+      loop.dev(), "bin/self_check_context", false, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }
 #endif  // __ANDROID__
@@ -375,7 +406,7 @@
   loop_.PostTask(FROM_HERE,
                  base::Bind(&PostinstallRunnerActionTest::SuspendRunningAction,
                             base::Unretained(this)));
-  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false, false);
   // postinst_suspend returns 0 only if it was suspended at some point.
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
   EXPECT_TRUE(processor_delegate_.processing_done_called_);
@@ -387,7 +418,7 @@
 
   // Wait for the action to start and then cancel it.
   CancelWhenStarted();
-  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false);
+  RunPostinstallAction(loop.dev(), "bin/postinst_suspend", false, false, false);
   // When canceling the action, the action never finished and therefore we had
   // a ProcessingStopped call instead.
   EXPECT_FALSE(processor_delegate_.code_set_);
@@ -410,7 +441,8 @@
 
   ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr);
   setup_action_delegate_ = &mock_delegate_;
-  RunPostinstallAction(loop.dev(), "bin/postinst_progress", false, false);
+  RunPostinstallAction(
+      loop.dev(), "bin/postinst_progress", false, false, false);
   EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_);
 }