blob: fa08e45eeef40e312c47794235b85993a7a00e60 [file] [log] [blame]
// Copyright 2017 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 SRC_STORAGE_LIB_VFS_CPP_PSEUDO_DIR_H_
#define SRC_STORAGE_LIB_VFS_CPP_PSEUDO_DIR_H_
#include <fidl/fuchsia.io/cpp/wire.h>
#include <cstdint>
#include <memory>
#include <mutex>
#include <string_view>
#include <fbl/intrusive_wavl_tree.h>
#include <fbl/macros.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/string.h>
#include "vnode.h"
#include "watcher.h"
namespace fs {
// A pseudo-directory is a directory-like object whose entries are constructed by a program at
// runtime. The client can lookup, enumerate, and watch these directory entries but it cannot
// create, remove, or rename them.
//
// This class is designed to allow programs to publish a relatively small number of entries (up to a
// few dozen) such as services, file-system roots, debugging pseudo-files, or other vnodes. It is
// not suitable for very large directories (hundreds of entries).
//
// This class is thread-safe.
class PseudoDir : public Vnode {
public:
// Construct with fbl::MakeRefCounted.
// Adds a directory entry associating the given |name| with |vn|. It is ok to add the same Vnode
// 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 AddEntry(fbl::String name, fbl::RefPtr<fs::Vnode> 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(std::string_view name);
// An extension of |RemoveEntry| which additionally verifies
// that the target vnode is |vn|.
//
// Returns |ZX_OK| on success.
// Returns |ZX_ERR_NOT_FOUND| if there is no node with the given name/vn pair.
zx_status_t RemoveEntry(std::string_view name, fs::Vnode* vn);
// Removes all directory entries.
void RemoveAllEntries();
// Checks if directory is empty. Be careful while using this function if using this Dir in
// multiple threads.
bool IsEmpty() const;
// Returns true if there are watchers.
bool HasWatchers() const { return watcher_.HasWatchers(); }
// |Vnode| implementation:
fuchsia_io::NodeProtocolKinds GetProtocols() const final;
zx::result<fs::VnodeAttributes> GetAttributes() const final;
zx_status_t Lookup(std::string_view name, fbl::RefPtr<fs::Vnode>* out) override;
void Notify(std::string_view name, fuchsia_io::wire::WatchEvent event) final;
zx_status_t WatchDir(fs::FuchsiaVfs* vfs, fuchsia_io::wire::WatchMask mask, uint32_t options,
fidl::ServerEnd<fuchsia_io::DirectoryWatcher> watcher) final;
zx_status_t Readdir(VdirCookie* cookie, void* dirents, size_t len, size_t* out_actual) final;
private:
friend fbl::internal::MakeRefCountedHelper<PseudoDir>;
friend fbl::RefPtr<PseudoDir>;
static constexpr uint64_t kDotId = 1u;
struct IdTreeTag {};
struct NameTreeTag {};
class Entry : public fbl::ContainableBaseClasses<
fbl::TaggedWAVLTreeContainable<std::unique_ptr<Entry>, IdTreeTag,
fbl::NodeOptions::AllowMultiContainerUptr>,
fbl::TaggedWAVLTreeContainable<Entry*, NameTreeTag,
fbl::NodeOptions::AllowClearUnsafe>> {
public:
Entry(uint64_t id, fbl::String name, fbl::RefPtr<fs::Vnode> node);
~Entry();
uint64_t id() const { return id_; }
const fbl::String& name() const { return name_; }
const fbl::RefPtr<fs::Vnode>& node() const { return node_; }
private:
uint64_t const id_;
fbl::String name_;
fbl::RefPtr<fs::Vnode> node_;
};
struct KeyByIdTraits {
static uint64_t GetKey(const Entry& entry) { return entry.id(); }
static bool LessThan(uint64_t key1, uint64_t key2) { return key1 < key2; }
static bool EqualTo(uint64_t key1, uint64_t key2) { return key1 == key2; }
};
struct KeyByNameTraits {
static fbl::String GetKey(const Entry& entry) { return entry.name(); }
static bool LessThan(const fbl::String& key1, const fbl::String& key2) { return key1 < key2; }
static bool EqualTo(const fbl::String& key1, const fbl::String& key2) { return key1 == key2; }
};
using EntryByIdMap =
fbl::TaggedWAVLTree<uint64_t, std::unique_ptr<Entry>, IdTreeTag, KeyByIdTraits>;
using EntryByNameMap = fbl::TaggedWAVLTree<fbl::String, Entry*, NameTreeTag, KeyByNameTraits>;
protected:
// Creates a directory which is initially empty.
PseudoDir();
// Destroys the directory and releases the nodes it contains.
~PseudoDir() override;
private:
uint64_t next_node_id_ __TA_GUARDED(mutex_) = kDotId + 1;
EntryByIdMap entries_by_id_ __TA_GUARDED(mutex_);
EntryByNameMap entries_by_name_ __TA_GUARDED(mutex_);
fs::WatcherContainer watcher_; // note: uses its own internal mutex
DISALLOW_COPY_ASSIGN_AND_MOVE(PseudoDir);
};
} // namespace fs
#endif // SRC_STORAGE_LIB_VFS_CPP_PSEUDO_DIR_H_