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