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