blob: 525a0e190ce2b72601fc7d96143af91d86272ee3 [file] [log] [blame]
// Copyright 2017 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 <fs/pseudo_file.h>
#include <fuchsia/io/llcpp/fidl.h>
#include <zircon/device/vfs.h>
#include <utility>
#include <fs/vfs.h>
#include <fs/vfs_types.h>
namespace fio = ::llcpp::fuchsia::io;
namespace fs {
PseudoFile::PseudoFile(ReadHandler read_handler, WriteHandler write_handler)
: read_handler_(std::move(read_handler)), write_handler_(std::move(write_handler)) {}
PseudoFile::~PseudoFile() = default;
VnodeProtocolSet PseudoFile::GetProtocols() const { return VnodeProtocol::kFile; }
bool PseudoFile::ValidateRights(Rights rights) {
if (rights.read && !read_handler_) {
return false;
}
if (rights.write && !write_handler_) {
return false;
}
return true;
}
zx_status_t PseudoFile::GetAttributes(VnodeAttributes* attr) {
*attr = VnodeAttributes();
attr->mode = V_TYPE_FILE;
if (read_handler_)
attr->mode |= V_IRUSR;
if (write_handler_)
attr->mode |= V_IWUSR;
attr->inode = fio::INO_UNKNOWN;
attr->link_count = 1;
return ZX_OK;
}
zx_status_t PseudoFile::GetNodeInfoForProtocol([[maybe_unused]] VnodeProtocol protocol,
[[maybe_unused]] Rights rights,
VnodeRepresentation* info) {
*info = VnodeRepresentation::File();
return ZX_OK;
}
BufferedPseudoFile::BufferedPseudoFile(ReadHandler read_handler, WriteHandler write_handler,
size_t input_buffer_capacity)
: PseudoFile(std::move(read_handler), std::move(write_handler)),
input_buffer_capacity_(input_buffer_capacity) {}
BufferedPseudoFile::~BufferedPseudoFile() = default;
zx_status_t BufferedPseudoFile::Open(ValidatedOptions options,
fbl::RefPtr<Vnode>* out_redirect) {
fbl::String output;
if (options->rights.read) {
zx_status_t status = read_handler_(&output);
if (status != ZX_OK) {
return status;
}
}
*out_redirect = fbl::AdoptRef(new Content(fbl::RefPtr(this), *options, std::move(output)));
return ZX_OK;
}
BufferedPseudoFile::Content::Content(fbl::RefPtr<BufferedPseudoFile> file,
VnodeConnectionOptions options, fbl::String output)
: file_(std::move(file)), options_(options), output_(std::move(output)) {}
BufferedPseudoFile::Content::~Content() { delete[] input_data_; }
VnodeProtocolSet BufferedPseudoFile::Content::GetProtocols() const { return VnodeProtocol::kFile; }
zx_status_t BufferedPseudoFile::Content::Close() {
if (options_.rights.write) {
return file_->write_handler_(fbl::StringPiece(input_data_, input_length_));
}
return ZX_OK;
}
zx_status_t BufferedPseudoFile::Content::GetAttributes(fs::VnodeAttributes* a) {
return file_->GetAttributes(a);
}
zx_status_t BufferedPseudoFile::Content::Read(void* data, size_t length, size_t offset,
size_t* out_actual) {
ZX_DEBUG_ASSERT(options_.rights.read);
if (length == 0u || offset >= output_.length()) {
*out_actual = 0u;
return ZX_OK;
}
size_t remaining_length = output_.length() - offset;
if (length > remaining_length) {
length = remaining_length;
}
memcpy(data, output_.data() + offset, length);
*out_actual = length;
return ZX_OK;
}
zx_status_t BufferedPseudoFile::Content::Write(const void* data, size_t length, size_t offset,
size_t* out_actual) {
ZX_DEBUG_ASSERT(options_.rights.write);
if (length == 0u) {
*out_actual = 0u;
return ZX_OK;
}
if (offset >= file_->input_buffer_capacity_) {
return ZX_ERR_NO_SPACE;
}
size_t remaining_length = file_->input_buffer_capacity_ - offset;
if (length > remaining_length) {
length = remaining_length;
}
if (offset + length > input_length_) {
SetInputLength(offset + length);
}
memcpy(input_data_ + offset, data, length);
*out_actual = length;
return ZX_OK;
}
zx_status_t BufferedPseudoFile::Content::Append(const void* data, size_t length, size_t* out_end,
size_t* out_actual) {
ZX_DEBUG_ASSERT(options_.rights.write);
zx_status_t status = Write(data, length, input_length_, out_actual);
if (status == ZX_OK) {
*out_end = input_length_;
}
return status;
}
zx_status_t BufferedPseudoFile::Content::Truncate(size_t length) {
ZX_DEBUG_ASSERT(options_.rights.write);
if (length > file_->input_buffer_capacity_) {
return ZX_ERR_NO_SPACE;
}
size_t old_length = input_length_;
SetInputLength(length);
if (length > old_length) {
memset(input_data_ + old_length, 0, length - old_length);
}
return ZX_OK;
}
zx_status_t BufferedPseudoFile::Content::GetNodeInfoForProtocol(VnodeProtocol protocol,
Rights rights,
VnodeRepresentation* info) {
return file_->GetNodeInfoForProtocol(protocol, rights, info);
}
void BufferedPseudoFile::Content::SetInputLength(size_t length) {
ZX_DEBUG_ASSERT(length <= file_->input_buffer_capacity_);
if (input_data_ == nullptr && length != 0u) {
input_data_ = new char[file_->input_buffer_capacity_];
}
input_length_ = length;
}
UnbufferedPseudoFile::UnbufferedPseudoFile(ReadHandler read_handler, WriteHandler write_handler)
: PseudoFile(std::move(read_handler), std::move(write_handler)) {}
UnbufferedPseudoFile::~UnbufferedPseudoFile() = default;
VnodeProtocolSet UnbufferedPseudoFile::Content::GetProtocols() const { return VnodeProtocol::kFile; }
zx_status_t UnbufferedPseudoFile::Open(ValidatedOptions options,
fbl::RefPtr<Vnode>* out_redirect) {
*out_redirect = fbl::AdoptRef(new Content(fbl::RefPtr(this), *options));
return ZX_OK;
}
UnbufferedPseudoFile::Content::Content(fbl::RefPtr<UnbufferedPseudoFile> file,
VnodeConnectionOptions options)
: file_(std::move(file)),
options_(options),
truncated_since_last_successful_write_(options.flags.create || options.flags.truncate) {}
UnbufferedPseudoFile::Content::~Content() = default;
zx_status_t UnbufferedPseudoFile::Content::Open(ValidatedOptions options,
fbl::RefPtr<Vnode>* out_redirect) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t UnbufferedPseudoFile::Content::Close() {
if (options_.rights.write && truncated_since_last_successful_write_) {
return file_->write_handler_(fbl::StringPiece());
}
return ZX_OK;
}
zx_status_t UnbufferedPseudoFile::Content::GetAttributes(fs::VnodeAttributes* a) {
return file_->GetAttributes(a);
}
zx_status_t UnbufferedPseudoFile::Content::Read(void* data, size_t length, size_t offset,
size_t* out_actual) {
ZX_DEBUG_ASSERT(options_.rights.read);
if (offset != 0u) {
// If the offset is non-zero, we assume the client already read the property.
// Simulate end of file.
*out_actual = 0u;
return ZX_OK;
}
fbl::String output;
zx_status_t status = file_->read_handler_(&output);
if (status == ZX_OK) {
if (length > output.length()) {
length = output.length();
}
memcpy(data, output.data(), length);
*out_actual = length;
}
return status;
}
zx_status_t UnbufferedPseudoFile::Content::Write(const void* data, size_t length, size_t offset,
size_t* out_actual) {
ZX_DEBUG_ASSERT(options_.rights.write);
if (offset != 0u) {
// If the offset is non-zero, we assume the client already wrote the property.
// Simulate an inability to write additional data.
return ZX_ERR_NO_SPACE;
}
zx_status_t status =
file_->write_handler_(fbl::StringPiece(static_cast<const char*>(data), length));
if (status == ZX_OK) {
truncated_since_last_successful_write_ = false;
*out_actual = length;
}
return status;
}
zx_status_t UnbufferedPseudoFile::Content::Append(const void* data, size_t length, size_t* out_end,
size_t* out_actual) {
ZX_DEBUG_ASSERT(options_.rights.write);
zx_status_t status = Write(data, length, 0u, out_actual);
if (status == ZX_OK) {
*out_end = length;
}
return status;
}
zx_status_t UnbufferedPseudoFile::Content::Truncate(size_t length) {
ZX_DEBUG_ASSERT(options_.rights.write);
if (length != 0u) {
return ZX_ERR_INVALID_ARGS;
}
truncated_since_last_successful_write_ = true;
return ZX_OK;
}
zx_status_t UnbufferedPseudoFile::Content::GetNodeInfoForProtocol(
VnodeProtocol protocol, Rights rights, VnodeRepresentation* info) {
return file_->GetNodeInfoForProtocol(protocol, rights, info);
}
} // namespace fs