| // 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/fidl/cpp/event_sender.h> |
| #include <lib/vfs/cpp/flags.h> |
| #include <lib/vfs/cpp/internal/connection.h> |
| #include <lib/vfs/cpp/internal/node.h> |
| #include <lib/vfs/cpp/internal/node_connection.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; |
| |
| constexpr std::tuple<NodeKind::Type, uint32_t> kKindFlagMap[] = { |
| {NodeKind::kReadable, fuchsia::io::OPEN_RIGHT_READABLE}, |
| {NodeKind::kMountable, fuchsia::io::OPEN_RIGHT_ADMIN}, |
| {NodeKind::kWritable, fuchsia::io::OPEN_RIGHT_WRITABLE}, |
| {NodeKind::kAppendable, fuchsia::io::OPEN_FLAG_APPEND}, |
| {NodeKind::kCanTruncate, fuchsia::io::OPEN_FLAG_TRUNCATE}, |
| {NodeKind::kCreatable, |
| fuchsia::io::OPEN_FLAG_CREATE | fuchsia::io::OPEN_FLAG_CREATE_IF_ABSENT}}; |
| |
| } // namespace |
| |
| namespace internal { |
| |
| 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::PreClose(Connection* connection) { return ZX_OK; } |
| |
| zx_status_t Node::Sync() { return ZX_ERR_NOT_SUPPORTED; } |
| |
| 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. |
| 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; |
| } |
| 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 | GetAllowedFlags(); |
| if (is_directory) { |
| allowed_flags = allowed_flags | fuchsia::io::OPEN_FLAG_DIRECTORY; |
| } else { |
| allowed_flags = allowed_flags | fuchsia::io::OPEN_FLAG_NOT_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::GetAllowedFlags() const { |
| NodeKind::Type kind = GetKind(); |
| uint32_t flags = 0; |
| for (auto& tuple : kKindFlagMap) { |
| if ((kind & std::get<0>(tuple)) == std::get<0>(tuple)) { |
| flags = flags | std::get<1>(tuple); |
| } |
| } |
| return flags; |
| } |
| |
| uint32_t Node::GetProhibitiveFlags() const { |
| NodeKind::Type kind = GetKind(); |
| if (NodeKind::IsDirectory(kind)) { |
| return fuchsia::io::OPEN_FLAG_CREATE | fuchsia::io::OPEN_FLAG_CREATE_IF_ABSENT | |
| fuchsia::io::OPEN_FLAG_TRUNCATE | fuchsia::io::OPEN_FLAG_APPEND; |
| } |
| 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; |
| } |
| |
| fidl::EventSender<fuchsia::io::Node> sender(std::move(request)); |
| sender.events().OnOpen(status, nullptr); |
| } |
| |
| 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 internal |
| } // namespace vfs |