blob: 593dd2fc5378d22a4b2f7e0c30eac04d901b4a5b [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/fdio/vfs.h>
#include <lib/vfs/cpp/flags.h>
#include <lib/vfs/cpp/internal/file_connection.h>
#include <lib/vfs/cpp/pseudo_file.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <sstream>
namespace vfs {
PseudoFile::PseudoFile(size_t max_file_size, ReadHandler read_handler, WriteHandler write_handler)
: read_handler_(std::move(read_handler)),
write_handler_(std::move(write_handler)),
max_file_size_(max_file_size) {
ZX_DEBUG_ASSERT(read_handler_ != nullptr);
}
PseudoFile::~PseudoFile() = default;
zx_status_t PseudoFile::CreateConnection(uint32_t flags,
std::unique_ptr<vfs::internal::Connection>* connection) {
std::vector<uint8_t> output;
if (Flags::IsReadable(flags)) {
zx_status_t status = read_handler_(&output, max_file_size_);
if (status != ZX_OK) {
return status;
}
if (output.size() > max_file_size_) {
return ZX_ERR_FILE_BIG;
}
}
*connection = std::make_unique<PseudoFile::Content>(this, flags, std::move(output));
return ZX_OK;
}
zx_status_t PseudoFile::GetAttr(fuchsia::io::NodeAttributes* out_attributes) const {
out_attributes->mode = fuchsia::io::MODE_TYPE_FILE;
if (read_handler_ != nullptr)
out_attributes->mode |= V_IRUSR;
if (write_handler_)
out_attributes->mode |= V_IWUSR;
out_attributes->id = fuchsia::io::INO_UNKNOWN;
out_attributes->content_size = 0;
out_attributes->storage_size = 0;
out_attributes->link_count = 1;
out_attributes->creation_time = 0;
out_attributes->modification_time = 0;
return ZX_OK;
}
NodeKind::Type PseudoFile::GetKind() const {
auto kind = File::GetKind() | NodeKind::kReadable;
if (write_handler_ != nullptr) {
kind |= NodeKind::kWritable | NodeKind::kCanTruncate;
}
return kind;
}
uint64_t PseudoFile::GetLength() {
// this should never be called
ZX_ASSERT(false);
return 0u;
}
size_t PseudoFile::GetCapacity() {
// this should never be called
ZX_DEBUG_ASSERT(false);
return max_file_size_;
}
PseudoFile::Content::Content(PseudoFile* file, uint32_t flags, std::vector<uint8_t> content)
: Connection(flags), file_(file), buffer_(std::move(content)), flags_(flags) {
SetInputLength(buffer_.size());
}
PseudoFile::Content::~Content() { TryFlushIfRequired(); }
zx_status_t PseudoFile::Content::TryFlushIfRequired() {
if (!dirty_) {
return ZX_OK;
}
dirty_ = false;
return file_->write_handler_(std::move(buffer_));
}
zx_status_t PseudoFile::Content::PreClose(Connection* connection) { return TryFlushIfRequired(); }
NodeKind::Type PseudoFile::Content::GetKind() const { return file_->GetKind(); }
zx_status_t PseudoFile::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;
}
zx_status_t PseudoFile::Content::GetAttr(fuchsia::io::NodeAttributes* out_attributes) const {
auto status = file_->GetAttr(out_attributes);
if (status == ZX_OK) {
out_attributes->content_size = buffer_.size();
}
return status;
}
zx_status_t PseudoFile::Content::WriteAt(std::vector<uint8_t> data, uint64_t offset,
uint64_t* out_actual) {
if (offset >= file_->max_file_size_) {
*out_actual = 0u;
return ZX_OK;
}
size_t actual = std::min(data.size(), file_->max_file_size_ - offset);
if (actual == 0) {
*out_actual = 0u;
return ZX_OK;
}
dirty_ = true;
if (actual + offset > buffer_.size()) {
SetInputLength(offset + actual);
}
std::copy_n(data.begin(), actual, buffer_.begin() + offset);
*out_actual = actual;
return ZX_OK;
}
zx_status_t PseudoFile::Content::Truncate(uint64_t length) {
if (length > file_->max_file_size_) {
return ZX_ERR_NO_SPACE;
}
dirty_ = true;
SetInputLength(length);
return ZX_OK;
}
uint64_t PseudoFile::Content::GetLength() { return buffer_.size(); }
size_t PseudoFile::Content::GetCapacity() { return file_->max_file_size_; }
void PseudoFile::Content::SetInputLength(size_t length) {
ZX_ASSERT_MSG(length <= file_->max_file_size_, "Should not happen. Please report a bug.");
buffer_.resize(length);
}
zx_status_t PseudoFile::Content::BindInternal(zx::channel request, async_dispatcher_t* dispatcher) {
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));
// only one connection allowed per content
ZX_DEBUG_ASSERT(GetConnectionCount() == 1);
return status;
}
std::unique_ptr<vfs::internal::Connection> PseudoFile::Content::Close(Connection* connection) {
File::Close(connection);
return file_->Close(this);
}
void PseudoFile::Content::Clone(uint32_t flags, uint32_t parent_flags, zx::channel request,
async_dispatcher_t* dispatcher) {
file_->Clone(flags, parent_flags, std::move(request), dispatcher);
}
void PseudoFile::Content::SendOnOpenEvent(zx_status_t status) {
// not needed as underlying connection should do this
}
} // namespace vfs