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 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include <fs/vfs.h>
10 #include <fbl/alloc_checker.h>
11 #include <fbl/ref_ptr.h>
12 #include <fbl/unique_ptr.h>
13 #include <lib/memfs/cpp/vnode.h>
14
15 #include <utility>
16
17 #include "dnode.h"
18
19 namespace memfs {
20
21 // Create a new dnode and attach it to a vnode
Create(fbl::StringPiece name,fbl::RefPtr<VnodeMemfs> vn)22 fbl::RefPtr<Dnode> Dnode::Create(fbl::StringPiece name, fbl::RefPtr<VnodeMemfs> vn) {
23 if ((name.length() > kDnodeNameMax) || (name.length() < 1)) {
24 return nullptr;
25 }
26
27 fbl::AllocChecker ac;
28 fbl::unique_ptr<char[]> namebuffer (new (&ac) char[name.length() + 1]);
29 if (!ac.check()) {
30 return nullptr;
31 }
32 memcpy(namebuffer.get(), name.data(), name.length());
33 namebuffer[name.length()] = '\0';
34 fbl::RefPtr<Dnode> dn = fbl::AdoptRef(new (&ac) Dnode(vn, std::move(namebuffer),
35 static_cast<uint32_t>(name.length())));
36 if (!ac.check()) {
37 return nullptr;
38 }
39
40 return dn;
41 }
42
RemoveFromParent()43 void Dnode::RemoveFromParent() {
44 ZX_DEBUG_ASSERT(vnode_ != nullptr);
45
46 // Detach from parent
47 if (parent_) {
48 parent_->children_.erase(*this);
49 if (IsDirectory()) {
50 // '..' no longer references parent.
51 parent_->vnode_->link_count_--;
52 }
53 parent_->vnode_->UpdateModified();
54 parent_ = nullptr;
55 vnode_->link_count_--;
56 }
57 }
58
Detach()59 void Dnode::Detach() {
60 ZX_DEBUG_ASSERT(children_.is_empty());
61 if (vnode_ == nullptr) { // Dnode already detached.
62 return;
63 }
64
65 RemoveFromParent();
66 // Detach from vnode
67 vnode_->dnode_ = nullptr;
68 vnode_ = nullptr;
69 }
70
AddChild(fbl::RefPtr<Dnode> parent,fbl::RefPtr<Dnode> child)71 void Dnode::AddChild(fbl::RefPtr<Dnode> parent, fbl::RefPtr<Dnode> child) {
72 ZX_DEBUG_ASSERT(parent != nullptr);
73 ZX_DEBUG_ASSERT(child != nullptr);
74 ZX_DEBUG_ASSERT(child->parent_ == nullptr); // Child shouldn't have a parent
75 ZX_DEBUG_ASSERT(child != parent);
76 ZX_DEBUG_ASSERT(parent->IsDirectory());
77
78 child->parent_ = parent;
79 child->vnode_->link_count_++;
80 if (child->IsDirectory()) {
81 // Child has '..' pointing back at parent.
82 parent->vnode_->link_count_++;
83 }
84 // Ensure that the ordering of tokens in the children list is absolute.
85 if (parent->children_.is_empty()) {
86 child->ordering_token_ = 2; // '0' for '.', '1' for '..'
87 } else {
88 child->ordering_token_ = parent->children_.back().ordering_token_ + 1;
89 }
90 parent->children_.push_back(std::move(child));
91 parent->vnode_->UpdateModified();
92 }
93
Lookup(fbl::StringPiece name,fbl::RefPtr<Dnode> * out) const94 zx_status_t Dnode::Lookup(fbl::StringPiece name, fbl::RefPtr<Dnode>* out) const {
95 auto dn = children_.find_if([&name](const Dnode& elem) -> bool {
96 return elem.NameMatch(name);
97 });
98 if (dn == children_.end()) {
99 return ZX_ERR_NOT_FOUND;
100 }
101
102 if (out != nullptr) {
103 *out = dn.CopyPointer();
104 }
105 return ZX_OK;
106 }
107
AcquireVnode() const108 fbl::RefPtr<VnodeMemfs> Dnode::AcquireVnode() const {
109 return vnode_;
110 }
111
CanUnlink() const112 zx_status_t Dnode::CanUnlink() const {
113 if (!children_.is_empty()) {
114 // Cannot unlink non-empty directory
115 return ZX_ERR_NOT_EMPTY;
116 } else if (vnode_->IsRemote()) {
117 // Cannot unlink mount points
118 return ZX_ERR_UNAVAILABLE;
119 }
120 return ZX_OK;
121 }
122
123 struct dircookie_t {
124 size_t order; // Minimum 'order' of the next dnode dirent to be read.
125 };
126
127 static_assert(sizeof(dircookie_t) <= sizeof(fs::vdircookie_t),
128 "MemFS dircookie too large to fit in IO state");
129
130 // Read the canned "." and ".." entries that should
131 // appear at the beginning of a directory.
ReaddirStart(fs::DirentFiller * df,void * cookie)132 zx_status_t Dnode::ReaddirStart(fs::DirentFiller* df, void* cookie) {
133 dircookie_t* c = static_cast<dircookie_t*>(cookie);
134 zx_status_t r;
135
136 if (c->order == 0) {
137 // TODO(smklein): Return the real ino.
138 uint64_t ino = fuchsia_io_INO_UNKNOWN;
139 if ((r = df->Next(".", VTYPE_TO_DTYPE(V_TYPE_DIR), ino)) != ZX_OK) {
140 return r;
141 }
142 c->order++;
143 }
144 return ZX_OK;
145 }
146
Readdir(fs::DirentFiller * df,void * cookie) const147 void Dnode::Readdir(fs::DirentFiller* df, void* cookie) const {
148 dircookie_t* c = static_cast<dircookie_t*>(cookie);
149 zx_status_t r = 0;
150
151 if (c->order < 1) {
152 if ((r = Dnode::ReaddirStart(df, cookie)) != ZX_OK) {
153 return;
154 }
155 }
156
157 for (const auto& dn : children_) {
158 if (dn.ordering_token_ < c->order) {
159 continue;
160 }
161 uint32_t vtype = dn.IsDirectory() ? V_TYPE_DIR : V_TYPE_FILE;
162 if ((r = df->Next(fbl::StringPiece(dn.name_.get(), dn.NameLen()),
163 VTYPE_TO_DTYPE(vtype), dn.AcquireVnode()->ino())) != ZX_OK) {
164 return;
165 }
166 c->order = dn.ordering_token_ + 1;
167 }
168 }
169
170 // Answers the question: "Is dn a subdirectory of this?"
IsSubdirectory(fbl::RefPtr<Dnode> dn) const171 bool Dnode::IsSubdirectory(fbl::RefPtr<Dnode> dn) const {
172 if (IsDirectory() && dn->IsDirectory()) {
173 // Iterate all the way up to root
174 while (dn->parent_ != nullptr && dn->parent_ != dn) {
175 if (vnode_ == dn->vnode_) {
176 return true;
177 }
178 dn = dn->parent_;
179 }
180 }
181 return false;
182 }
183
TakeName()184 fbl::unique_ptr<char[]> Dnode::TakeName() {
185 return std::move(name_);
186 }
187
PutName(fbl::unique_ptr<char[]> name,size_t len)188 void Dnode::PutName(fbl::unique_ptr<char[]> name, size_t len) {
189 flags_ = static_cast<uint32_t>((flags_ & ~kDnodeNameMax) | len);
190 name_ = std::move(name);
191 }
192
IsDirectory() const193 bool Dnode::IsDirectory() const { return vnode_->IsDirectory(); }
194
Dnode(fbl::RefPtr<VnodeMemfs> vn,fbl::unique_ptr<char[]> name,uint32_t flags)195 Dnode::Dnode(fbl::RefPtr<VnodeMemfs> vn, fbl::unique_ptr<char[]> name, uint32_t flags) :
196 vnode_(std::move(vn)), parent_(nullptr), ordering_token_(0), flags_(flags), name_(std::move(name)) {
197 };
198
NameLen() const199 size_t Dnode::NameLen() const {
200 return flags_ & kDnodeNameMax;
201 }
202
NameMatch(fbl::StringPiece name) const203 bool Dnode::NameMatch(fbl::StringPiece name) const {
204 return name == fbl::StringPiece(name_.get(), NameLen());
205 }
206
207 } // namespace memfs
208