blob: e3c80b18d85f6479b68a1d5a7ae87bcd2f4223a0 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/vfs/cpp/pseudo_file.h>
#include <lib/vfs/cpp/flags.h>
#include <lib/vfs/cpp/internal/file_connection.h>
#include <zircon/assert.h>
#include <sstream>
namespace vfs {
BufferedPseudoFile::BufferedPseudoFile(ReadHandler read_handler,
WriteHandler write_handler,
size_t buffer_capacity)
: read_handler_(std::move(read_handler)),
write_handler_(std::move(write_handler)),
buffer_capacity_(buffer_capacity) {
ZX_DEBUG_ASSERT(read_handler_ != nullptr);
}
BufferedPseudoFile::~BufferedPseudoFile() = default;
zx_status_t BufferedPseudoFile::CreateConnection(
uint32_t flags, std::unique_ptr<Connection>* connection) {
std::vector<uint8_t> output;
if (Flags::IsReadable(flags)) {
zx_status_t status = read_handler_(&output);
if (status != ZX_OK) {
return status;
}
}
*connection = std::make_unique<BufferedPseudoFile::Content>(
this, flags, std::move(output));
return ZX_OK;
}
uint32_t BufferedPseudoFile::GetAdditionalAllowedFlags() const {
auto allowed_flags = fuchsia::io::OPEN_RIGHT_READABLE;
if (write_handler_ != nullptr) {
allowed_flags |=
fuchsia::io::OPEN_RIGHT_WRITABLE | fuchsia::io::OPEN_FLAG_TRUNCATE;
}
return allowed_flags;
}
uint32_t BufferedPseudoFile::GetProhibitiveFlags() const {
return fuchsia::io::OPEN_FLAG_APPEND;
}
uint64_t BufferedPseudoFile::GetLength() {
// this should never be called
ZX_DEBUG_ASSERT(false);
return 0u;
}
size_t BufferedPseudoFile::GetCapacity() {
// this should never be called
ZX_DEBUG_ASSERT(false);
return buffer_capacity_;
}
BufferedPseudoFile::Content::Content(BufferedPseudoFile* file, uint32_t flags,
std::vector<uint8_t> content)
: Connection(flags),
file_(file),
buffer_(std::move(content)),
flags_(flags) {
SetInputLength(buffer_.size());
}
BufferedPseudoFile::Content::~Content() {
if (!dirty_) {
return;
}
file_->write_handler_(std::move(buffer_));
};
zx_status_t BufferedPseudoFile::Content::ReadAt(
uint64_t count, uint64_t offset, std::vector<uint8_t>* out_data) {
if (offset >= buffer_.size()) {
return ZX_OK;
}
size_t actual = std::min(buffer_.size() - offset, count);
out_data->resize(actual);
std::copy_n(buffer_.begin() + offset, actual, out_data->begin());
return ZX_OK;
}
uint32_t BufferedPseudoFile::Content::GetAdditionalAllowedFlags() const {
return file_->GetAdditionalAllowedFlags();
}
uint32_t BufferedPseudoFile::Content::GetProhibitiveFlags() const {
return file_->GetProhibitiveFlags();
}
zx_status_t BufferedPseudoFile::Content::WriteAt(std::vector<uint8_t> data,
uint64_t offset,
uint64_t* out_actual) {
if (offset >= file_->buffer_capacity_) {
*out_actual = 0u;
return ZX_OK;
}
size_t actual = std::min(data.size(), file_->buffer_capacity_ - offset);
if (actual == 0) {
*out_actual = 0u;
return ZX_OK;
}
dirty_ = true;
if (actual > buffer_.size() - offset) {
SetInputLength(offset + actual);
}
std::copy_n(data.begin(), actual, buffer_.begin() + offset);
*out_actual = actual;
return ZX_OK;
}
zx_status_t BufferedPseudoFile::Content::Truncate(uint64_t length) {
if (length > file_->buffer_capacity_) {
return ZX_ERR_NO_SPACE;
}
dirty_ = true;
SetInputLength(length);
return ZX_OK;
}
uint64_t BufferedPseudoFile::Content::GetLength() { return buffer_.size(); }
size_t BufferedPseudoFile::Content::GetCapacity() {
return file_->buffer_capacity_;
}
void BufferedPseudoFile::Content::SetInputLength(size_t length) {
ZX_DEBUG_ASSERT(length <= file_->buffer_capacity_);
buffer_.resize(length);
}
zx_status_t BufferedPseudoFile::Content::Bind(zx::channel request,
async_dispatcher_t* dispatcher) {
// only one connection allowed per content
ZX_DEBUG_ASSERT(connections().size() == 0);
std::unique_ptr<Connection> connection;
zx_status_t status = CreateConnection(flags_, &connection);
if (status != ZX_OK) {
SendOnOpenEventOnError(flags_, std::move(request), status);
return status;
}
status = connection->Bind(std::move(request), dispatcher);
AddConnection(std::move(connection));
return status;
}
std::unique_ptr<Connection> BufferedPseudoFile::Content::Close(
Connection* connection) {
File::Close(connection);
return file_->Close(this);
}
void BufferedPseudoFile::Content::SendOnOpenEvent(zx_status_t status) {
ZX_DEBUG_ASSERT(connections().size() == 1);
connections()[0]->SendOnOpenEvent(status);
}
} // namespace vfs