blob: 1101f5521e2072805e8d8fc13b114f306e0a0eb8 [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 "src/storage/lib/vfs/cpp/pseudo_file.h"
#include <fidl/fuchsia.io/cpp/wire.h>
#include <string_view>
#include <utility>
#include "src/storage/lib/vfs/cpp/vfs_types.h"
namespace fio = 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;
fuchsia_io::NodeProtocolKinds PseudoFile::GetProtocols() const {
return fuchsia_io::NodeProtocolKinds::kFile;
}
bool PseudoFile::ValidateRights(fuchsia_io::Rights rights) const {
if ((rights & fuchsia_io::Rights::kReadBytes) && !read_handler_) {
return false;
}
if ((rights & fuchsia_io::Rights::kWriteBytes) && !write_handler_) {
return false;
}
// Executable pseudo-files are not supported, thus we prevent it from being opened with
// OPEN_RIGHT_EXECUTABLE (since even if GetBackingMemory was supported, there is no way of
// creating an executable VMO without a VMEX, which poses potential security issues).
if (rights & fuchsia_io::Rights::kExecute) {
return false;
}
return true;
}
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::OpenNode(fbl::RefPtr<Vnode>* out_redirect) {
fbl::String output;
if (read_handler_) {
zx_status_t status = read_handler_(&output);
if (status != ZX_OK) {
return status;
}
}
*out_redirect = fbl::MakeRefCounted<Content>(fbl::RefPtr(this), std::move(output));
return ZX_OK;
}
BufferedPseudoFile::Content::Content(fbl::RefPtr<BufferedPseudoFile> file, fbl::String output)
: file_(std::move(file)), output_(std::move(output)) {}
BufferedPseudoFile::Content::~Content() { delete[] input_data_; }
fuchsia_io::NodeProtocolKinds BufferedPseudoFile::Content::GetProtocols() const {
return fuchsia_io::NodeProtocolKinds::kFile;
}
zx_status_t BufferedPseudoFile::Content::CloseNode() {
if (file_->write_handler_) {
return file_->write_handler_(std::string_view(input_data_, input_length_));
}
return ZX_OK;
}
zx::result<fs::VnodeAttributes> BufferedPseudoFile::Content::GetAttributes() const {
zx::result attributes = file_->GetAttributes();
if (attributes.is_ok()) {
attributes->content_size = output_.size();
}
return attributes;
}
zx_status_t BufferedPseudoFile::Content::Read(void* data, size_t length, size_t offset,
size_t* out_actual) {
if (length == 0u || offset >= output_.length()) {
*out_actual = 0u;
return ZX_OK;
}
length = std::min(length, output_.length() - offset);
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) {
if (length == 0u) {
*out_actual = 0u;
return ZX_OK;
}
if (offset >= file_->input_buffer_capacity_) {
return ZX_ERR_NO_SPACE;
}
length = std::min(length, file_->input_buffer_capacity_ - offset);
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_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) {
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;
}
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;
fuchsia_io::NodeProtocolKinds UnbufferedPseudoFile::Content::GetProtocols() const {
return fuchsia_io::NodeProtocolKinds::kFile;
}
zx_status_t UnbufferedPseudoFile::OpenNode(fbl::RefPtr<Vnode>* out_redirect) {
*out_redirect = fbl::MakeRefCounted<Content>(fbl::RefPtr(this));
return ZX_OK;
}
UnbufferedPseudoFile::Content::Content(fbl::RefPtr<UnbufferedPseudoFile> file)
: file_(std::move(file)), truncated_since_last_successful_write_(false) {}
UnbufferedPseudoFile::Content::~Content() = default;
zx_status_t UnbufferedPseudoFile::Content::OpenNode(fbl::RefPtr<Vnode>* out_redirect) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t UnbufferedPseudoFile::Content::CloseNode() {
if (file_->write_handler_ && truncated_since_last_successful_write_) {
return file_->write_handler_(std::string_view());
}
return ZX_OK;
}
zx::result<fs::VnodeAttributes> UnbufferedPseudoFile::Content::GetAttributes() const {
return file_->GetAttributes();
}
zx_status_t UnbufferedPseudoFile::Content::Read(void* data, size_t length, size_t offset,
size_t* out_actual) {
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) {
length = std::min(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) {
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_(std::string_view(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_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) {
if (length != 0u) {
return ZX_ERR_INVALID_ARGS;
}
truncated_since_last_successful_write_ = true;
return ZX_OK;
}
} // namespace fs