libsnapshot: Add an open /dev/null mode for CowWriter.

This will be used by payload_generator to estimate the COW size without
using extra storage space.

Bug: 168554689
Test: cow_api_test, ota_from_target_files
Change-Id: I095c809e7d81eff5321b86f4c6bdfb6e9467e84e
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 8704eb9..632c866 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -110,15 +110,28 @@
     return true;
 }
 
+bool CowWriter::SetFd(android::base::borrowed_fd fd) {
+    if (fd.get() < 0) {
+        owned_fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
+        if (owned_fd_ < 0) {
+            PLOG(ERROR) << "open /dev/null failed";
+            return false;
+        }
+        fd_ = owned_fd_;
+        is_dev_null_ = true;
+    } else {
+        fd_ = fd;
+    }
+    return true;
+}
+
 bool CowWriter::Initialize(unique_fd&& fd, OpenMode mode) {
     owned_fd_ = std::move(fd);
     return Initialize(borrowed_fd{owned_fd_}, mode);
 }
 
 bool CowWriter::Initialize(borrowed_fd fd, OpenMode mode) {
-    fd_ = fd;
-
-    if (!ParseOptions()) {
+    if (!SetFd(fd) || !ParseOptions()) {
         return false;
     }
 
@@ -139,9 +152,7 @@
 }
 
 bool CowWriter::InitializeAppend(android::base::borrowed_fd fd, uint64_t label) {
-    fd_ = fd;
-
-    if (!ParseOptions()) {
+    if (!SetFd(fd) || !ParseOptions()) {
         return false;
     }
 
@@ -304,7 +315,7 @@
     CowOperation op = {};
     op.type = kCowLabelOp;
     op.source = label;
-    return WriteOperation(op) && !fsync(fd_.get());
+    return WriteOperation(op) && Sync();
 }
 
 std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
@@ -383,7 +394,7 @@
         PLOG(ERROR) << "lseek ops failed";
         return false;
     }
-    return !fsync(fd_.get());
+    return Sync();
 }
 
 uint64_t CowWriter::GetCowSize() {
@@ -424,5 +435,16 @@
     return true;
 }
 
+bool CowWriter::Sync() {
+    if (is_dev_null_) {
+        return true;
+    }
+    if (fsync(fd_.get()) < 0) {
+        PLOG(ERROR) << "fsync failed";
+        return false;
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 50ce5bc..35690d4 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -90,6 +90,9 @@
     // If opening for write, the file starts from the beginning.
     // If opening for append, if the file has a footer, we start appending to the last op.
     // If the footer isn't found, the last label is considered corrupt, and dropped.
+    //
+    // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
+    // computing COW sizes without using storage space.
     bool Initialize(android::base::unique_fd&& fd, OpenMode mode = OpenMode::WRITE);
     bool Initialize(android::base::borrowed_fd fd, OpenMode mode = OpenMode::WRITE);
     // Set up a writer, assuming that the given label is the last valid label.
@@ -119,6 +122,9 @@
     void AddOperation(const CowOperation& op);
     std::basic_string<uint8_t> Compress(const void* data, size_t length);
 
+    bool SetFd(android::base::borrowed_fd fd);
+    bool Sync();
+
   private:
     android::base::unique_fd owned_fd_;
     android::base::borrowed_fd fd_;
@@ -126,6 +132,7 @@
     CowFooter footer_{};
     int compression_ = 0;
     uint64_t next_op_pos_ = 0;
+    bool is_dev_null_ = false;
 
     // :TODO: this is not efficient, but stringstream ubsan aborts because some
     // bytes overflow a signed char.