| // 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 FS_PSEUDO_DIR_H_ |
| #define FS_PSEUDO_DIR_H_ |
| |
| #include <fbl/intrusive_wavl_tree.h> |
| #include <fbl/macros.h> |
| #include <fbl/mutex.h> |
| #include <fbl/ref_counted.h> |
| #include <fbl/string.h> |
| #include <fbl/unique_ptr.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: |
| // Creates a directory which is initially empty. |
| PseudoDir(); |
| |
| // Destroys the directory and releases the nodes it contains. |
| ~PseudoDir() override; |
| |
| // 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(fbl::StringPiece 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(fbl::StringPiece 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; |
| |
| // |Vnode| implementation: |
| zx_status_t Open(uint32_t flags, fbl::RefPtr<Vnode>* out_redirect) final; |
| zx_status_t Getattr(vnattr_t* a) final; |
| zx_status_t Lookup(fbl::RefPtr<fs::Vnode>* out, fbl::StringPiece name) final; |
| void Notify(fbl::StringPiece name, unsigned event) final; |
| zx_status_t WatchDir(fs::Vfs* vfs, uint32_t mask, uint32_t options, zx::channel watcher) final; |
| zx_status_t Readdir(vdircookie_t* cookie, void* dirents, size_t len, size_t* out_actual) final; |
| bool IsDirectory() const final { return true; } |
| zx_status_t GetNodeInfo(uint32_t flags, fuchsia_io_NodeInfo* info) final; |
| |
| private: |
| static constexpr uint64_t kDotId = 1u; |
| |
| struct NameTreeTraits; |
| struct IdTreeTraits; |
| class Entry { |
| 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_; |
| |
| // Node states. |
| friend IdTreeTraits; |
| friend NameTreeTraits; |
| fbl::WAVLTreeNodeState<fbl::unique_ptr<Entry>> id_tree_state_; |
| fbl::WAVLTreeNodeState<Entry*> name_tree_state_; |
| }; |
| |
| 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; |
| } |
| }; |
| |
| struct IdTreeTraits { |
| using PtrTraits = fbl::internal::ContainerPtrTraits<fbl::unique_ptr<Entry>>; |
| static fbl::WAVLTreeNodeState<fbl::unique_ptr<Entry>>& node_state( |
| Entry& entry) { |
| return entry.id_tree_state_; |
| } |
| }; |
| |
| struct NameTreeTraits { |
| using PtrTraits = fbl::internal::ContainerPtrTraits<Entry*>; |
| static fbl::WAVLTreeNodeState<Entry*>& node_state(Entry& entry) { |
| return entry.name_tree_state_; |
| } |
| }; |
| |
| using EntryByIdMap = fbl::WAVLTree<uint64_t, fbl::unique_ptr<Entry>, |
| KeyByIdTraits, IdTreeTraits>; |
| using EntryByNameMap = fbl::WAVLTree<fbl::String, Entry*, |
| KeyByNameTraits, NameTreeTraits>; |
| |
| mutable fbl::Mutex mutex_; |
| |
| 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 // FS_PSEUDO_DIR_H_ |