blob: a7d43a7132abf2b22fcf5d57a07d839dc026da2a [file] [log] [blame]
//
// 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 <string>
#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::ICowWriter> cow_writer,
std::unique_ptr<FileDescriptor> cow_reader,
const std::optional<std::string>& source_device)
: cow_writer_(std::move(cow_writer)),
cow_reader_(std::move(cow_reader)),
source_device_(source_device) {
CHECK_NE(cow_writer_, nullptr);
CHECK_NE(cow_reader_, nullptr);
}
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_->OpenFileDescriptor(source_device_);
if (cow_reader_ == nullptr) {
LOG(ERROR)
<< "Failed to re-open cow file descriptor 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_->GetBlockSize(), 0);
auto success = cow_writer_->AddRawBlocks(
offset / cow_writer_->GetBlockSize(), 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_) {
// b/186196758
// When calling
// InitializeAppend(kEndOfInstall), the SnapshotWriter only reads up to the
// given label. But OpenReader() completely disregards the resume label and
// reads all ops. Therefore, update_engine sees the verity data. However,
// when calling SnapshotWriter::Finalize(), data after resume label are
// discarded, therefore verity data is gone. To prevent phantom reads, don't
// call Finalize() unless we actually write something.
if (dirty_) {
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