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