|  | // 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/fdio/vfs.h> | 
|  | #include <lib/vfs/cpp/internal/dirent_filler.h> | 
|  | #include <lib/vfs/cpp/pseudo_dir.h> | 
|  |  | 
|  | #include <mutex> | 
|  |  | 
|  | namespace vfs { | 
|  |  | 
|  | PseudoDir::PseudoDir() : next_node_id_(kDotId + 1) {} | 
|  |  | 
|  | PseudoDir::~PseudoDir() = default; | 
|  |  | 
|  | zx_status_t PseudoDir::AddSharedEntry(std::string name, std::shared_ptr<Node> vn) { | 
|  | ZX_DEBUG_ASSERT(vn); | 
|  |  | 
|  | auto id = next_node_id_++; | 
|  | return AddEntry(std::make_unique<SharedEntry>(id, std::move(name), std::move(vn))); | 
|  | } | 
|  |  | 
|  | zx_status_t PseudoDir::AddEntry(std::string name, std::unique_ptr<Node> vn) { | 
|  | ZX_DEBUG_ASSERT(vn); | 
|  |  | 
|  | auto id = next_node_id_++; | 
|  | return AddEntry(std::make_unique<UniqueEntry>(id, std::move(name), std::move(vn))); | 
|  | } | 
|  |  | 
|  | zx_status_t PseudoDir::AddEntry(std::unique_ptr<Entry> entry) { | 
|  | ZX_DEBUG_ASSERT(entry); | 
|  |  | 
|  | if (!vfs::internal::IsValidName(entry->name())) { | 
|  | return ZX_ERR_INVALID_ARGS; | 
|  | } | 
|  |  | 
|  | std::lock_guard<std::mutex> guard(mutex_); | 
|  |  | 
|  | if (entries_by_name_.find(entry->name()) != entries_by_name_.end()) { | 
|  | return ZX_ERR_ALREADY_EXISTS; | 
|  | } | 
|  | entries_by_name_[entry->name()] = entry.get(); | 
|  | auto id = entry->id(); | 
|  | entries_by_id_.emplace_hint(entries_by_id_.end(), id, std::move(entry)); | 
|  |  | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | zx_status_t PseudoDir::RemoveEntry(const std::string& name) { | 
|  | std::lock_guard<std::mutex> guard(mutex_); | 
|  | auto entry = entries_by_name_.find(name); | 
|  | if (entry == entries_by_name_.end()) { | 
|  | return ZX_ERR_NOT_FOUND; | 
|  | } | 
|  | entries_by_id_.erase(entry->second->id()); | 
|  | entries_by_name_.erase(name); | 
|  |  | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | zx_status_t PseudoDir::RemoveEntry(const std::string& name, Node* node) { | 
|  | std::lock_guard<std::mutex> guard(mutex_); | 
|  | auto entry = entries_by_name_.find(name); | 
|  | if (entry == entries_by_name_.end() || entry->second->node() != node) { | 
|  | return ZX_ERR_NOT_FOUND; | 
|  | } | 
|  | entries_by_id_.erase(entry->second->id()); | 
|  | entries_by_name_.erase(name); | 
|  |  | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | zx_status_t PseudoDir::Lookup(const std::string& name, vfs::internal::Node** out_node) const { | 
|  | std::lock_guard<std::mutex> guard(mutex_); | 
|  |  | 
|  | auto search = entries_by_name_.find(name); | 
|  | if (search != entries_by_name_.end()) { | 
|  | *out_node = search->second->node(); | 
|  | return ZX_OK; | 
|  | } else { | 
|  | return ZX_ERR_NOT_FOUND; | 
|  | } | 
|  | } | 
|  |  | 
|  | zx_status_t PseudoDir::Readdir(uint64_t offset, void* data, uint64_t len, uint64_t* out_offset, | 
|  | uint64_t* out_actual) { | 
|  | vfs::internal::DirentFiller df(data, len); | 
|  | *out_actual = 0; | 
|  | *out_offset = offset; | 
|  | if (offset < kDotId) { | 
|  | if (df.Next(".", 1, fuchsia::io::DIRENT_TYPE_DIRECTORY, fuchsia::io::INO_UNKNOWN) != ZX_OK) { | 
|  | *out_actual = df.GetBytesFilled(); | 
|  | return ZX_ERR_INVALID_ARGS;  // out_actual would be 0 | 
|  | } | 
|  | (*out_offset)++; | 
|  | } | 
|  |  | 
|  | std::lock_guard<std::mutex> guard(mutex_); | 
|  |  | 
|  | for (auto it = entries_by_id_.upper_bound(*out_offset); it != entries_by_id_.end(); ++it) { | 
|  | fuchsia::io::NodeAttributes attr; | 
|  | auto d_type = fuchsia::io::DIRENT_TYPE_UNKNOWN; | 
|  | auto ino = fuchsia::io::INO_UNKNOWN; | 
|  | if (it->second->node()->GetAttr(&attr) == ZX_OK) { | 
|  | d_type = ((fuchsia::io::MODE_TYPE_MASK & attr.mode) >> 12); | 
|  | ino = attr.id; | 
|  | } | 
|  |  | 
|  | if (df.Next(it->second->name(), d_type, ino) != ZX_OK) { | 
|  | *out_actual = df.GetBytesFilled(); | 
|  | if (*out_actual == 0) { | 
|  | // no space to fill even 1 dentry | 
|  | return ZX_ERR_INVALID_ARGS; | 
|  | } | 
|  | return ZX_OK; | 
|  | } | 
|  | *out_offset = it->second->id(); | 
|  | } | 
|  |  | 
|  | *out_actual = df.GetBytesFilled(); | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | bool PseudoDir::IsEmpty() const { | 
|  | std::lock_guard<std::mutex> guard(mutex_); | 
|  | return entries_by_name_.size() == 0; | 
|  | } | 
|  |  | 
|  | PseudoDir::Entry::Entry(uint64_t id, std::string name) : id_(id), name_(std::move(name)) {} | 
|  |  | 
|  | PseudoDir::Entry::~Entry() = default; | 
|  |  | 
|  | PseudoDir::SharedEntry::SharedEntry(uint64_t id, std::string name, std::shared_ptr<Node> node) | 
|  | : Entry(id, std::move(name)), node_(std::move(node)) {} | 
|  |  | 
|  | vfs::internal::Node* PseudoDir::SharedEntry::node() const { return node_.get(); } | 
|  |  | 
|  | PseudoDir::SharedEntry::~SharedEntry() = default; | 
|  |  | 
|  | PseudoDir::UniqueEntry::UniqueEntry(uint64_t id, std::string name, std::unique_ptr<Node> node) | 
|  | : Entry(id, std::move(name)), node_(std::move(node)) {} | 
|  |  | 
|  | vfs::internal::Node* PseudoDir::UniqueEntry::node() const { return node_.get(); } | 
|  |  | 
|  | PseudoDir::UniqueEntry::~UniqueEntry() = default; | 
|  |  | 
|  | }  // namespace vfs |