aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKelvin Zhang <zhangkelvin@google.com>2021-03-24 20:58:39 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-03-24 20:58:39 +0000
commitdb302a06e6d787e04c18a3017a8a9be32412876c (patch)
tree169ec2046e1af4a76d4de2c48f1301076175e317
parentd727ae04de370ccb0e7978b64edd9ec803505f54 (diff)
parent569c97eebd46b7c7a7740dea837f2e86475c02c2 (diff)
downloadupdate_engine-db302a06e6d787e04c18a3017a8a9be32412876c.tar.gz
Add CowWriterFileDescriptor am: 569c97eebd
Original change: https://android-review.googlesource.com/c/platform/system/update_engine/+/1474356 Change-Id: I5a6ceb2a0adbdc4705dbfd7d7135e907cec33009
-rw-r--r--Android.bp1
-rw-r--r--payload_consumer/cow_writer_file_descriptor.cc138
-rw-r--r--payload_consumer/cow_writer_file_descriptor.h66
3 files changed, 205 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
index bc178bc5..a4b7978e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -224,6 +224,7 @@ cc_library_static {
"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 00000000..d8c7afb8
--- /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 00000000..5d9ffc64
--- /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