| // 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 <fbl/auto_call.h> |
| #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 <fbl/string_piece.h> |
| #include <lib/zx/channel.h> |
| #include <zircon/types.h> |
| |
| #include "local-vnode.h" |
| |
| namespace fdio_internal { |
| |
| fbl::RefPtr<LocalVnode> LocalVnode::Create(fbl::RefPtr<LocalVnode> parent, |
| zx::channel remote, fbl::String name) { |
| auto vn = fbl::AdoptRef(new LocalVnode(std::move(parent), std::move(remote), |
| std::move(name))); |
| if (vn->parent_ != nullptr) { |
| vn->parent_->children_.push_back(vn); |
| } |
| return vn; |
| } |
| |
| void LocalVnode::Unlink() { |
| UnlinkChildren(); |
| UnlinkFromParent(); |
| } |
| |
| zx_status_t LocalVnode::SetRemote(zx::channel remote) { |
| if (remote_.is_valid()) { |
| // Cannot re-bind after initial bind. |
| return ZX_ERR_ALREADY_EXISTS; |
| } |
| if (!children_.is_empty()) { |
| // Overlay remotes are disallowed. |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| remote_ = std::move(remote); |
| return ZX_OK; |
| } |
| |
| // Returns a child if it has the name |name|. |
| // Otherwise, returns nullptr. |
| fbl::RefPtr<LocalVnode> LocalVnode::Lookup(const fbl::StringPiece& name) const { |
| auto vn = children_.find_if([&name](const LocalVnode& elem) -> bool { |
| return fbl::StringPiece(elem.Name()) == name; |
| }); |
| return vn != children_.end() ? vn.CopyPointer() : nullptr; |
| } |
| |
| LocalVnode::LocalVnode(fbl::RefPtr<LocalVnode> parent, zx::channel remote, fbl::String name) |
| : parent_(std::move(parent)), remote_(std::move(remote)), name_(std::move(name)) {} |
| |
| void LocalVnode::UnlinkChildren() { |
| while (!children_.is_empty()) { |
| auto child = children_.pop_front(); |
| child->UnlinkChildren(); |
| child->parent_ = nullptr; |
| } |
| } |
| |
| void LocalVnode::UnlinkFromParent() { |
| if (parent_) { |
| parent_->children_.erase(*this); |
| } |
| parent_ = nullptr; |
| } |
| |
| zx_status_t EnumerateInternal(const LocalVnode& vn, fbl::StringBuffer<PATH_MAX>* path, |
| const EnumerateCallback& func) { |
| size_t original_length = path->length(); |
| |
| // Add this current node to the path, and enumerate it if it has a remote |
| // object. |
| path->Append(vn.Name().data(), vn.Name().length()); |
| if (vn.Remote().is_valid()) { |
| func(fbl::StringPiece(path->data(), path->length()), vn.Remote()); |
| } |
| |
| // If we added a non-null path, add a separator and enumerate all the |
| // children. |
| if (vn.Name().length() > 0) { |
| path->Append('/'); |
| } |
| |
| vn.ForAllChildren([&path, &func](const LocalVnode& child) { |
| return EnumerateInternal(child, path, func); |
| }); |
| |
| // 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 EnumerateRemotes(const LocalVnode& vn, const EnumerateCallback& func) { |
| fbl::StringBuffer<PATH_MAX> path; |
| path.Append('/'); |
| return EnumerateInternal(vn, &path, func); |
| } |
| |
| } // namespace fdio_internal |