diff options
author | Kelvin Zhang <zhangkelvin@google.com> | 2020-10-26 12:12:24 -0400 |
---|---|---|
committer | Kelvin Zhang <zhangkelvin@google.com> | 2021-03-24 10:08:48 -0400 |
commit | 569c97eebd46b7c7a7740dea837f2e86475c02c2 (patch) | |
tree | 169ec2046e1af4a76d4de2c48f1301076175e317 | |
parent | eeec3819f022ca4d3792ce8b627f370285cd66dc (diff) | |
download | update_engine-569c97eebd46b7c7a7740dea837f2e86475c02c2.tar.gz |
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
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | payload_consumer/cow_writer_file_descriptor.cc | 138 | ||||
-rw-r--r-- | payload_consumer/cow_writer_file_descriptor.h | 66 |
3 files changed, 205 insertions, 0 deletions
@@ -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 |