| // 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 "local-vnode.h" |
| |
| #include <lib/zx/channel.h> |
| #include <lib/zxio/ops.h> |
| #include <lib/zxio/zxio.h> |
| #include <zircon/types.h> |
| |
| #include <utility> |
| |
| #include <fbl/auto_lock.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/string.h> |
| #include <fbl/string_buffer.h> |
| |
| #include "sdk/lib/fdio/internal.h" |
| #include "sdk/lib/fdio/zxio.h" |
| |
| namespace fdio_internal { |
| |
| zx_status_t LocalVnode::AddChild(fbl::RefPtr<LocalVnode> child) { |
| return std::visit(fdio::overloaded{ |
| [](LocalVnode::Local& c) { |
| // Calling AddChild on a Local node is invalid. |
| return ZX_ERR_NOT_DIR; |
| }, |
| [&child](LocalVnode::Intermediate& c) { |
| c.AddEntry(child); |
| return ZX_OK; |
| }, |
| [](LocalVnode::Remote& s) { |
| // Calling AddChild on a Storage node is invalid, and implies |
| // a poorly formed path. |
| return ZX_ERR_BAD_PATH; |
| }, |
| }, |
| node_type_); |
| } |
| |
| void LocalVnode::Intermediate::AddEntry(fbl::RefPtr<LocalVnode> vn) { |
| // |fdio_namespace| already checked that the entry does not exist. |
| ZX_DEBUG_ASSERT(entries_by_name_.find(vn->Name()) == entries_by_name_.end()); |
| |
| auto entry = std::make_unique<Entry>(next_node_id_, std::move(vn)); |
| entries_by_name_.insert(entry.get()); |
| entries_by_id_.insert(std::move(entry)); |
| next_node_id_++; |
| } |
| |
| zx_status_t LocalVnode::RemoveChild(LocalVnode* child) { |
| return std::visit(fdio::overloaded{ |
| [](LocalVnode::Local& c) { |
| // Calling RemoveChild on a Local node fails. |
| return ZX_ERR_NOT_FOUND; |
| }, |
| [&child](LocalVnode::Intermediate& c) { |
| c.RemoveEntry(child); |
| return ZX_OK; |
| }, |
| [](LocalVnode::Remote& s) { |
| // Calling RemoveChild on a Storage node is invalid, and implies |
| // a poorly formed path. |
| return ZX_ERR_BAD_PATH; |
| }, |
| }, |
| node_type_); |
| } |
| |
| void LocalVnode::Intermediate::RemoveEntry(LocalVnode* vn) { |
| auto it = entries_by_name_.find(vn->Name()); |
| if (it != entries_by_name_.end() && it->node().get() == vn) { |
| auto id = it->id(); |
| entries_by_name_.erase(it); |
| entries_by_id_.erase(id); |
| } |
| } |
| |
| void LocalVnode::Unlink() { |
| std::visit(fdio::overloaded{ |
| [](LocalVnode::Local& c) { c.Unlink(); }, |
| [](LocalVnode::Intermediate& c) { c.UnlinkEntries(); }, |
| [](LocalVnode::Remote& s) {}, |
| }, |
| node_type_); |
| UnlinkFromParent(); |
| } |
| |
| fbl::RefPtr<LocalVnode> LocalVnode::Intermediate::Lookup(std::string_view name) const { |
| auto it = entries_by_name_.find(fbl::String{name}); |
| if (it != entries_by_name_.end()) { |
| return it->node(); |
| } |
| return nullptr; |
| } |
| |
| LocalVnode::~LocalVnode() { |
| std::visit(fdio::overloaded{ |
| [](LocalVnode::Local& c) {}, |
| [](LocalVnode::Intermediate& c) {}, |
| [](LocalVnode::Remote& s) { |
| // Close the channel underlying the remote connection without making a Close |
| // call to preserve previous behavior. |
| zx::channel remote_channel; |
| zxio_release(s.Connection(), remote_channel.reset_and_get_address()); |
| }, |
| }, |
| node_type_); |
| } |
| |
| void LocalVnode::Intermediate::UnlinkEntries() { |
| for (auto& entry : entries_by_name_) { |
| std::visit(fdio::overloaded{ |
| [](LocalVnode::Local& c) {}, |
| [](LocalVnode::Intermediate& c) { c.UnlinkEntries(); }, |
| [](LocalVnode::Remote& s) {}, |
| }, |
| entry.node()->node_type_); |
| entry.node()->parent_ = nullptr; |
| } |
| entries_by_name_.clear(); |
| entries_by_id_.clear(); |
| } |
| |
| LocalVnode::Local::Local(fdio_open_local_func_t on_open, void* context) |
| : on_open_(on_open), context_(context) {} |
| |
| zx::result<fdio_ptr> LocalVnode::Local::Open() { |
| fbl::AutoLock lock(&lock_); |
| if (on_open_ == nullptr) { |
| return zx::error(ZX_ERR_BAD_HANDLE); |
| } |
| |
| fdio_ptr io = fbl::MakeRefCounted<zxio>(); |
| if (io == nullptr) { |
| return zx::error(ZX_ERR_NO_MEMORY); |
| } |
| zxio_ops_t const* ops = nullptr; |
| zx_status_t status = on_open_(&io->zxio_storage(), context_, &ops); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| zxio_init(&io->zxio_storage().io, ops); |
| return zx::ok(io); |
| } |
| |
| void LocalVnode::Local::Unlink() { |
| fbl::AutoLock lock(&lock_); |
| on_open_ = nullptr; |
| context_ = nullptr; |
| } |
| |
| void LocalVnode::UnlinkFromParent() { |
| if (parent_) { |
| parent_->RemoveChild(this); |
| } |
| parent_ = nullptr; |
| } |
| |
| zx_status_t LocalVnode::EnumerateInternal(PathBuffer* path, const EnumerateCallback& func) const { |
| const size_t original_length = path->length(); |
| |
| // Add this current node to the path, and enumerate it if it has a remote |
| // object. |
| path->Append(Name().data(), Name().length()); |
| |
| std::visit(fdio::overloaded{ |
| [](const LocalVnode::Local& c) { |
| // Nothing to do as the node has no children and is not a |
| // remote node. |
| }, |
| [&path, &func, this](const LocalVnode::Intermediate& c) { |
| // If we added a node with children, add a separator and enumerate all the |
| // children. |
| if (Name().length() > 0) { |
| path->Append('/'); |
| } |
| |
| c.ForAllEntries([&path, &func](const LocalVnode& child) { |
| return child.EnumerateInternal(path, func); |
| }); |
| }, |
| [&path, &func](const LocalVnode::Remote& s) { |
| // If we added a remote node, call the enumeration function on the remote node. |
| func(std::string_view(path->data(), path->length()), s.Connection()); |
| }, |
| }, |
| node_type_); |
| |
| // To re-use the same prefix buffer, restore the original buffer length |
| // after enumeration has completed. |
| path->Resize(original_length); |
| return ZX_OK; |
| } |
| |
| zx_status_t LocalVnode::EnumerateRemotes(const EnumerateCallback& func) const { |
| PathBuffer path; |
| path.Append('/'); |
| return EnumerateInternal(&path, func); |
| } |
| |
| zx_status_t LocalVnode::Readdir(uint64_t* last_seen, fbl::RefPtr<LocalVnode>* out_vnode) const { |
| return std::visit(fdio::overloaded{ |
| [&](const LocalVnode::Local& c) { |
| // Calling readdir on a Local node is invalid. |
| return ZX_ERR_NOT_DIR; |
| }, |
| [&](const LocalVnode::Intermediate& c) { |
| for (auto it = c.GetEntriesById().lower_bound(*last_seen); |
| it != c.GetEntriesById().end(); ++it) { |
| if (it->id() <= *last_seen) { |
| continue; |
| } |
| *last_seen = it->id(); |
| *out_vnode = it->node(); |
| return ZX_OK; |
| } |
| *out_vnode = nullptr; |
| return ZX_OK; |
| }, |
| [](const LocalVnode::Remote& s) { |
| // If we've called Readdir on a Remote node, the path |
| // was misconfigured. |
| return ZX_ERR_BAD_PATH; |
| }, |
| }, |
| node_type_); |
| } |
| |
| template <typename Fn> |
| zx_status_t LocalVnode::Intermediate::ForAllEntries(Fn fn) const { |
| for (const Entry& entry : entries_by_id_) { |
| zx_status_t status = fn(*entry.node()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| } // namespace fdio_internal |