Add partition writer class.

Previously, delta_performer assumes that each InstallOp can be processed
independently, and therefore it creates an ExntentWriter instance for
every operation. It also assumes that source/target partition can be
read/written using raw system file descriptors.

With the introduction of Virtual Ab Compression, both assumptions fall
apart. We need to process all SOURCE_COPY operations and reorder them
according to merge sequence, which means InstallOperations are no
longer independent of each other. Also, VABC requires us to perform
writes using their ICowWriter interface, as opposed to read/write
syscall.

We can add extra logic to handle these cases, but that will make the
already huge delta_performer.cc even bigger. It's 2000 lines right now.
So instead, we plan to add an additional class called PartitionWriter.
Which is supposed to perform partition level initialization, such as
performing SOURCE_COPY ahead of time according to merge sequence,
setting up snapshot devices, etc. This will make our code more
maintainable.

The purpose of this CL is to refactor DeltaPerformer, and move some of
the logic into PartitionWriter. Future CLs will add a PartitionWriter
for VABC.

Test: treehugger, generate && serve an OTA
Bug: 168554689

Change-Id: I305fe479b22d829dde527ee01df0e48e4dcb7b46
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index d9efc30..b49139e 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -282,6 +282,15 @@
 }
 
 int DeltaPerformer::CloseCurrentPartition() {
+  if (!partition_writer_) {
+    return 0;
+  }
+  int err = partition_writer_->Close();
+  partition_writer_ = nullptr;
+  return err;
+}
+
+int PartitionWriter::Close() {
   int err = 0;
   if (source_fd_ && !source_fd_->Close()) {
     err = errno;
@@ -290,14 +299,6 @@
       err = 1;
   }
   source_fd_.reset();
-  if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
-    err = errno;
-    PLOG(ERROR) << "Error closing ECC source partition";
-    if (!err)
-      err = 1;
-  }
-  source_ecc_fd_.reset();
-  source_ecc_open_failure_ = false;
   source_path_.clear();
 
   if (target_fd_ && !target_fd_->Close()) {
@@ -308,6 +309,15 @@
   }
   target_fd_.reset();
   target_path_.clear();
+
+  if (source_ecc_fd_ && !source_ecc_fd_->Close()) {
+    err = errno;
+    PLOG(ERROR) << "Error closing ECC source partition";
+    if (!err)
+      err = 1;
+  }
+  source_ecc_fd_.reset();
+  source_ecc_open_failure_ = false;
   return -err;
 }
 
@@ -320,27 +330,43 @@
       install_plan_->partitions.size() - partitions_.size();
   const InstallPlan::Partition& install_part =
       install_plan_->partitions[num_previous_partitions + current_partition_];
+  partition_writer_ = std::make_unique<PartitionWriter>(
+      partition,
+      install_part,
+      boot_control_->GetDynamicPartitionControl(),
+      block_size_,
+      interactive_);
+
   // 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;
