blob: b3cb702153a5335c6c7bca731b58385f0dbfb4f6 [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 <fuchsia/io/c/fidl.h>
#include <lib/vfs/cpp/connection.h>
#include <lib/vfs/cpp/flags.h>
#include <lib/vfs/cpp/internal/node_connection.h>
#include <lib/vfs/cpp/node.h>
#include <zircon/assert.h>
#include <algorithm>
#include <mutex>
namespace vfs {
namespace {
constexpr uint32_t kCommonAllowedFlags =
fuchsia::io::OPEN_FLAG_DESCRIBE | fuchsia::io::OPEN_FLAG_NODE_REFERENCE |
fuchsia::io::OPEN_FLAG_POSIX | fuchsia::io::CLONE_FLAG_SAME_RIGHTS;
} // namespace
bool IsValidName(const std::string& name) {
return name.length() <= NAME_MAX &&
memchr(name.data(), '/', name.length()) == nullptr && name != "." &&
name != "..";
}
Node::Node() = default;
Node::~Node() = default;
std::unique_ptr<Connection> Node::Close(Connection* connection) {
std::lock_guard<std::mutex> guard(mutex_);
auto connection_iterator = std::find_if(
connections_.begin(), connections_.end(),
[connection](const auto& entry) { return entry.get() == connection; });
auto ret = std::move(*connection_iterator);
connections_.erase(connection_iterator);
return ret;
}
zx_status_t Node::Sync() { return ZX_ERR_NOT_SUPPORTED; }
bool Node::IsRemote() const { return false; }
zx_status_t Node::GetAttr(fuchsia::io::NodeAttributes* out_attributes) const {
return ZX_ERR_NOT_SUPPORTED;
}
void Node::Clone(uint32_t flags, uint32_t parent_flags, zx::channel request,
async_dispatcher_t* dispatcher) {
if (!Flags::InputPrecondition(flags)) {
SendOnOpenEventOnError(flags, std::move(request), ZX_ERR_INVALID_ARGS);
return;
}
// If SAME_RIGHTS is specified, the client cannot request any specific rights.
// TODO(yifeit): Start enforcing this after soft transition
// if (Flags::ShouldCloneWithSameRights(flags) && (flags & Flags::kFsRights))
// {
// SendOnOpenEventOnError(flags, std::move(request), ZX_ERR_INVALID_ARGS);
// return;
// }
flags |= (parent_flags & Flags::kStatusFlags);
// If SAME_RIGHTS is requested, cloned connection will inherit the same rights
// as those from the originating connection.
if (Flags::ShouldCloneWithSameRights(flags)) {
flags &= (~Flags::kFsRights);
flags |= (parent_flags & Flags::kFsRights);
flags &= ~fuchsia::io::CLONE_FLAG_SAME_RIGHTS;
}
// TODO(yifeit): Start enforcing hierarchical rights during clone
// after soft transition
// if (!Flags::StricterOrSameRights(flags, parent_flags)) {
// SendOnOpenEventOnError(flags, std::move(request), ZX_ERR_ACCESS_DENIED);
// return;
// }
Serve(flags, std::move(request), dispatcher);
}
zx_status_t Node::ValidateFlags(uint32_t flags) const {
if (!Flags::InputPrecondition(flags)) {
return ZX_ERR_INVALID_ARGS;
}
bool is_directory = IsDirectory();
if (!is_directory && Flags::IsDirectory(flags)) {
return ZX_ERR_NOT_DIR;
}
if (is_directory && Flags::IsNotDirectory(flags)) {
return ZX_ERR_NOT_FILE;
}
uint32_t allowed_flags = kCommonAllowedFlags | GetAdditionalAllowedFlags();
if (is_directory) {
allowed_flags = allowed_flags | fuchsia::io::OPEN_FLAG_DIRECTORY;
}
uint32_t prohibitive_flags = GetProhibitiveFlags();
if ((flags & prohibitive_flags) != 0) {
return ZX_ERR_INVALID_ARGS;
}
if ((flags & ~allowed_flags) != 0) {
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t Node::ValidateMode(uint32_t mode) const {
fuchsia::io::NodeAttributes attr;
uint32_t mode_from_attr = 0;
zx_status_t status = GetAttr(&attr);
if (status == ZX_OK) {
mode_from_attr = attr.mode & fuchsia::io::MODE_TYPE_MASK;
}
if (((mode & ~fuchsia::io::MODE_PROTECTION_MASK) & ~mode_from_attr) != 0) {
return ZX_ERR_INVALID_ARGS;
}
return ZX_OK;
}
zx_status_t Node::Lookup(const std::string& name, Node** out_node) const {
ZX_ASSERT(!IsDirectory());
return ZX_ERR_NOT_DIR;
}
uint32_t Node::GetAdditionalAllowedFlags() const { return 0; }
uint32_t Node::GetProhibitiveFlags() const { return 0; }
zx_status_t Node::SetAttr(uint32_t flags,
const fuchsia::io::NodeAttributes& attributes) {
return ZX_ERR_NOT_SUPPORTED;
}
uint32_t Node::FilterRefFlags(uint32_t flags) {
if (Flags::IsNodeReference(flags)) {
return flags & (kCommonAllowedFlags | fuchsia::io::OPEN_FLAG_DIRECTORY);
}
return flags;
}
zx_status_t Node::Serve(uint32_t flags, zx::channel request,
async_dispatcher_t* dispatcher) {
flags = FilterRefFlags(flags);
zx_status_t status = ValidateFlags(flags);
if (status != ZX_OK) {
SendOnOpenEventOnError(flags, std::move(request), status);
return status;
}
return Connect(flags, std::move(request), dispatcher);
}
zx_status_t Node::Connect(uint32_t flags, zx::channel request,
async_dispatcher_t* dispatcher) {
zx_status_t status;
std::unique_ptr<Connection> connection;
if (Flags::IsNodeReference(flags)) {
status = Node::CreateConnection(flags, &connection);
} else {
status = CreateConnection(flags, &connection);
}
if (status != ZX_OK) {
SendOnOpenEventOnError(flags, std::move(request), status);
return status;
}
status = connection->Bind(std::move(request), dispatcher);
if (status == ZX_OK) {
AddConnection(std::move(connection));
}
return status;
}
zx_status_t Node::ServeWithMode(uint32_t flags, uint32_t mode,
zx::channel request,
async_dispatcher_t* dispatcher) {
zx_status_t status = ValidateMode(mode);
if (status != ZX_OK) {
SendOnOpenEventOnError(flags, std::move(request), status);
return status;
}
return Serve(flags, std::move(request), dispatcher);
}
void Node::SendOnOpenEventOnError(uint32_t flags, zx::channel request,
zx_status_t status) {
ZX_DEBUG_ASSERT(status != ZX_OK);
if (!Flags::ShouldDescribe(flags)) {
return;
}
fuchsia_io_NodeOnOpenEvent msg;
memset(&msg, 0, sizeof(msg));
msg.hdr.ordinal = fuchsia_io_NodeOnOpenOrdinal;
msg.s = status;
request.write(0, &msg, sizeof(msg), nullptr, 0);
}
uint64_t Node::GetConnectionCount() const {
std::lock_guard<std::mutex> guard(mutex_);
return connections_.size();
}
void Node::AddConnection(std::unique_ptr<Connection> connection) {
std::lock_guard<std::mutex> guard(mutex_);
connections_.push_back(std::move(connection));
}
zx_status_t Node::CreateConnection(uint32_t flags,
std::unique_ptr<Connection>* connection) {
*connection = std::make_unique<internal::NodeConnection>(flags, this);
return ZX_OK;
}
} // namespace vfs