blob: cce170b4cde5cd72e4a302f9c3841981019276a3 [file] [log] [blame]
// 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/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 {
size_t LocalVnode::Intermediate::num_children() const { return entries_by_id_.size(); }
zx::result<std::tuple<fbl::RefPtr<LocalVnode>, bool>> LocalVnode::Intermediate::LookupOrInsert(
fbl::String name, fit::function<zx::result<fbl::RefPtr<LocalVnode>>(ParentAndId)> builder) {
auto it = entries_by_name_.find(name);
if (it != entries_by_name_.end()) {
return zx::ok(std::make_tuple(it->node(), false));
}
zx::result vn_res = builder(std::make_tuple(std::reference_wrapper(*this), next_node_id_));
if (vn_res.is_error()) {
return vn_res.take_error();
}
auto& vn = vn_res.value();
auto entry = std::make_unique<Entry>(next_node_id_, std::move(name), vn);
entries_by_name_.insert(entry.get());
entries_by_id_.insert(std::move(entry));
next_node_id_++;
return zx::ok(std::make_tuple(std::move(vn), true));
}
void LocalVnode::Intermediate::RemoveEntry(LocalVnode* vn, uint64_t id) {
auto it = entries_by_id_.find(id);
if (it != entries_by_id_.end() && it->node().get() == vn) {
entries_by_name_.erase(it->name());
entries_by_id_.erase(it);
}
}
fbl::RefPtr<LocalVnode> LocalVnode::Intermediate::Lookup(const fbl::String& name) const {
auto it = entries_by_name_.find(name);
if (it != entries_by_name_.end()) {
return it->node();
}
return nullptr;
}
LocalVnode::Remote::~Remote() {
// Close the channel underlying the remote connection without making a Close
// call to preserve previous behavior.
zx::channel remote_channel;
zxio_release(Connection(), remote_channel.reset_and_get_address());
}
LocalVnode::Intermediate::~Intermediate() {
for (auto& entry : entries_by_id_) {
entry.node()->parent_and_id_.reset();
}
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() {
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::UnlinkFromParent() {
if (std::optional opt = std::exchange(parent_and_id_, {}); opt.has_value()) {
const auto& [parent, id] = opt.value();
parent.get().RemoveEntry(this, id);
}
}
zx_status_t LocalVnode::EnumerateInternal(PathBuffer* path, std::string_view name,
const EnumerateCallback& func) {
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);
std::visit(fdio_internal::overloaded{
[](const LocalVnode::Local& c) {
// Nothing to do as the node has no children and is not a
// remote node.
},
[&path, &func, &name](const LocalVnode::Intermediate& c) {
// If we added a node with children, add a separator and enumerate all the
// children.
if (!name.empty()) {
path->Append('/');
}
c.ForAllEntries([&path, &func](LocalVnode& child, std::string_view name) {
return child.EnumerateInternal(path, name, func);
});
},
[&path, &func](LocalVnode::Remote& s) {
// If we added a remote node, call the enumeration function on the remote node.
func(*path, 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) {
PathBuffer path;
path.Append('/');
return EnumerateInternal(&path, {}, func);
}
zx::result<std::string_view> LocalVnode::Readdir(uint64_t* last_seen) const {
return std::visit(fdio_internal::overloaded{
[](const LocalVnode::Local&) -> zx::result<std::string_view> {
// Calling readdir on a Local node is invalid.
return zx::error(ZX_ERR_NOT_DIR);
},
[&](const LocalVnode::Intermediate& c) { return c.Readdir(last_seen); },
[](const LocalVnode::Remote&) -> zx::result<std::string_view> {
// If we've called Readdir on a Remote node, the path
// was misconfigured.
return zx::error(ZX_ERR_BAD_PATH);
},
},
node_type_);
}
zx::result<std::string_view> LocalVnode::Intermediate::Readdir(uint64_t* last_seen) 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();
return zx::ok(it->name());
}
return zx::error(ZX_ERR_NOT_FOUND);
}
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(), entry.name());
if (status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
} // namespace fdio_internal