Add CowWriterFileDescriptor

Add a wrapper to use CowWriter as a file descriptor, this will be used
by FileSystemVerification stage when writing FEC

Test: treehugger
Bug: 173432386

Change-Id: Iea42ca1081cd6836c7700db172d6987f770d52c0
diff --git a/Android.bp b/Android.bp
index bc178bc..a4b7978 100644
--- a/Android.bp
+++ b/Android.bp
@@ -224,6 +224,7 @@
         "payload_consumer/bzip_extent_writer.cc",
         "payload_consumer/cached_file_descriptor.cc",
         "payload_consumer/certificate_parser_android.cc",
+        "payload_consumer/cow_writer_file_descriptor.cc",
         "payload_consumer/delta_performer.cc",
         "payload_consumer/extent_reader.cc",
         "payload_consumer/extent_writer.cc",
diff --git a/payload_consumer/cow_writer_file_descriptor.cc b/payload_consumer/cow_writer_file_descriptor.cc
new file mode 100644
index 0000000..d8c7afb
--- /dev/null
+++ b/payload_consumer/cow_writer_file_descriptor.cc
@@ -0,0 +1,138 @@
+//
+// Copyright (C) 2021 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/cow_writer_file_descriptor.h"
+
+#include <memory>
+#include <utility>
+
+#include <base/logging.h>
+
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+CowWriterFileDescriptor::CowWriterFileDescriptor(
+    std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer)
+    : cow_writer_(std::move(cow_writer)),
+      cow_reader_(cow_writer_->OpenReader()) {}
+
+bool CowWriterFileDescriptor::Open(const char* path, int flags, mode_t mode) {
+  LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
+  return false;
+}
+bool CowWriterFileDescriptor::Open(const char* path, int flags) {
+  LOG(ERROR) << "CowWriterFileDescriptor doesn't support Open()";
+  return false;
+}
+
+ssize_t CowWriterFileDescriptor::Read(void* buf, size_t count) {
+  if (dirty_) {
+    // OK, CowReader provides a snapshot view of what the cow contains. Which
+    // means any writes happened after opening a CowReader isn't visible to
+    // that CowReader. Therefore, we re-open CowReader whenever we attempt a
+    // read after write. This does incur an overhead everytime you read after
+    // write.
+    // The usage of |dirty_| flag to coordinate re-open is a very coarse grained
+    // checked. This implementation has suboptimal performance. For better
+    // performance, keep track of blocks which are overwritten, and only re-open
+    // if reading a dirty block.
+    // TODO(b/173432386) Implement finer grained dirty checks
+    const auto offset = cow_reader_->Seek(0, SEEK_CUR);
+    cow_reader_.reset();
+    if (!cow_writer_->Finalize()) {
+      LOG(ERROR) << "Failed to Finalize() cow writer";
+      return -1;
+    }
+    cow_reader_ = cow_writer_->OpenReader();
+    if (cow_reader_ == nullptr) {
+      LOG(ERROR) << "Failed to re-open cow reader after writing to COW";
+      return -1;
+    }
+    const auto pos = cow_reader_->Seek(offset, SEEK_SET);
+    if (pos != offset) {
+      LOG(ERROR) << "Failed to seek to previous position after re-opening cow "
+                    "reader, expected "
+                 << offset << " actual: " << pos;
+      return -1;
+    }
+    dirty_ = false;
+  }
+  return cow_reader_->Read(buf, count);
+}
+
+ssize_t CowWriterFileDescriptor::Write(const void* buf, size_t count) {
+  auto offset = cow_reader_->Seek(0, SEEK_CUR);
+  CHECK_EQ(offset % cow_writer_->options().block_size, 0);
+  auto success = cow_writer_->AddRawBlocks(
+      offset / cow_writer_->options().block_size, buf, count);
+  if (success) {
+    if (cow_reader_->Seek(count, SEEK_CUR) < 0) {
+      return -1;
+    }
+    dirty_ = true;
+    return count;
+  }
+  return -1;
+}
+
+off64_t CowWriterFileDescriptor::Seek(const off64_t offset, int whence) {
+  return cow_reader_->Seek(offset, whence);
+}
+
+uint64_t CowWriterFileDescriptor::BlockDevSize() {
+  LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlockDevSize()";
+  return 0;
+}
+
+bool CowWriterFileDescriptor::BlkIoctl(int request,
+                                       uint64_t start,
+                                       uint64_t length,
+                                       int* result) {
+  LOG(ERROR) << "CowWriterFileDescriptor doesn't support BlkIoctl()";
+  return false;
+}
+
+bool CowWriterFileDescriptor::Flush() {
+  // CowWriter already automatilly flushes, no need to do anything.
+  return true;
+}
+
+bool CowWriterFileDescriptor::Close() {
+  if (cow_writer_) {
+    TEST_AND_RETURN_FALSE(cow_writer_->Finalize());
+    cow_writer_ = nullptr;
+  }
+  if (cow_reader_) {
+    TEST_AND_RETURN_FALSE(cow_reader_->Close());
+    cow_reader_ = nullptr;
+  }
+  return true;
+}
+
+bool CowWriterFileDescriptor::IsSettingErrno() {
+  return false;
+}
+
+bool CowWriterFileDescriptor::IsOpen() {
+  return cow_writer_ != nullptr && cow_reader_ != nullptr;
+}
+
+CowWriterFileDescriptor::~CowWriterFileDescriptor() {
+  Close();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/cow_writer_file_descriptor.h b/payload_consumer/cow_writer_file_descriptor.h
new file mode 100644
index 0000000..5d9ffc6
--- /dev/null
+++ b/payload_consumer/cow_writer_file_descriptor.h
@@ -0,0 +1,66 @@
+//
+// Copyright (C) 2021 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 <cstdint>
+#include <memory>
+
+#include <libsnapshot/snapshot_writer.h>
+
+#include "update_engine/payload_consumer/file_descriptor.h"
+
+namespace chromeos_update_engine {
+
+// A Readable/Writable FileDescriptor class. This is a simple wrapper around
+// CowWriter. Only intended to be used by FileSystemVerifierAction for writing
+// FEC. Writes must be block aligned(4096) or write will fail.
+class CowWriterFileDescriptor final : public FileDescriptor {
+ public:
+  explicit CowWriterFileDescriptor(
+      std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer);
+  ~CowWriterFileDescriptor();
+
+  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;
+
+  // |count| must be block aligned, current offset of this fd must also be block
+  // aligned.
+  ssize_t Write(const void* buf, size_t count) override;
+
+  off64_t Seek(off64_t offset, int whence) override;
+
+  uint64_t BlockDevSize() override;
+
+  bool BlkIoctl(int request,
+                uint64_t start,
+                uint64_t length,
+                int* result) override;
+
+  bool Flush() override;
+
+  bool Close() override;
+
+  bool IsSettingErrno() override;
+
+  bool IsOpen() override;
+
+ private:
+  std::unique_ptr<android::snapshot::ISnapshotWriter> cow_writer_;
+  FileDescriptorPtr cow_reader_;
+  bool dirty_ = false;
+};
+}  // namespace chromeos_update_engine