| // 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 <zircon/types.h> | 
 |  | 
 | #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> | 
 |  | 
 | 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_->AddEntry(vn); | 
 |   } | 
 |   return vn; | 
 | } | 
 |  | 
 | void LocalVnode::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_++; | 
 | } | 
 |  | 
 | void LocalVnode::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() { | 
 |   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 (!entries_by_id_.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 it = entries_by_name_.find(name); | 
 |   if (it != entries_by_name_.end()) { | 
 |     return it->node(); | 
 |   } | 
 |   return 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() { | 
 |   for (auto& entry : entries_by_name_) { | 
 |     entry.node()->UnlinkChildren(); | 
 |     entry.node()->parent_ = nullptr; | 
 |   } | 
 |   entries_by_name_.clear(); | 
 |   entries_by_id_.clear(); | 
 | } | 
 |  | 
 | void LocalVnode::UnlinkFromParent() { | 
 |   if (parent_) { | 
 |     parent_->RemoveEntry(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); | 
 | } | 
 |  | 
 | void LocalVnode::Readdir(uint64_t* last_seen, fbl::RefPtr<LocalVnode>* out_vnode) const { | 
 |   for (auto it = entries_by_id_.lower_bound(*last_seen); it != entries_by_id_.end(); ++it) { | 
 |     if (it->id() <= *last_seen) { | 
 |       continue; | 
 |     } | 
 |     *last_seen = it->id(); | 
 |     *out_vnode = it->node(); | 
 |     return; | 
 |   } | 
 |   *out_vnode = nullptr; | 
 | } | 
 |  | 
 | }  // namespace fdio_internal |