blob: 92da76017c75bc3dad36ec4b60bb3fb36cf3ec4b [file] [log] [blame] [edit]
// Copyright 2023 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.
#ifndef LIB_VFS_CPP_NEW_PSEUDO_DIR_H_
#define LIB_VFS_CPP_NEW_PSEUDO_DIR_H_
#include <fuchsia/io/cpp/fidl.h>
#include <lib/vfs/cpp/new/internal/node.h>
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <map>
#include <memory>
#include <mutex>
#include <string>
namespace vfs {
class PseudoDir final : public internal::Node {
public:
PseudoDir() : internal::Node(CreateDirectory()) {}
// Adds a directory entry associating the given |name| with |vn|.
// It is ok to add the same Node multiple times with different names.
//
// Returns |ZX_OK| on success.
// Returns |ZX_ERR_ALREADY_EXISTS| if there is already a node with the given
// name.
zx_status_t AddSharedEntry(std::string name, std::shared_ptr<Node> vn) {
std::lock_guard guard(mutex_);
if (node_map_.find(name) != node_map_.cend()) {
return ZX_ERR_ALREADY_EXISTS;
}
if (zx_status_t status = vfs_internal_directory_add(handle(), vn->handle(), name.c_str());
status != ZX_OK) {
return status;
}
node_map_.emplace(std::move(name), std::move(vn));
return ZX_OK;
}
// Adds a directory entry associating the given |name| with |vn|.
//
// Returns |ZX_OK| on success.
// Returns |ZX_ERR_ALREADY_EXISTS| if there is already a node with the given
// name.
zx_status_t AddEntry(std::string name, std::unique_ptr<Node> vn) {
return AddSharedEntry(std::move(name), std::move(vn));
}
// Removes a directory entry with the given |name|.
//
// Returns |ZX_OK| on success.
// Returns |ZX_ERR_NOT_FOUND| if there is no node with the given name.
zx_status_t RemoveEntry(const std::string& name) { return RemoveEntryImpl(name, nullptr); }
// Removes a directory entry with the given |name| and |node|.
//
// Returns |ZX_OK| on success.
// Returns |ZX_ERR_NOT_FOUND| if there is no node with |name| that matches |node|.
// TODO(https://fxbug.dev/293936429): Verify `node`.
zx_status_t RemoveEntry(const std::string& name, const Node* node) {
return RemoveEntryImpl(name, node);
}
// Checks if directory is empty. Use caution if modifying this directory from multiple threads.
bool IsEmpty() const {
std::lock_guard guard(mutex_);
return node_map_.empty();
}
// |vfs::internal::Node| implementation
zx_status_t Lookup(std::string_view name, Node** out_node) const override {
std::lock_guard guard(mutex_);
if (auto node_it = node_map_.find(name); node_it != node_map_.cend()) {
*out_node = node_it->second.get();
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
bool IsDirectory() const override { return true; }
private:
static inline vfs_internal_node_t* CreateDirectory() {
vfs_internal_node_t* dir;
ZX_ASSERT(vfs_internal_directory_create(&dir) == ZX_OK);
return dir;
}
zx_status_t RemoveEntryImpl(const std::string& name, const Node* node) {
std::lock_guard guard(mutex_);
if (auto found = node_map_.find(name); found != node_map_.cend()) {
if (node && node != found->second.get()) {
return ZX_ERR_NOT_FOUND;
}
if (zx_status_t status = vfs_internal_directory_remove(handle(), name.c_str());
status != ZX_OK) {
return status;
}
node_map_.erase(found);
return ZX_OK;
}
return ZX_ERR_NOT_FOUND;
}
mutable std::mutex mutex_;
// *NOTE*: Due to the SDK VFS `Lookup()` semantics, we need to maintain a strong reference to the
// nodes added to this directory. `Lookup()` returns a `Node*` which callers then downcast to the
// concrete node type. The underlying implementation of the pseudo-directory has no concept of
// the C++ types specified here, so we must store them here to allow safe downcasting.
//
// TODO(https://fxbug.dev/311176363): `Lookup()` *should* be handled by the in-tree VFS code, but
// this requires changing `Lookup()` to return a concrete type of node specified by the caller.
// This implies that the C++ types defined here should act more as handle-based types that can be
// copied/moved. We might also consider removing `Lookup()` and require callers maintain
// references/pointers for the nodes they wish to keep track of.
std::map<std::string, std::shared_ptr<internal::Node>, std::less<>> node_map_ __TA_GUARDED(
mutex_);
};
} // namespace vfs
#endif // LIB_VFS_CPP_NEW_PSEUDO_DIR_H_