1 // Copyright 2017 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 <fs/pseudo-dir.h>
6 
7 #include <sys/stat.h>
8 
9 #include <fbl/auto_lock.h>
10 #include <fuchsia/io/c/fidl.h>
11 
12 #include <utility>
13 
14 namespace fs {
15 
16 PseudoDir::PseudoDir() = default;
17 
~PseudoDir()18 PseudoDir::~PseudoDir() {
19   entries_by_name_.clear_unsafe();
20   entries_by_id_.clear();
21 }
22 
Open(uint32_t flags,fbl::RefPtr<Vnode> * out_redirect)23 zx_status_t PseudoDir::Open(uint32_t flags, fbl::RefPtr<Vnode>* out_redirect) {
24     return ZX_OK;
25 }
26 
Getattr(vnattr_t * attr)27 zx_status_t PseudoDir::Getattr(vnattr_t* attr) {
28     memset(attr, 0, sizeof(vnattr_t));
29     attr->mode = V_TYPE_DIR | V_IRUSR;
30     attr->nlink = 1;
31     return ZX_OK;
32 }
33 
Lookup(fbl::RefPtr<fs::Vnode> * out,fbl::StringPiece name)34 zx_status_t PseudoDir::Lookup(fbl::RefPtr<fs::Vnode>* out, fbl::StringPiece name) {
35     fbl::AutoLock lock(&mutex_);
36 
37     auto it = entries_by_name_.find(name);
38     if (it != entries_by_name_.end()) {
39         *out = it->node();
40         return ZX_OK;
41     }
42 
43     return ZX_ERR_NOT_FOUND;
44 }
45 
Notify(fbl::StringPiece name,unsigned event)46 void PseudoDir::Notify(fbl::StringPiece name, unsigned event) {
47     watcher_.Notify(name, event);
48 }
49 
WatchDir(fs::Vfs * vfs,uint32_t mask,uint32_t options,zx::channel watcher)50 zx_status_t PseudoDir::WatchDir(fs::Vfs* vfs, uint32_t mask, uint32_t options,
51                                 zx::channel watcher) {
52     return watcher_.WatchDir(vfs, this, mask, options, std::move(watcher));
53 }
54 
Readdir(vdircookie_t * cookie,void * data,size_t len,size_t * out_actual)55 zx_status_t PseudoDir::Readdir(vdircookie_t* cookie, void* data, size_t len, size_t* out_actual) {
56     fs::DirentFiller df(data, len);
57     zx_status_t r = 0;
58     if (cookie->n < kDotId) {
59         uint64_t ino = fuchsia_io_INO_UNKNOWN;
60         if ((r = df.Next(".", VTYPE_TO_DTYPE(V_TYPE_DIR), ino)) != ZX_OK) {
61             *out_actual = df.BytesFilled();
62             return r;
63         }
64         cookie->n = kDotId;
65     }
66 
67     fbl::AutoLock lock(&mutex_);
68 
69     for (auto it = entries_by_id_.lower_bound(cookie->n); it != entries_by_id_.end(); ++it) {
70         if (cookie->n >= it->id()) {
71             continue;
72         }
73         vnattr_t attr;
74         if ((r = it->node()->Getattr(&attr)) != ZX_OK) {
75             continue;
76         }
77         if (df.Next(it->name().ToStringPiece(),
78                          VTYPE_TO_DTYPE(attr.mode), attr.inode) != ZX_OK) {
79             *out_actual = df.BytesFilled();
80             return ZX_OK;
81         }
82         cookie->n = it->id();
83     }
84 
85     *out_actual = df.BytesFilled();
86     return ZX_OK;
87 }
88 
AddEntry(fbl::String name,fbl::RefPtr<fs::Vnode> vn)89 zx_status_t PseudoDir::AddEntry(fbl::String name, fbl::RefPtr<fs::Vnode> vn) {
90     ZX_DEBUG_ASSERT(vn);
91 
92     if (!vfs_valid_name(name.ToStringPiece())) {
93         return ZX_ERR_INVALID_ARGS;
94     }
95 
96     fbl::AutoLock lock(&mutex_);
97 
98     if (entries_by_name_.find(name) != entries_by_name_.end()) {
99         return ZX_ERR_ALREADY_EXISTS;
100     }
101 
102     Notify(name.ToStringPiece(), fuchsia_io_WATCH_EVENT_ADDED);
103     auto entry = fbl::make_unique<Entry>(next_node_id_++,
104                                          std::move(name), std::move(vn));
105     entries_by_name_.insert(entry.get());
106     entries_by_id_.insert(std::move(entry));
107     return ZX_OK;
108 }
109 
RemoveEntry(fbl::StringPiece name)110 zx_status_t PseudoDir::RemoveEntry(fbl::StringPiece name) {
111     fbl::AutoLock lock(&mutex_);
112 
113     auto it = entries_by_name_.find(name);
114     if (it != entries_by_name_.end()) {
115         entries_by_name_.erase(it);
116         entries_by_id_.erase(it->id());
117         Notify(name, fuchsia_io_WATCH_EVENT_REMOVED);
118         return ZX_OK;
119     }
120 
121     return ZX_ERR_NOT_FOUND;
122 }
123 
RemoveAllEntries()124 void PseudoDir::RemoveAllEntries() {
125     fbl::AutoLock lock(&mutex_);
126 
127     for (auto& entry : entries_by_name_) {
128         Notify(entry.name().ToStringPiece(), fuchsia_io_WATCH_EVENT_REMOVED);
129     }
130     entries_by_name_.clear();
131     entries_by_id_.clear();
132 }
133 
IsEmpty() const134 bool PseudoDir::IsEmpty() const {
135     fbl::AutoLock lock(&mutex_);
136     return entries_by_name_.is_empty();
137 }
138 
Entry(uint64_t id,fbl::String name,fbl::RefPtr<fs::Vnode> node)139 PseudoDir::Entry::Entry(uint64_t id, fbl::String name, fbl::RefPtr<fs::Vnode> node)
140     : id_(id), name_(std::move(name)), node_(std::move(node)) {
141 }
142 
143 PseudoDir::Entry::~Entry() = default;
144 
145 } // namespace fs
146