+  return partition_writer_->Init(install_plan_, source_may_exist);
+}
+
+bool PartitionWriter::Init(const InstallPlan* install_plan,
+                           bool source_may_exist) {
+  const PartitionUpdate& partition = partition_update_;
+  uint32_t source_slot = install_plan->source_slot;
+  uint32_t target_slot = install_plan->target_slot;
+
   // 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.
-  if (source_may_exist && install_part.source_size > 0) {
-    source_path_ = install_part.source_path;
+  if (source_may_exist && install_part_.source_size > 0) {
+    source_path_ = install_part_.source_path;
     int err;
     source_fd_ = OpenFile(source_path_.c_str(), O_RDONLY, false, &err);
     if (!source_fd_) {
       LOG(ERROR) << "Unable to open source partition "
                  << partition.partition_name() << " on slot "
-                 << BootControlInterface::SlotName(install_plan_->source_slot)
-                 << ", file " << source_path_;
+                 << BootControlInterface::SlotName(source_slot) << ", file "
+                 << source_path_;
       return false;
     }
   }
 
-  target_path_ = install_part.target_path;
+  target_path_ = install_part_.target_path;
   int err;
 
   int flags = O_RDWR;
@@ -354,8 +380,8 @@
   if (!target_fd_) {
     LOG(ERROR) << "Unable to open target partition "
                << partition.partition_name() << " on slot "
-               << BootControlInterface::SlotName(install_plan_->target_slot)
-               << ", file " << target_path_;
+               << BootControlInterface::SlotName(target_slot) << ", file "
+               << target_path_;
     return false;
   }
 
@@ -364,38 +390,28 @@
             << "\"";
 
   // Discard the end of the partition, but ignore failures.
-  DiscardPartitionTail(target_fd_, install_part.target_size);
+  DiscardPartitionTail(target_fd_, install_part_.target_size);
 
   return true;
 }
 
-bool DeltaPerformer::OpenCurrentECCPartition() {
+bool PartitionWriter::OpenCurrentECCPartition() {
+  // No support for ECC for full payloads.
+  // Full Paylods should not have any operation that requires ECCPartition.
   if (source_ecc_fd_)
     return true;
 
   if (source_ecc_open_failure_)
     return false;
 
-  if (current_partition_ >= partitions_.size())
-    return false;
-
-  // No support for ECC for full payloads.
-  if (payload_->type == InstallPayloadType::kFull)
-    return false;
-
 #if USE_FEC
-  const PartitionUpdate& partition = partitions_[current_partition_];
-  size_t num_previous_partitions =
-      install_plan_->partitions.size() - partitions_.size();
-  const InstallPlan::Partition& install_part =
-      install_plan_->partitions[num_previous_partitions + current_partition_];
-  string path = install_part.source_path;
+  const PartitionUpdate& partition = partition_update_;
+  const InstallPlan::Partition& install_part = install_part_;
+  std::string path = install_part.source_path;
   FileDescriptorPtr fd(new FecFileDescriptor());
   if (!fd->Open(path.c_str(), O_RDONLY, 0)) {
     PLOG(ERROR) << "Unable to open ECC source partition "
-                << partition.partition_name() << " on slot "
-                << BootControlInterface::SlotName(install_plan_->source_slot)
-                << ", file " << path;
+                << partition.partition_name() << ", file " << path;
     source_ecc_open_failure_ = true;
     return false;
   }
@@ -733,10 +749,6 @@
     if (!HandleOpResult(op_result, InstallOperationTypeName(op.type()), error))
       return false;
 
-    if (!target_fd_->Flush()) {
-      return false;
-    }
-
     next_operation_num_++;
     UpdateOverallProgress(false, "Completed ");
     CheckpointUpdateProgress(false);
@@ -1003,9 +1015,18 @@
 
   // 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());
 
+  TEST_AND_RETURN_FALSE(partition_writer_->PerformReplaceOperation(
+      operation, buffer_.data(), buffer_.size()));
+  // Update buffer
+  DiscardBuffer(true, buffer_.size());
+  return true;
+}
+
+bool PartitionWriter::PerformReplaceOperation(const InstallOperation& operation,
+                                              const void* data,
+                                              size_t count) {
   // Setup the ExtentWriter stack based on the operation type.
   std::unique_ptr<ExtentWriter> writer = std::make_unique<DirectExtentWriter>();
 
@@ -1017,11 +1038,9 @@
 
   TEST_AND_RETURN_FALSE(
       writer->Init(target_fd_, operation.dst_extents(), block_size_));
-  TEST_AND_RETURN_FALSE(writer->Write(buffer_.data(), operation.data_length()));
+  TEST_AND_RETURN_FALSE(writer->Write(data, operation.data_length()));
 
-  // Update buffer
-  DiscardBuffer(true, buffer_.size());
-  return true;
+  return target_fd_->Flush();
 }
 
 bool DeltaPerformer::PerformZeroOrDiscardOperation(
@@ -1032,7 +1051,11 @@
   // These operations have no blob.
   TEST_AND_RETURN_FALSE(!operation.has_data_offset());
   TEST_AND_RETURN_FALSE(!operation.has_data_length());
+  return partition_writer_->PerformZeroOrDiscardOperation(operation);
+}
 
+bool PartitionWriter::PerformZeroOrDiscardOperation(
+    const InstallOperation& operation) {
 #ifdef BLKZEROOUT
   bool attempt_ioctl = true;
   int request =
@@ -1061,13 +1084,13 @@
           target_fd_, zeros.data(), chunk_length, start + offset));
     }
   }
-  return true;
+  return target_fd_->Flush();
 }
 
-bool DeltaPerformer::ValidateSourceHash(const brillo::Blob& calculated_hash,
-                                        const InstallOperation& operation,
-                                        const FileDescriptorPtr source_fd,
-                                        ErrorCode* error) {
+bool PartitionWriter::ValidateSourceHash(const brillo::Blob& calculated_hash,
+                                         const InstallOperation& operation,
+                                         const FileDescriptorPtr source_fd,
+                                         ErrorCode* error) {
   brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
                                     operation.src_sha256_hash().end());
   if (calculated_hash != expected_source_hash) {
@@ -1108,14 +1131,18 @@
     TEST_AND_RETURN_FALSE(operation.src_length() % block_size_ == 0);
   if (operation.has_dst_length())
     TEST_AND_RETURN_FALSE(operation.dst_length() % block_size_ == 0);
+  return partition_writer_->PerformSourceCopyOperation(operation, error);
+}
 
+bool PartitionWriter::PerformSourceCopyOperation(
+    const InstallOperation& operation, ErrorCode* error) {
   TEST_AND_RETURN_FALSE(source_fd_ != nullptr);
 
   // The device may optimize the SOURCE_COPY operation.
   // Being this a device-specific optimization let DynamicPartitionController
   // decide it the operation should be skipped.
-  const PartitionUpdate& partition = partitions_[current_partition_];
-  const auto& partition_control = boot_control_->GetDynamicPartitionControl();
+  const PartitionUpdate& partition = partition_update_;
+  const auto& partition_control = dynamic_control_;
 
   InstallOperation buf;
   bool should_optimize = partition_control->OptimizeOperation(
@@ -1189,7 +1216,7 @@
     }
     TEST_AND_RETURN_FALSE(
         ValidateSourceHash(source_hash, operation, source_ecc_fd_, error));
-    // At this point reading from the the error corrected device worked, but
+    // At this point reading from the error corrected device worked, but
     // reading from the raw device failed, so this is considered a recovered
     // failure.
     source_ecc_recovered_failures_++;
@@ -1215,10 +1242,10 @@
                                                        block_size_,
                                                        nullptr));
   }
