1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#pragma once
6
7#include <limits.h>
8#include <string.h>
9
10#include <fs/vfs.h>
11#include <fs/vnode.h>
12#include <lib/fdio/vfs.h>
13#include <fbl/intrusive_double_list.h>
14#include <fbl/ref_counted.h>
15#include <fbl/ref_ptr.h>
16#include <fbl/unique_ptr.h>
17
18namespace memfs {
19
20class VnodeMemfs;
21
22constexpr size_t kDnodeNameMax = NAME_MAX;
23static_assert(NAME_MAX == 255, "NAME_MAX must be 255");
24
25// Assert that kDnodeNameMax can be used as a bitmask
26static_assert(((kDnodeNameMax + 1) & kDnodeNameMax) == 0,
27              "Expected kDnodeNameMax to be one less than a power of two");
28
29class Dnode : public fbl::RefCounted<Dnode> {
30public:
31    DISALLOW_COPY_ASSIGN_AND_MOVE(Dnode);
32    using NodeState = fbl::DoublyLinkedListNodeState<fbl::RefPtr<Dnode>>;
33
34    // ChildTraits is the state used for a Dnode to appear as the child
35    // of another dnode.
36    struct TypeChildTraits { static NodeState& node_state(Dnode& dn) { return dn.type_child_state_; }};
37    using ChildList = fbl::DoublyLinkedList<fbl::RefPtr<Dnode>, Dnode::TypeChildTraits>;
38
39    // Allocates a dnode, attached to a vnode
40    static fbl::RefPtr<Dnode> Create(fbl::StringPiece name, fbl::RefPtr<VnodeMemfs> vn);
41
42    // Takes a parent-less node and makes it a child of the parent node.
43    //
44    // Increments child link count by one.
45    // If the child is a directory, increments the parent link count by one.
46    static void AddChild(fbl::RefPtr<Dnode> parent, fbl::RefPtr<Dnode> child);
47
48    // Removes a dnode from its parent (if dnode has a parent)
49    // Decrements parent link count by one.
50    void RemoveFromParent();
51
52    // Detaches a dnode from its parent / vnode.
53    // Decrements dn->vnode link count by one (if it exists).
54    void Detach();
55
56    bool HasChildren() const { return !children_.is_empty(); }
57
58    // Look up the child dnode (within a parent directory) by name.
59    // Returns ZX_OK if the child is found.
60    //
61    // If the looked up child is the current node, "out" is nullptr, and
62    // ZX_OK is still returned.
63    // If "out" is provided as "nullptr", the returned status appears the
64    // same, but the "out" argument is not touched.
65    zx_status_t Lookup(fbl::StringPiece name, fbl::RefPtr<Dnode>* out) const;
66
67    // Acquire a pointer to the vnode underneath this dnode.
68    // Acquires a reference to the underlying vnode.
69    fbl::RefPtr<VnodeMemfs> AcquireVnode() const;
70
71    // Returns ZX_OK if the dnode may be unlinked
72    zx_status_t CanUnlink() const;
73
74    // Read dirents (up to len bytes worth) into data.
75    // ReaddirStart reads the canned "." and ".." entries that should appear
76    // at the beginning of a directory.
77    // On success, return the number of bytes read.
78    static zx_status_t ReaddirStart(fs::DirentFiller* df, void* cookie);
79    void Readdir(fs::DirentFiller* df, void* cookie) const;
80
81    // Answers the question: "Is dn a subdirectory of this?"
82    bool IsSubdirectory(fbl::RefPtr<Dnode> dn) const;
83
84    // Functions to take / steal the allocated dnode name.
85    fbl::unique_ptr<char[]> TakeName();
86    void PutName(fbl::unique_ptr<char[]> name, size_t len);
87
88    bool IsDirectory() const;
89
90private:
91    friend struct TypeChildTraits;
92
93    Dnode(fbl::RefPtr<VnodeMemfs> vn, fbl::unique_ptr<char[]> name, uint32_t flags);
94
95    size_t NameLen() const;
96    bool NameMatch(fbl::StringPiece name) const;
97
98    NodeState type_child_state_;
99    fbl::RefPtr<VnodeMemfs> vnode_;
100    fbl::RefPtr<Dnode> parent_;
101    // Used to impose an absolute order on dnodes within a directory.
102    size_t ordering_token_;
103    ChildList children_;
104    uint32_t flags_;
105    fbl::unique_ptr<char[]> name_;
106};
107
108} // namespace memfs
109