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 "coordinator.h"
6 #include "devmgr.h"
7 #include "../shared/async-loop-owned-rpc-handler.h"
8 #include "../shared/log.h"
9 
10 #include <zircon/fidl.h>
11 #include <zircon/syscalls.h>
12 #include <zircon/types.h>
13 #include <zircon/device/vfs.h>
14 
15 #include <fbl/intrusive_double_list.h>
16 #include <fbl/string.h>
17 #include <fs/connection.h>
18 #include <fs/handler.h>
19 #include <fuchsia/io/c/fidl.h>
20 #include <lib/async/cpp/wait.h>
21 #include <lib/fdio/util.h>
22 #include <lib/fidl/coding.h>
23 #include <lib/memfs/cpp/vnode.h>
24 
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 namespace devmgr {
31 
32 namespace {
33 
34 uint64_t next_ino = 2;
35 
36 fbl::unique_ptr<Devnode> root_devnode;
37 
38 fbl::unique_ptr<Devnode> class_devnode;
39 
40 fbl::unique_ptr<Devnode> devfs_mkdir(Devnode* parent, const char* name);
41 
42 #define PNMAX 16
proto_name(uint32_t id,char buf[PNMAX])43 const char* proto_name(uint32_t id, char buf[PNMAX]) {
44     switch (id) {
45 #define DDK_PROTOCOL_DEF(tag, val, name, flags) case val: return name;
46 #include <ddk/protodefs.h>
47     default:
48         snprintf(buf, PNMAX, "proto-%08x", id);
49         return buf;
50     }
51 }
52 
53 zx::channel g_devfs_root;
54 
55 } // namespace
56 
57 struct Watcher : fbl::DoublyLinkedListable<fbl::unique_ptr<Watcher>> {
58     Watcher(Devnode* dn, zx::channel ch, uint32_t mask);
59 
60     Watcher(const Watcher&) = delete;
61     Watcher& operator=(const Watcher&) = delete;
62 
63     Watcher(Watcher&&) = delete;
64     Watcher& operator=(Watcher&&) = delete;
65 
66     Devnode* devnode = nullptr;
67     zx::channel handle;
68     uint32_t mask = 0;
69 };
70 
Watcher(Devnode * dn,zx::channel ch,uint32_t mask)71 Watcher::Watcher(Devnode* dn, zx::channel ch, uint32_t mask)
72     : devnode(dn), handle(std::move(ch)), mask(mask) {
73 }
74 
75 class DcIostate : public AsyncLoopOwnedRpcHandler<DcIostate> {
76 public:
77     explicit DcIostate(Devnode* dn);
78     ~DcIostate();
79 
80     // Claims ownership of |*h| on success
81     static zx_status_t Create(Devnode* dn, zx::channel* h);
82 
83     static zx_status_t DevfsFidlHandler(fidl_msg_t* msg, fidl_txn_t* txn, void* cookie);
84 
85     static void HandleRpc(fbl::unique_ptr<DcIostate> ios, async_dispatcher_t* dispatcher,
86                           async::WaitBase* wait, zx_status_t status,
87                           const zx_packet_signal_t* signal);
88 
89     struct Node {
node_statedevmgr::DcIostate::Node90         static fbl::DoublyLinkedListNodeState<DcIostate*>& node_state(DcIostate& obj) {
91             return obj.node_;
92         }
93     };
94     // Remove this DcIostate from its devnode
95     void DetachFromDevnode();
96 
97  private:
98     uint64_t readdir_ino_ = 0;
99 
100     // pointer to our devnode, nullptr if it has been removed
101     Devnode* devnode_ = nullptr;
102 
103     // entry in our devnode's iostate list
104     fbl::DoublyLinkedListNodeState<DcIostate*> node_;
105 };
106 
107 // BUG(ZX-2868): We currently never free these after allocating them
108 struct Devnode {
109     explicit Devnode(fbl::String name);
110 
111     Devnode(const Devnode&) = delete;
112     Devnode& operator=(const Devnode&) = delete;
113 
114     Devnode(Devnode&&) = delete;
115     Devnode& operator=(Devnode&&) = delete;
116 
117     fbl::String name;
118     uint64_t ino = 0;
119 
120     // nullptr if we are a pure directory node,
121     // otherwise the device we are referencing
122     Device* device = nullptr;
123 
124     fbl::DoublyLinkedList<fbl::unique_ptr<Watcher>> watchers;
125 
126     // entry in our parent devnode's children list
127     struct Node {
node_statedevmgr::Devnode::Node128         static fbl::DoublyLinkedListNodeState<Devnode*>& node_state(Devnode& obj) {
129             return obj.node;
130         }
131     };
132     fbl::DoublyLinkedListNodeState<Devnode*> node;
133 
134     // list of our child devnodes
135     fbl::DoublyLinkedList<Devnode*, Devnode::Node> children;
136 
137     // Pointer to our parent, for removing ourselves from its list of
138     // children. Our parent must outlive us.
139     Devnode* parent = nullptr;
140 
141     // list of attached iostates
142     fbl::DoublyLinkedList<DcIostate*, DcIostate::Node> iostate;
143 
144     // used to assign unique small device numbers
145     // for class device links
146     uint32_t seqcount = 0;
147 };
148 
149 namespace {
150 
151 struct ProtocolInfo {
152     const char* name;
153     Devnode* devnode;
154     uint32_t id;
155     uint32_t flags;
156 };
157 
158 ProtocolInfo proto_infos[] = {
159 #define DDK_PROTOCOL_DEF(tag, val, name, flags) { name, nullptr, val, flags },
160 #include <ddk/protodefs.h>
161 };
162 
proto_dir(uint32_t id)163 Devnode* proto_dir(uint32_t id) {
164     for (const auto& info : proto_infos) {
165         if (info.id == id) {
166             return info.devnode;
167         }
168     }
169     return nullptr;
170 }
171 
prepopulate_protocol_dirs()172 void prepopulate_protocol_dirs() {
173     class_devnode = devfs_mkdir(root_devnode.get(), "class");
174     for (auto& info : proto_infos) {
175         if (!(info.flags & PF_NOPUB)) {
176             info.devnode = devfs_mkdir(class_devnode.get(), info.name).release();
177         }
178     }
179 }
180 
describe_error(zx::channel h,zx_status_t status)181 void describe_error(zx::channel h, zx_status_t status) {
182     fuchsia_io_NodeOnOpenEvent msg;
183     memset(&msg, 0, sizeof(msg));
184     msg.hdr.ordinal = fuchsia_io_NodeOnOpenOrdinal;
185     msg.s = status;
186     h.write(0, &msg, sizeof(msg), nullptr, 0);
187 }
188 
189 // A devnode is a directory (from stat's perspective) if
190 // it has children, or if it doesn't have a device, or if
191 // its device has no rpc handle
devnode_is_dir(const Devnode * dn)192 bool devnode_is_dir(const Devnode* dn) {
193     if (dn->children.is_empty()) {
194         return (dn->device == nullptr) || (dn->device->hrpc == ZX_HANDLE_INVALID);
195     }
196     return true;
197 }
198 
199 // Local devnodes are ones that we should not hand off OPEN
200 // RPCs to the underlying devhost
devnode_is_local(Devnode * dn)201 bool devnode_is_local(Devnode* dn) {
202     if (dn->device == nullptr) {
203         return true;
204     }
205     if (dn->device->hrpc == ZX_HANDLE_INVALID) {
206         return true;
207     }
208     if (dn->device->flags & DEV_CTX_MUST_ISOLATE) {
209         return true;
210     }
211     return false;
212 }
213 
214 // Notify a single watcher about the given operation and path.  On failure,
215 // frees the watcher.  This can only be called on a watcher that has not yet
216 // been added to a Devnode's watchers list.
devfs_notify_single(fbl::unique_ptr<Watcher> * watcher,const fbl::String & name,unsigned op)217 void devfs_notify_single(fbl::unique_ptr<Watcher>* watcher,
218                          const fbl::String& name, unsigned op) {
219     size_t len = name.length();
220     if (!*watcher || len > fuchsia_io_MAX_FILENAME) {
221         return;
222     }
223 
224     ZX_ASSERT(!(*watcher)->InContainer());
225 
226     uint8_t msg[fuchsia_io_MAX_FILENAME + 2];
227     const uint32_t msg_len = static_cast<uint32_t>(len + 2);
228     msg[0] = static_cast<uint8_t>(op);
229     msg[1] = static_cast<uint8_t>(len);
230     memcpy(msg + 2, name.c_str(), len);
231 
232     // convert to mask
233     op = (1u << op);
234 
235     if (!((*watcher)->mask & op)) {
236         return;
237     }
238 
239     if ((*watcher)->handle.write(0, msg, msg_len, nullptr, 0) != ZX_OK) {
240         watcher->reset();
241     }
242 }
243 
devfs_notify(Devnode * dn,const fbl::String & name,unsigned op)244 void devfs_notify(Devnode* dn, const fbl::String& name, unsigned op) {
245     if (dn->watchers.is_empty()) {
246         return;
247     }
248 
249     size_t len = name.length();
250     if (len > fuchsia_io_MAX_FILENAME) {
251         return;
252     }
253 
254     uint8_t msg[fuchsia_io_MAX_FILENAME + 2];
255     const uint32_t msg_len = static_cast<uint32_t>(len + 2);
256     msg[0] = static_cast<uint8_t>(op);
257     msg[1] = static_cast<uint8_t>(len);
258     memcpy(msg + 2, name.c_str(), len);
259 
260     // convert to mask
261     op = (1u << op);
262 
263     for (auto itr = dn->watchers.begin(); itr != dn->watchers.end(); ) {
264         auto& cur = *itr;
265         // Advance the iterator now instead of at the end of the loop because we
266         // may erase the current element from the list.
267         ++itr;
268 
269         if (!(cur.mask & op)) {
270             continue;
271         }
272 
273         if (cur.handle.write(0, msg, msg_len, nullptr, 0) != ZX_OK) {
274             dn->watchers.erase(cur);
275             // The Watcher is free'd here
276         }
277     }
278 }
279 
devfs_watch(Devnode * dn,zx::channel h,uint32_t mask)280 zx_status_t devfs_watch(Devnode* dn, zx::channel h, uint32_t mask) {
281     auto watcher = fbl::make_unique<Watcher>(dn, std::move(h), mask);
282     if (watcher == nullptr) {
283         return ZX_ERR_NO_MEMORY;
284     }
285 
286     // If the watcher has asked for all existing entries, send it all of them
287     // followed by the end-of-existing marker (IDLE).
288     if (mask & fuchsia_io_WATCH_MASK_EXISTING) {
289         for (const auto& child : dn->children) {
290             if (child.device && (child.device->flags & DEV_CTX_INVISIBLE)) {
291                 continue;
292             }
293             //TODO: send multiple per write
294             devfs_notify_single(&watcher, child.name, fuchsia_io_WATCH_EVENT_EXISTING);
295         }
296         devfs_notify_single(&watcher, "", fuchsia_io_WATCH_EVENT_IDLE);
297     }
298 
299     // Watcher may have been freed by devfs_notify_single, so check before
300     // adding.
301     if (watcher) {
302         dn->watchers.push_front(std::move(watcher));
303     }
304     return ZX_OK;
305 }
306 
devfs_mknode(Device * dev,const char * name)307 fbl::unique_ptr<Devnode> devfs_mknode(Device* dev, const char* name) {
308     auto dn = fbl::make_unique<Devnode>(name);
309     if (!dn) {
310         return nullptr;
311     }
312     dn->ino = next_ino++;
313     dn->device = dev;
314     return dn;
315 }
316 
devfs_mkdir(Devnode * parent,const char * name)317 fbl::unique_ptr<Devnode> devfs_mkdir(Devnode* parent, const char* name) {
318     fbl::unique_ptr<Devnode> dn = devfs_mknode(nullptr, name);
319     if (dn == nullptr) {
320         return nullptr;
321     }
322     dn->parent = parent;
323     parent->children.push_back(dn.get());
324     return dn;
325 }
326 
devfs_lookup(Devnode * parent,const char * name)327 Devnode* devfs_lookup(Devnode* parent, const char* name) {
328     for (auto& child : parent->children) {
329         if (!strcmp(name, child.name.c_str())) {
330             return &child;
331         }
332     }
333     return nullptr;
334 }
335 
fill_dirent(vdirent_t * de,size_t delen,uint64_t ino,const fbl::String & name,uint8_t type)336 zx_status_t fill_dirent(vdirent_t* de, size_t delen, uint64_t ino,
337                                const fbl::String& name, uint8_t type) {
338     size_t len = name.length();
339     size_t sz = sizeof(vdirent_t) + len;
340 
341     if (sz > delen || len > NAME_MAX) {
342         return ZX_ERR_INVALID_ARGS;
343     }
344     de->ino = ino;
345     de->size = static_cast<uint8_t>(len);
346     de->type = type;
347     memcpy(de->name, name.c_str(), len);
348     return static_cast<zx_status_t>(sz);
349 }
350 
devfs_readdir(Devnode * dn,uint64_t * ino_inout,void * data,size_t len)351 zx_status_t devfs_readdir(Devnode* dn, uint64_t* ino_inout, void* data, size_t len) {
352     char* ptr = static_cast<char*>(data);
353     uint64_t ino = *ino_inout;
354 
355     for (const auto& child : dn->children) {
356         if (child.ino <= ino) {
357             continue;
358         }
359         if (child.device == nullptr) {
360             // "pure" directories (like /dev/class/$NAME) do not show up
361             // if they have no children, to avoid clutter and confusion.
362             // They remain openable, so they can be watched.
363             if (child.children.is_empty()) {
364                 continue;
365             }
366         } else {
367             // invisible devices also do not show up
368             if (child.device->flags & DEV_CTX_INVISIBLE) {
369                 continue;
370             }
371         }
372         ino = child.ino;
373         auto vdirent = reinterpret_cast<vdirent_t*>(ptr);
374         zx_status_t r = fill_dirent(vdirent, len, ino, child.name,
375                                     VTYPE_TO_DTYPE(V_TYPE_DIR));
376         if (r < 0) {
377             break;
378         }
379         ptr += r;
380         len -= r;
381     }
382 
383     *ino_inout = ino;
384     return static_cast<zx_status_t>(ptr - static_cast<char*>(data));
385 }
386 
devfs_walk(Devnode ** dn_inout,char * path,char ** pathout)387 zx_status_t devfs_walk(Devnode** dn_inout, char* path, char** pathout) {
388     Devnode* dn = *dn_inout;
389 
390 again:
391     if ((path == nullptr) || (path[0] == 0)) {
392         *dn_inout = dn;
393         return ZX_OK;
394     }
395     char* name = path;
396     char* undo = nullptr;
397     if ((path = strchr(path, '/')) != nullptr) {
398         undo = path;
399         *path++ = 0;
400     }
401     if (name[0] == 0) {
402         return ZX_ERR_BAD_PATH;
403     }
404     for (auto& child : dn->children) {
405         if (!strcmp(child.name.c_str(), name)) {
406             if(child.device && (child.device->flags & DEV_CTX_INVISIBLE)) {
407                 continue;
408             }
409             dn = &child;
410             goto again;
411         }
412     }
413     if (dn == *dn_inout) {
414         return ZX_ERR_NOT_FOUND;
415     }
416     if (undo) {
417         *undo = '/';
418     }
419     *dn_inout = dn;
420     *pathout = name;
421     return ZX_ERR_NEXT;
422 }
423 
devfs_open(Devnode * dirdn,zx_handle_t h,char * path,uint32_t flags)424 void devfs_open(Devnode* dirdn, zx_handle_t h, char* path, uint32_t flags) {
425     zx::channel ipc(h);
426     h = ZX_HANDLE_INVALID;
427 
428     if (!strcmp(path, ".")) {
429         path = nullptr;
430     }
431 
432     Devnode* dn = dirdn;
433     zx_status_t r = devfs_walk(&dn, path, &path);
434 
435     bool describe = flags & ZX_FS_FLAG_DESCRIBE;
436 
437     bool no_remote = (dn->device == nullptr) || (dn->device->hrpc == ZX_HANDLE_INVALID);
438     bool local_required = devnode_is_local(dn);
439     bool local_requested = flags & (ZX_FS_FLAG_NOREMOTE | ZX_FS_FLAG_DIRECTORY);
440 
441     if (r == ZX_ERR_NEXT) {
442         // We only partially matched -- there's more path to walk.
443         if (no_remote || local_required) {
444             // No remote to pass this on to.
445             r = ZX_ERR_NOT_FOUND;
446         } else if (local_requested) {
447             // Local requested, but this is remote only.
448             r = ZX_ERR_NOT_SUPPORTED;
449         } else {
450             // There is more path to walk, and this node can accept
451             // remote requests.
452             r = ZX_OK;
453         }
454     } else {
455         path = (char*) ".";
456     }
457 
458     if (r != ZX_OK) {
459         if (describe) {
460             describe_error(std::move(ipc), r);
461         }
462         return;
463     }
464 
465     // If we are a local-only node, or we are asked to not go remote,
466     // or we are asked to open-as-a-directory, open locally:
467     if (local_requested || local_required) {
468         zx::unowned_channel unowned_ipc(ipc);
469         if ((r = DcIostate::Create(dn, &ipc)) != ZX_OK) {
470             if (describe) {
471                 describe_error(std::move(ipc), r);
472             }
473             return;
474         }
475         if (describe) {
476             fs::OnOpenMsg msg;
477             memset(&msg, 0, sizeof(msg));
478             msg.primary.hdr.ordinal = fuchsia_io_NodeOnOpenOrdinal;
479             msg.primary.s = ZX_OK;
480             msg.primary.info = (fuchsia_io_NodeInfo*)FIDL_ALLOC_PRESENT;
481             msg.extra.tag = fuchsia_io_NodeInfoTag_directory;
482 
483             // This is safe because this is executing on the same thread as the
484             // DcAsyncLoop(), so the handle can't be closed underneath us.
485             unowned_ipc->write(0, &msg, sizeof(msg), nullptr, 0);
486         }
487         return;
488     }
489 
490     // Otherwise we will pass the request on to the remote.
491     fuchsia_io_DirectoryOpen(dn->device->hrpc.get(), flags, 0, path, strlen(path), ipc.release());
492 }
493 
devfs_remove(Devnode * dn)494 void devfs_remove(Devnode* dn) {
495     if (dn->node.InContainer()) {
496         dn->parent->children.erase(*dn);
497     }
498 
499     // detach all connected iostates
500     while (!dn->iostate.is_empty()) {
501         dn->iostate.pop_front()->DetachFromDevnode();
502     }
503 
504     // notify own file watcher
505     if ((dn->device == nullptr) ||
506         !(dn->device->flags & DEV_CTX_INVISIBLE)) {
507         devfs_notify(dn, "", fuchsia_io_WATCH_EVENT_DELETED);
508     }
509 
510     // disconnect from device and notify parent/link directory watchers
511     if (dn->device != nullptr) {
512         if (dn->device->self == dn) {
513             dn->device->self = nullptr;
514 
515             if ((dn->device->parent != nullptr) &&
516                 (dn->device->parent->self != nullptr) &&
517                 !(dn->device->flags & DEV_CTX_INVISIBLE)) {
518                 devfs_notify(dn->device->parent->self, dn->name, fuchsia_io_WATCH_EVENT_REMOVED);
519             }
520         }
521         if (dn->device->link == dn) {
522             dn->device->link = nullptr;
523 
524             if (!(dn->device->flags & DEV_CTX_INVISIBLE)) {
525                 Devnode* dir = proto_dir(dn->device->protocol_id);
526                 devfs_notify(dir, dn->name, fuchsia_io_WATCH_EVENT_REMOVED);
527             }
528         }
529         dn->device = nullptr;
530     }
531 
532     // destroy all watchers
533     dn->watchers.clear();
534 
535     // detach children
536     while ((dn->children.pop_front() != nullptr)) {
537         // they will be unpublished when the devices they're
538         // associated with are eventually destroyed
539     }
540 }
541 
542 } // namespace
543 
Devnode(fbl::String name)544 Devnode::Devnode(fbl::String name)
545     : name(std::move(name)) {
546 }
547 
DcIostate(Devnode * dn)548 DcIostate::DcIostate(Devnode* dn) : devnode_(dn) {
549 
550     devnode_->iostate.push_back(this);
551 }
552 
Create(Devnode * dn,zx::channel * ipc)553 zx_status_t DcIostate::Create(Devnode* dn, zx::channel* ipc) {
554     auto ios = fbl::make_unique<DcIostate>(dn);
555     if (ios == nullptr) {
556         return ZX_ERR_NO_MEMORY;
557     }
558 
559     ios->set_channel(std::move(*ipc));
560     zx_status_t status = DcIostate::BeginWait(&ios, DcAsyncLoop()->dispatcher());
561     if (status != ZX_OK) {
562         // Take the handle back from |ios| so it doesn't close it when it's
563         // destroyed
564         *ipc = ios->set_channel(zx::channel());
565     }
566     return status;
567 }
568 
DetachFromDevnode()569 void DcIostate::DetachFromDevnode() {
570     if (devnode_) {
571         devnode_->iostate.erase(*this);
572         devnode_ = nullptr;
573     }
574     set_channel(zx::channel());
575 }
576 
~DcIostate()577 DcIostate::~DcIostate() {
578     DetachFromDevnode();
579 }
580 
devfs_advertise(Device * dev)581 void devfs_advertise(Device* dev) {
582     if (dev->link) {
583         Devnode* dir = proto_dir(dev->protocol_id);
584         devfs_notify(dir, dev->link->name, fuchsia_io_WATCH_EVENT_ADDED);
585     }
586     if (dev->parent && dev->parent->self) {
587         devfs_notify(dev->parent->self, dev->self->name, fuchsia_io_WATCH_EVENT_ADDED);
588     }
589 }
590 
591 // TODO: generate a MODIFIED event rather than back to back REMOVED and ADDED
devfs_advertise_modified(Device * dev)592 void devfs_advertise_modified(Device* dev) {
593     if (dev->link) {
594         Devnode* dir = proto_dir(dev->protocol_id);
595         devfs_notify(dir, dev->link->name, fuchsia_io_WATCH_EVENT_REMOVED);
596         devfs_notify(dir, dev->link->name, fuchsia_io_WATCH_EVENT_ADDED);
597     }
598     if (dev->parent && dev->parent->self) {
599         devfs_notify(dev->parent->self, dev->self->name, fuchsia_io_WATCH_EVENT_REMOVED);
600         devfs_notify(dev->parent->self, dev->self->name, fuchsia_io_WATCH_EVENT_ADDED);
601     }
602 }
603 
devfs_publish(Device * parent,Device * dev)604 zx_status_t devfs_publish(Device* parent, Device* dev) {
605     if ((parent->self == nullptr) || (dev->self != nullptr) || (dev->link != nullptr)) {
606         return ZX_ERR_INTERNAL;
607     }
608 
609     fbl::unique_ptr<Devnode> dnself = devfs_mknode(dev, dev->name);
610     if (dnself == nullptr) {
611         return ZX_ERR_NO_MEMORY;
612     }
613 
614     if ((dev->protocol_id == ZX_PROTOCOL_TEST_PARENT) ||
615         (dev->protocol_id == ZX_PROTOCOL_MISC_PARENT) ||
616         (dev->protocol_id == ZX_PROTOCOL_MISC)) {
617         // misc devices are singletons, not a class
618         // in the sense of other device classes.
619         // They do not get aliases in /dev/class/misc/...
620         // instead they exist only under their parent
621         // device.
622         goto done;
623     }
624 
625     // Create link in /dev/class/... if this id has a published class
626     Devnode* dir;
627     dir = proto_dir(dev->protocol_id);
628     if (dir != nullptr) {
629         char tmp[32];
630         const char* name = dev->name;
631 
632         if (dev->protocol_id != ZX_PROTOCOL_CONSOLE) {
633 
634             for (unsigned n = 0; n < 1000; n++) {
635                 snprintf(tmp, sizeof(tmp), "%03u", (dir->seqcount++) % 1000);
636                 if (devfs_lookup(dir, tmp) == nullptr) {
637                     name = tmp;
638                     goto got_name;
639                 }
640             }
641             return ZX_ERR_ALREADY_EXISTS;
642 got_name:
643             ;
644         }
645 
646         fbl::unique_ptr<Devnode> dnlink = devfs_mknode(dev, name);
647         if (dnlink == nullptr) {
648             return ZX_ERR_NO_MEMORY;
649         }
650 
651         // add link node to class directory
652         dnlink->parent = dir;
653         dir->children.push_back(dnlink.get());
654         dev->link = dnlink.release();
655     }
656 
657 done:
658     // add self node to parent directory
659     dnself->parent = parent->self;
660     parent->self->children.push_back(dnself.get());
661     dev->self = dnself.release();
662 
663     if (!(dev->flags & DEV_CTX_INVISIBLE)) {
664         devfs_advertise(dev);
665     }
666     return ZX_OK;
667 }
668 
devfs_unpublish(Device * dev)669 void devfs_unpublish(Device* dev) {
670     if (dev->self != nullptr) {
671         devfs_remove(dev->self);
672         dev->self = nullptr;
673     }
674     if (dev->link != nullptr) {
675         devfs_remove(dev->link);
676         dev->link = nullptr;
677     }
678 }
679 
680 // Helper macros for |DevfsFidlHandler| which make it easier
681 // avoid typing generated names.
682 
683 // Decode the incoming request, returning an error and consuming
684 // all handles on error.
685 #define DECODE_REQUEST(MSG, METHOD)                                     \
686     do {                                                                \
687         zx_status_t r;                                                  \
688         if ((r = fidl_decode_msg(&fuchsia_io_ ## METHOD ##RequestTable, \
689                                  msg, nullptr)) != ZX_OK) {                \
690             return r;                                                   \
691         }                                                               \
692     } while(0);
693 
694 // Define a variable |request| from the incoming method, of
695 // the requested type.
696 #define DEFINE_REQUEST(MSG, METHOD)                     \
697     fuchsia_io_ ## METHOD ## Request* request =         \
698         (fuchsia_io_ ## METHOD ## Request*) MSG->bytes;
699 
700 
DevfsFidlHandler(fidl_msg_t * msg,fidl_txn_t * txn,void * cookie)701 zx_status_t DcIostate::DevfsFidlHandler(fidl_msg_t* msg, fidl_txn_t* txn, void* cookie) {
702     auto ios = static_cast<DcIostate*>(cookie);
703     Devnode* dn = ios->devnode_;
704     if (dn == nullptr) {
705         return ZX_ERR_PEER_CLOSED;
706     }
707 
708     auto hdr = static_cast<fidl_message_header_t*>(msg->bytes);
709 
710     zx_status_t r;
711     switch (hdr->ordinal) {
712     case fuchsia_io_NodeCloneOrdinal: {
713         DECODE_REQUEST(msg, NodeClone);
714         DEFINE_REQUEST(msg, NodeClone);
715         zx_handle_t h = request->object;
716         uint32_t flags = request->flags;
717         char path[PATH_MAX];
718         path[0] = '\0';
719         devfs_open(dn, h, path, flags | ZX_FS_FLAG_NOREMOTE);
720         return ZX_OK;
721     }
722     case fuchsia_io_NodeDescribeOrdinal: {
723         DECODE_REQUEST(msg, NodeDescribe);
724         fuchsia_io_NodeInfo info;
725         memset(&info, 0, sizeof(info));
726         info.tag = fuchsia_io_NodeInfoTag_directory;
727         return fuchsia_io_NodeDescribe_reply(txn, &info);
728     }
729     case fuchsia_io_DirectoryOpenOrdinal: {
730         DECODE_REQUEST(msg, DirectoryOpen);
731         DEFINE_REQUEST(msg, DirectoryOpen);
732         uint32_t len = static_cast<uint32_t>(request->path.size);
733         char* path = request->path.data;
734         zx_handle_t h = request->object;
735         uint32_t flags = request->flags;
736         if ((len < 1) || (len > 1024)) {
737             zx_handle_close(h);
738         } else {
739             path[len] = '\0';
740             devfs_open(dn, h, path, flags);
741         }
742         return ZX_OK;
743     }
744     case fuchsia_io_NodeGetAttrOrdinal: {
745         DECODE_REQUEST(msg, NodeGetAttr);
746         uint32_t mode;
747         if (devnode_is_dir(dn)) {
748             mode = V_TYPE_DIR | V_IRUSR | V_IWUSR;
749         } else {
750             mode = V_TYPE_CDEV | V_IRUSR | V_IWUSR;
751         }
752 
753         fuchsia_io_NodeAttributes attributes;
754         memset(&attributes, 0, sizeof(attributes));
755         attributes.mode = mode;
756         attributes.content_size = 0;
757         attributes.link_count = 1;
758         attributes.id = dn->ino;
759         return fuchsia_io_NodeGetAttr_reply(txn, ZX_OK, &attributes);
760     }
761     case fuchsia_io_DirectoryRewindOrdinal: {
762         DECODE_REQUEST(msg, DirectoryRewind);
763         ios->readdir_ino_ = 0;
764         return fuchsia_io_DirectoryRewind_reply(txn, ZX_OK);
765     }
766     case fuchsia_io_DirectoryReadDirentsOrdinal: {
767         DECODE_REQUEST(msg, DirectoryReadDirents);
768         DEFINE_REQUEST(msg, DirectoryReadDirents);
769 
770         if (request->max_bytes > ZXFIDL_MAX_MSG_BYTES) {
771             return fuchsia_io_DirectoryReadDirents_reply(txn, ZX_ERR_INVALID_ARGS, nullptr, 0);
772         }
773 
774         uint8_t data[request->max_bytes];
775         size_t actual = 0;
776         r = devfs_readdir(dn, &ios->readdir_ino_, data, request->max_bytes);
777         if (r >= 0) {
778             actual = r;
779             r = ZX_OK;
780         }
781         return fuchsia_io_DirectoryReadDirents_reply(txn, r, data, actual);
782     }
783     case fuchsia_io_DirectoryWatchOrdinal: {
784         DECODE_REQUEST(msg, DirectoryWatch);
785         DEFINE_REQUEST(msg, DirectoryWatch);
786         zx::channel watcher(request->watcher);
787 
788         request->watcher = ZX_HANDLE_INVALID;
789         if (request->mask & (~fuchsia_io_WATCH_MASK_ALL) || request->options != 0) {
790             return fuchsia_io_DirectoryWatch_reply(txn, ZX_ERR_INVALID_ARGS);
791         }
792         r = devfs_watch(dn, std::move(watcher), request->mask);
793         return fuchsia_io_DirectoryWatch_reply(txn, r);
794     }
795     case fuchsia_io_DirectoryAdminQueryFilesystemOrdinal: {
796         DECODE_REQUEST(msg, DirectoryAdminQueryFilesystem);
797         fuchsia_io_FilesystemInfo info;
798         memset(&info, 0, sizeof(info));
799         strlcpy((char*) info.name, "devfs", fuchsia_io_MAX_FS_NAME_BUFFER);
800         return fuchsia_io_DirectoryAdminQueryFilesystem_reply(txn, ZX_OK, &info);
801     }
802     case fuchsia_io_NodeIoctlOrdinal: {
803         DECODE_REQUEST(msg, NodeIoctl);
804         zx_handle_close_many(msg->handles, msg->num_handles);
805         return fuchsia_io_NodeIoctl_reply(txn, ZX_ERR_NOT_SUPPORTED, nullptr, 0, nullptr, 0);
806     }
807     }
808 
809     // close inbound handles so they do not leak
810     zx_handle_close_many(msg->handles, msg->num_handles);
811     return ZX_ERR_NOT_SUPPORTED;
812 }
813 
HandleRpc(fbl::unique_ptr<DcIostate> ios,async_dispatcher_t * dispatcher,async::WaitBase * wait,zx_status_t status,const zx_packet_signal_t * signal)814 void DcIostate::HandleRpc(fbl::unique_ptr<DcIostate> ios, async_dispatcher_t* dispatcher,
815                           async::WaitBase* wait, zx_status_t status,
816                           const zx_packet_signal_t* signal) {
817     if (status != ZX_OK) {
818         log(ERROR, "devcoord: DcIostate::HandleRpc aborting, saw status %d\n", status);
819         return;
820     }
821 
822     if (signal->observed & ZX_CHANNEL_READABLE) {
823         status = fs::ReadMessage(wait->object(), [&ios](fidl_msg_t* msg, fs::FidlConnection* txn) {
824             return DcIostate::DevfsFidlHandler(msg, txn->Txn(), ios.get());
825         });
826         if (status == ZX_OK) {
827             ios->BeginWait(std::move(ios), dispatcher);
828             return;
829         }
830     } else if (signal->observed & ZX_CHANNEL_PEER_CLOSED) {
831         fs::CloseMessage([&ios](fidl_msg_t* msg, fs::FidlConnection* txn) {
832             return DcIostate::DevfsFidlHandler(msg, txn->Txn(), ios.get());
833         });
834     } else {
835         log(ERROR, "devcoord: DcIostate::HandleRpc: invalid signals %x\n", signal->observed);
836         exit(0);
837     }
838     // Do not start waiting again, and destroy |ios|
839 }
840 
devfs_root_borrow()841 zx::unowned_channel devfs_root_borrow() {
842     return zx::unowned_channel(g_devfs_root);
843 }
844 
devfs_root_clone()845 zx::channel devfs_root_clone() {
846     return zx::channel(fdio_service_clone(g_devfs_root.get()));
847 }
848 
devfs_init(const zx::job & root_job)849 void devfs_init(const zx::job& root_job) {
850     printf("devmgr: init\n");
851 
852     root_devnode = fbl::make_unique<Devnode>("");
853     if (!root_devnode) {
854         printf("devmgr: failed to allocate devfs root node\n");
855         return;
856     }
857     root_devnode->ino = 1;
858 
859     prepopulate_protocol_dirs();
860 
861     root_devnode->device = coordinator_init(root_job);
862     root_devnode->device->self = root_devnode.get();
863 
864     zx::channel h0, h1;
865     if (zx::channel::create(0, &h0, &h1) != ZX_OK) {
866         return;
867     } else if (DcIostate::Create(root_devnode.get(), &h0) != ZX_OK) {
868         return;
869     }
870 
871     g_devfs_root = std::move(h1);
872 }
873 
874 } // namespace devmgr
875