-  return true;
+  return target_fd_->Flush();
 }
 
-FileDescriptorPtr DeltaPerformer::ChooseSourceFD(
+FileDescriptorPtr PartitionWriter::ChooseSourceFD(
     const InstallOperation& operation, ErrorCode* error) {
   if (source_fd_ == nullptr) {
     LOG(ERROR) << "ChooseSourceFD fail: source_fd_ == nullptr";
@@ -1264,7 +1291,7 @@
   if (fd_utils::ReadAndHashExtents(
           source_ecc_fd_, operation.src_extents(), block_size_, &source_hash) &&
       ValidateSourceHash(source_hash, operation, source_ecc_fd_, error)) {
-    // At this point reading from the the error corrected device worked, but
+    // At this point reading from the error corrected device worked, but
     // reading from the raw device failed, so this is considered a recovered
     // failure.
     source_ecc_recovered_failures_++;
@@ -1369,6 +1396,17 @@
   if (operation.has_dst_length())
     TEST_AND_RETURN_FALSE(operation.dst_length() % block_size_ == 0);
 
+  TEST_AND_RETURN_FALSE(partition_writer_->PerformSourceBsdiffOperation(
+      operation, error, buffer_.data(), buffer_.size()));
+  DiscardBuffer(true, buffer_.size());
+  return true;
+}
+
+bool PartitionWriter::PerformSourceBsdiffOperation(
+    const InstallOperation& operation,
+    ErrorCode* error,
+    const void* data,
+    size_t count) {
   FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
   TEST_AND_RETURN_FALSE(source_fd != nullptr);
 
@@ -1388,10 +1426,9 @@
 
   TEST_AND_RETURN_FALSE(bsdiff::bspatch(std::move(src_file),
                                         std::move(dst_file),
-                                        buffer_.data(),
-                                        buffer_.size()) == 0);
-  DiscardBuffer(true, buffer_.size());
-  return true;
+                                        static_cast<const uint8_t*>(data),
+                                        count) == 0);
+  return target_fd_->Flush();
 }
 
 namespace {
@@ -1475,7 +1512,17 @@
   // 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());
+  TEST_AND_RETURN_FALSE(partition_writer_->PerformPuffDiffOperation(
+      operation, error, buffer_.data(), buffer_.size()));
+  DiscardBuffer(true, buffer_.size());
+  return true;
+}
 
+bool PartitionWriter::PerformPuffDiffOperation(
+    const InstallOperation& operation,
+    ErrorCode* error,
+    const void* data,
+    size_t count) {
   FileDescriptorPtr source_fd = ChooseSourceFD(operation, error);
   TEST_AND_RETURN_FALSE(source_fd != nullptr);
 
@@ -1496,11 +1543,10 @@
   const size_t kMaxCacheSize = 5 * 1024 * 1024;  // Total 5MB cache.
   TEST_AND_RETURN_FALSE(puffin::PuffPatch(std::move(src_stream),
                                           std::move(dst_stream),
-                                          buffer_.data(),
-                                          buffer_.size(),
+                                          static_cast<const uint8_t*>(data),
+                                          count,
                                           kMaxCacheSize));
-  DiscardBuffer(true, buffer_.size());
-  return true;
+  return target_fd_->Flush();
 }
 
 bool DeltaPerformer::ExtractSignatureMessage() {
@@ -2040,4 +2086,20 @@
   return true;
 }
 
+PartitionWriter::PartitionWriter(
+    const PartitionUpdate& partition_update,
+    const InstallPlan::Partition& install_part,
+    DynamicPartitionControlInterface* dynamic_control,
+    size_t block_size,
+    bool is_interactive)
+    : partition_update_(partition_update),
+      install_part_(install_part),
+      dynamic_control_(dynamic_control),
+      interactive_(is_interactive),
+      block_size_(block_size) {}
+
+PartitionWriter::~PartitionWriter() {
+  Close();
+}
+
 }  // namespace chromeos_update_engine