Add FileDescriptorPtr interface to verity writer

In VABC, read/write of target partition is no longer done via unix file
descriptor. Instead it's done via CowWriter and FiledescriptorPtr.
We need abstraction for verity writer.

Test: treehugger

Change-Id: Id638b4e5a2cea4ab97927a6e7089170a3e257dee
diff --git a/payload_consumer/verity_writer_android.cc b/payload_consumer/verity_writer_android.cc
index d5437b6..864d9a1 100644
--- a/payload_consumer/verity_writer_android.cc
+++ b/payload_consumer/verity_writer_android.cc
@@ -29,6 +29,7 @@
 }
 
 #include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
 
 namespace chromeos_update_engine {
 
@@ -39,7 +40,16 @@
 }  // namespace verity_writer
 
 bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition) {
+  auto read_fd = FileDescriptorPtr(new EintrSafeFileDescriptor());
+  TEST_AND_RETURN_FALSE(read_fd->Open(partition.target_path.c_str(), O_RDWR));
+  return Init(partition, read_fd, read_fd);
+}
+bool VerityWriterAndroid::Init(const InstallPlan::Partition& partition,
+                               FileDescriptorPtr read_fd,
+                               FileDescriptorPtr write_fd) {
   partition_ = &partition;
+  read_fd_ = read_fd;
+  write_fd_ = write_fd;
 
   if (partition_->hash_tree_size != 0 || partition_->fec_size != 0) {
     utils::SetBlockDeviceReadOnly(partition_->target_path, false);
@@ -82,18 +92,18 @@
 
       if (end_offset == hash_tree_data_end) {
         // All hash tree data blocks has been hashed, write hash tree to disk.
-        int fd = HANDLE_EINTR(open(partition_->target_path.c_str(), O_WRONLY));
-        if (fd < 0) {
-          PLOG(ERROR) << "Failed to open " << partition_->target_path
-                      << " to write hash tree.";
-          return false;
-        }
-        ScopedFdCloser fd_closer(&fd);
-
         LOG(INFO) << "Writing verity hash tree to " << partition_->target_path;
         TEST_AND_RETURN_FALSE(hash_tree_builder_->BuildHashTree());
-        TEST_AND_RETURN_FALSE(hash_tree_builder_->WriteHashTreeToFd(
-            fd, partition_->hash_tree_offset));
+        TEST_AND_RETURN_FALSE_ERRNO(
+            write_fd_->Seek(partition_->hash_tree_offset, SEEK_SET));
+        auto success = hash_tree_builder_->WriteHashTree(
+            [write_fd_(this->write_fd_)](auto data, auto size) {
+              return utils::WriteAll(write_fd_, data, size);
+            });
+        // hashtree builder already prints error messages.
+        if (!success) {
+          return false;
+        }
         hash_tree_builder_.reset();
       }
     }
@@ -103,7 +113,8 @@
         partition_->fec_data_offset + partition_->fec_data_size;
     if (offset < fec_data_end && offset + size >= fec_data_end) {
       LOG(INFO) << "Writing verity FEC to " << partition_->target_path;
-      TEST_AND_RETURN_FALSE(EncodeFEC(partition_->target_path,
+      TEST_AND_RETURN_FALSE(EncodeFEC(read_fd_,
+                                      write_fd_,
                                       partition_->fec_data_offset,
                                       partition_->fec_data_size,
                                       partition_->fec_offset,
@@ -116,7 +127,8 @@
   return true;
 }
 
-bool VerityWriterAndroid::EncodeFEC(const std::string& path,
+bool VerityWriterAndroid::EncodeFEC(FileDescriptorPtr read_fd,
+                                    FileDescriptorPtr write_fd,
                                     uint64_t data_offset,
                                     uint64_t data_size,
                                     uint64_t fec_offset,
@@ -135,13 +147,6 @@
       init_rs_char(FEC_PARAMS(fec_roots)), &free_rs_char);
   TEST_AND_RETURN_FALSE(rs_char != nullptr);
 
-  int fd = HANDLE_EINTR(open(path.c_str(), verify_mode ? O_RDONLY : O_RDWR));
-  if (fd < 0) {
-    PLOG(ERROR) << "Failed to open " << path << " to write FEC.";
-    return false;
-  }
-  ScopedFdCloser fd_closer(&fd);
-
   for (size_t i = 0; i < rounds; i++) {
     // Encodes |block_size| number of rs blocks each round so that we can read
     // one block each time instead of 1 byte to increase random read
@@ -154,13 +159,13 @@
       // Don't read past |data_size|, treat them as 0.
       if (offset < data_size) {
         ssize_t bytes_read = 0;
-        TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
+        TEST_AND_RETURN_FALSE(utils::PReadAll(read_fd,
                                               buffer.data(),
                                               buffer.size(),
                                               data_offset + offset,
                                               &bytes_read));
-        TEST_AND_RETURN_FALSE(bytes_read ==
-                              static_cast<ssize_t>(buffer.size()));
+        TEST_AND_RETURN_FALSE(bytes_read >= 0);
+        TEST_AND_RETURN_FALSE(static_cast<size_t>(bytes_read) == buffer.size());
       }
       for (size_t k = 0; k < buffer.size(); k++) {
         rs_blocks[k * rs_n + j] = buffer[k];
@@ -179,17 +184,42 @@
       brillo::Blob fec_read(fec.size());
       ssize_t bytes_read = 0;
       TEST_AND_RETURN_FALSE(utils::PReadAll(
-          fd, fec_read.data(), fec_read.size(), fec_offset, &bytes_read));
-      TEST_AND_RETURN_FALSE(bytes_read ==
-                            static_cast<ssize_t>(fec_read.size()));
+          read_fd, fec_read.data(), fec_read.size(), fec_offset, &bytes_read));
+      TEST_AND_RETURN_FALSE(bytes_read >= 0);
+      TEST_AND_RETURN_FALSE(static_cast<size_t>(bytes_read) == fec_read.size());
       TEST_AND_RETURN_FALSE(fec == fec_read);
     } else {
-      TEST_AND_RETURN_FALSE(
-          utils::PWriteAll(fd, fec.data(), fec.size(), fec_offset));
+      CHECK(write_fd);
+      if (!utils::PWriteAll(write_fd, fec.data(), fec.size(), fec_offset)) {
+        PLOG(ERROR) << "EncodeFEC write() failed";
+        return false;
+      }
     }
     fec_offset += fec.size();
   }
 
   return true;
 }
+
+bool VerityWriterAndroid::EncodeFEC(const std::string& path,
+                                    uint64_t data_offset,
+                                    uint64_t data_size,
+                                    uint64_t fec_offset,
+                                    uint64_t fec_size,
+                                    uint32_t fec_roots,
+                                    uint32_t block_size,
+                                    bool verify_mode) {
+  FileDescriptorPtr fd(new EintrSafeFileDescriptor());
+  TEST_AND_RETURN_FALSE(
+      fd->Open(path.c_str(), verify_mode ? O_RDONLY : O_RDWR));
+  return EncodeFEC(fd,
+                   fd,
+                   data_offset,
+                   data_size,
+                   fec_offset,
+                   fec_size,
+                   fec_roots,
+                   block_size,
+                   verify_mode);
+}
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/verity_writer_android.h b/payload_consumer/verity_writer_android.h
index 05a5856..7dfac0f 100644
--- a/payload_consumer/verity_writer_android.h
+++ b/payload_consumer/verity_writer_android.h
@@ -22,6 +22,7 @@
 
 #include <verity/hash_tree_builder.h>
 
+#include "payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/verity_writer_interface.h"
 
 namespace chromeos_update_engine {
@@ -31,7 +32,10 @@
   VerityWriterAndroid() = default;
   ~VerityWriterAndroid() override = default;
 
-  bool Init(const InstallPlan::Partition& partition) override;
+  bool Init(const InstallPlan::Partition& partition,
+            FileDescriptorPtr read_fd,
+            FileDescriptorPtr write_fd) override;
+  bool Init(const InstallPlan::Partition& partition);
   bool Update(uint64_t offset, const uint8_t* buffer, size_t size) override;
 
   // Read [data_offset : data_offset + data_size) from |path| and encode FEC
@@ -40,6 +44,15 @@
   // in each Update() like hash tree, because for every rs block, its data are
   // spreaded across entire |data_size|, unless we can cache all data in
   // memory, we have to re-read them from disk.
+  static bool EncodeFEC(FileDescriptorPtr read_fd,
+                        FileDescriptorPtr write_fd,
+                        uint64_t data_offset,
+                        uint64_t data_size,
+                        uint64_t fec_offset,
+                        uint64_t fec_size,
+                        uint32_t fec_roots,
+                        uint32_t block_size,
+                        bool verify_mode);
   static bool EncodeFEC(const std::string& path,
                         uint64_t data_offset,
                         uint64_t data_size,
@@ -52,6 +65,8 @@
  private:
   const InstallPlan::Partition* partition_ = nullptr;
 
+  FileDescriptorPtr read_fd_;
+  FileDescriptorPtr write_fd_;
   std::unique_ptr<HashTreeBuilder> hash_tree_builder_;
 
   DISALLOW_COPY_AND_ASSIGN(VerityWriterAndroid);
diff --git a/payload_consumer/verity_writer_interface.h b/payload_consumer/verity_writer_interface.h
index a3ecef3..db7988e 100644
--- a/payload_consumer/verity_writer_interface.h
+++ b/payload_consumer/verity_writer_interface.h
@@ -22,6 +22,7 @@
 
 #include <base/macros.h>
 
+#include "payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/install_plan.h"
 
 namespace chromeos_update_engine {
@@ -30,6 +31,9 @@
  public:
   virtual ~VerityWriterInterface() = default;
 
+  virtual bool Init(const InstallPlan::Partition& partition,
+                    FileDescriptorPtr read_fd,
+                    FileDescriptorPtr write_fd) = 0;
   virtual bool Init(const InstallPlan::Partition& partition) = 0;
   // Update partition data at [offset : offset + size) stored in |buffer|.
   // Data not in |hash_tree_data_extent| or |fec_data_extent| is ignored.
diff --git a/payload_consumer/verity_writer_stub.cc b/payload_consumer/verity_writer_stub.cc
index a0e2467..314ec7e 100644
--- a/payload_consumer/verity_writer_stub.cc
+++ b/payload_consumer/verity_writer_stub.cc
@@ -26,7 +26,9 @@
 }
 }  // namespace verity_writer
 
-bool VerityWriterStub::Init(const InstallPlan::Partition& partition) {
+bool VerityWriterStub::Init(const InstallPlan::Partition& partition,
+                            FileDescriptorPtr read_fd,
+                            FileDescriptorPtr write_fd) {
   return partition.hash_tree_size == 0 && partition.fec_size == 0;
 }
 
diff --git a/payload_consumer/verity_writer_stub.h b/payload_consumer/verity_writer_stub.h
index ea5e574..f8d68ca 100644
--- a/payload_consumer/verity_writer_stub.h
+++ b/payload_consumer/verity_writer_stub.h
@@ -26,7 +26,9 @@
   VerityWriterStub() = default;
   ~VerityWriterStub() override = default;
 
-  bool Init(const InstallPlan::Partition& partition) override;
+  bool Init(const InstallPlan::Partition& partition,
+            FileDescriptorPtr read_fd,
+            FileDescriptorPtr write_fd) override;
   bool Update(uint64_t offset, const uint8_t* buffer, size_t size) override;
 
  private: