| // 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 |