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 <assert.h>
6 #include <fcntl.h>
7 #include <limits.h>
8 #include <poll.h>
9 #include <pthread.h>
10 #include <stdatomic.h>
11 #include <stddef.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <threads.h>
16 
17 #include <zircon/assert.h>
18 #include <zircon/device/device.h>
19 #include <zircon/device/ioctl.h>
20 #include <zircon/device/vfs.h>
21 #include <zircon/processargs.h>
22 #include <zircon/syscalls.h>
23 
24 #include <fuchsia/io/c/fidl.h>
25 #include <lib/fdio/debug.h>
26 #include <lib/fdio/io.h>
27 #include <lib/fdio/namespace.h>
28 #include <lib/fdio/util.h>
29 #include <lib/fdio/vfs.h>
30 
31 #include "private-remoteio.h"
32 
33 #define ZXDEBUG 0
34 
35 // POLL_MASK and POLL_SHIFT intend to convert the lower five POLL events into
36 // ZX_USER_SIGNALs and vice-versa. Other events need to be manually converted to
37 // a zx_signals_t, if they are desired.
38 #define POLL_SHIFT  24
39 #define POLL_MASK   0x1F
40 
41 static_assert(FDIO_CHUNK_SIZE >= PATH_MAX,
42               "FDIO_CHUNK_SIZE must be large enough to contain paths");
43 
44 static_assert(fuchsia_io_VMO_FLAG_READ == ZX_VM_PERM_READ,
45               "Vmar / Vmo flags should be aligned");
46 static_assert(fuchsia_io_VMO_FLAG_WRITE == ZX_VM_PERM_WRITE,
47               "Vmar / Vmo flags should be aligned");
48 static_assert(fuchsia_io_VMO_FLAG_EXEC == ZX_VM_PERM_EXECUTE,
49               "Vmar / Vmo flags should be aligned");
50 
51 static_assert(ZX_USER_SIGNAL_0 == (1 << POLL_SHIFT), "");
52 static_assert((POLLIN << POLL_SHIFT) == DEVICE_SIGNAL_READABLE, "");
53 static_assert((POLLPRI << POLL_SHIFT) == DEVICE_SIGNAL_OOB, "");
54 static_assert((POLLOUT << POLL_SHIFT) == DEVICE_SIGNAL_WRITABLE, "");
55 static_assert((POLLERR << POLL_SHIFT) == DEVICE_SIGNAL_ERROR, "");
56 static_assert((POLLHUP << POLL_SHIFT) == DEVICE_SIGNAL_HANGUP, "");
57 
58 // Acquire the additional handle from |info|.
59 //
60 // Returns |ZX_OK| if a handle was returned.
61 // Returns |ZX_ERR_NOT_FOUND| if no handle can be returned.
zxrio_object_extract_handle(const fuchsia_io_NodeInfo * info,zx_handle_t * out)62 static zx_status_t zxrio_object_extract_handle(const fuchsia_io_NodeInfo* info,
63                                                zx_handle_t* out) {
64     switch (info->tag) {
65     case fuchsia_io_NodeInfoTag_file:
66         if (info->file.event != ZX_HANDLE_INVALID) {
67             *out = info->file.event;
68             return ZX_OK;
69         }
70         break;
71     case fuchsia_io_NodeInfoTag_pipe:
72         if (info->pipe.socket != ZX_HANDLE_INVALID) {
73             *out = info->pipe.socket;
74             return ZX_OK;
75         }
76         break;
77     case fuchsia_io_NodeInfoTag_vmofile:
78         if (info->vmofile.vmo != ZX_HANDLE_INVALID) {
79             *out = info->vmofile.vmo;
80             return ZX_OK;
81         }
82         break;
83     case fuchsia_io_NodeInfoTag_device:
84         if (info->device.event != ZX_HANDLE_INVALID) {
85             *out = info->device.event;
86             return ZX_OK;
87         }
88         break;
89     }
90     return ZX_ERR_NOT_FOUND;
91 }
92 
93 // Open an object without waiting for the response.
94 // This function always consumes the cnxn handle
95 // The svc handle is only used to send a message
zxrio_connect(zx_handle_t svc,zx_handle_t cnxn,uint32_t op,uint32_t flags,uint32_t mode,const char * name)96 static zx_status_t zxrio_connect(zx_handle_t svc, zx_handle_t cnxn,
97                                  uint32_t op, uint32_t flags, uint32_t mode,
98                                  const char* name) {
99     size_t len = strlen(name);
100     if (len >= PATH_MAX) {
101         zx_handle_close(cnxn);
102         return ZX_ERR_BAD_PATH;
103     }
104     if (flags & ZX_FS_FLAG_DESCRIBE) {
105         zx_handle_close(cnxn);
106         return ZX_ERR_INVALID_ARGS;
107     }
108 
109     zx_status_t r;
110     switch (op) {
111     case fuchsia_io_NodeCloneOrdinal:
112         r = fuchsia_io_NodeClone(svc, flags, cnxn);
113         break;
114     case fuchsia_io_DirectoryOpenOrdinal:
115         r = fuchsia_io_DirectoryOpen(svc, flags, mode, name, len, cnxn);
116         break;
117     default:
118         zx_handle_close(cnxn);
119         r = ZX_ERR_NOT_SUPPORTED;
120     }
121     return r;
122 }
123 
124 // A one-way message which may be emitted by the server without an
125 // accompanying request. Optionally used as a part of the Open handshake.
126 typedef struct {
127     fuchsia_io_NodeOnOpenEvent primary;
128     fuchsia_io_NodeInfo extra;
129 } fdio_on_open_msg_t;
130 
131 // Takes ownership of the optional |extra_handle|.
132 //
133 // Decodes the handle into |info|, if it exists and should
134 // be decoded.
zxrio_decode_describe_handle(fdio_on_open_msg_t * info,zx_handle_t extra_handle)135 static zx_status_t zxrio_decode_describe_handle(fdio_on_open_msg_t* info,
136                                                 zx_handle_t extra_handle) {
137     bool have_handle = (extra_handle != ZX_HANDLE_INVALID);
138     bool want_handle = false;
139     zx_handle_t* handle_target = NULL;
140 
141     switch (info->extra.tag) {
142     // Case: No extra handles expected
143     case fuchsia_io_NodeInfoTag_service:
144     case fuchsia_io_NodeInfoTag_directory:
145         break;
146     // Case: Extra handles optional
147     case fuchsia_io_NodeInfoTag_file:
148         handle_target = &info->extra.file.event;
149         goto handle_optional;
150     case fuchsia_io_NodeInfoTag_device:
151         handle_target = &info->extra.device.event;
152         goto handle_optional;
153 handle_optional:
154         want_handle = *handle_target == FIDL_HANDLE_PRESENT;
155         break;
156     // Case: Extra handles required
157     case fuchsia_io_NodeInfoTag_pipe:
158         handle_target = &info->extra.pipe.socket;
159         goto handle_required;
160     case fuchsia_io_NodeInfoTag_vmofile:
161         handle_target = &info->extra.vmofile.vmo;
162         goto handle_required;
163 handle_required:
164         want_handle = *handle_target == FIDL_HANDLE_PRESENT;
165         if (!want_handle) {
166             goto fail;
167         }
168         break;
169     default:
170         printf("Unexpected protocol type opening connection\n");
171         goto fail;
172     }
173 
174     if (have_handle != want_handle) {
175         goto fail;
176     }
177     if (have_handle) {
178         *handle_target = extra_handle;
179     }
180     return ZX_OK;
181 
182 fail:
183     if (have_handle) {
184         zx_handle_close(extra_handle);
185     }
186     return ZX_ERR_IO;
187 }
188 
189 // Wait/Read from a new client connection, with the expectation of
190 // acquiring an Open response.
191 //
192 // Shared implementation between RemoteIO and FIDL, since the response
193 // message is aligned.
194 //
195 // Does not close |h|, even on error.
zxrio_process_open_response(zx_handle_t h,fdio_on_open_msg_t * info)196 static zx_status_t zxrio_process_open_response(zx_handle_t h, fdio_on_open_msg_t* info) {
197     zx_object_wait_one(h, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
198                        ZX_TIME_INFINITE, NULL);
199 
200     // Attempt to read the description from open
201     uint32_t dsize = sizeof(*info);
202     zx_handle_t extra_handle = ZX_HANDLE_INVALID;
203     uint32_t actual_handles;
204     zx_status_t r = zx_channel_read(h, 0, info, &extra_handle, dsize, 1, &dsize,
205                                     &actual_handles);
206     if (r != ZX_OK) {
207         return r;
208     }
209     if (dsize < sizeof(fuchsia_io_NodeOnOpenEvent) ||
210         info->primary.hdr.ordinal != fuchsia_io_NodeOnOpenOrdinal) {
211         r = ZX_ERR_IO;
212     } else {
213         r = info->primary.s;
214     }
215 
216     if (dsize != sizeof(fdio_on_open_msg_t)) {
217         r = (r != ZX_OK) ? r : ZX_ERR_IO;
218     }
219 
220     if (r != ZX_OK) {
221         if (extra_handle != ZX_HANDLE_INVALID) {
222             zx_handle_close(extra_handle);
223         }
224         return r;
225     }
226 
227     // Confirm that the objects "fdio_on_open_msg_t" and "fuchsia_io_NodeOnOpenEvent"
228     // are aligned enough to be compatible.
229     //
230     // This is somewhat complicated by the fact that the "fuchsia_io_NodeOnOpenEvent"
231     // object has an optional "fuchsia_io_NodeInfo" secondary which exists immediately
232     // following the struct.
233     static_assert(__builtin_offsetof(fdio_on_open_msg_t, extra) ==
234                   FIDL_ALIGN(sizeof(fuchsia_io_NodeOnOpenEvent)),
235                   "RIO Description message doesn't align with FIDL response secondary");
236     // Connection::NodeDescribe also relies on these static_asserts.
237     // fidl_describe also relies on these static_asserts.
238 
239     return zxrio_decode_describe_handle(info, extra_handle);
240 }
241 
242 __EXPORT
fdio_service_connect(const char * svcpath,zx_handle_t h)243 zx_status_t fdio_service_connect(const char* svcpath, zx_handle_t h) {
244     if (svcpath == NULL) {
245         zx_handle_close(h);
246         return ZX_ERR_INVALID_ARGS;
247     }
248     // Otherwise attempt to connect through the root namespace
249     if (fdio_root_ns != NULL) {
250         return fdio_ns_connect(fdio_root_ns, svcpath,
251                                ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE, h);
252     }
253     // Otherwise we fail
254     zx_handle_close(h);
255     return ZX_ERR_NOT_FOUND;
256 }
257 
258 __EXPORT
fdio_service_connect_at(zx_handle_t dir,const char * path,zx_handle_t h)259 zx_status_t fdio_service_connect_at(zx_handle_t dir, const char* path, zx_handle_t h) {
260     if (path == NULL) {
261         zx_handle_close(h);
262         return ZX_ERR_INVALID_ARGS;
263     }
264     if (dir == ZX_HANDLE_INVALID) {
265         zx_handle_close(h);
266         return ZX_ERR_UNAVAILABLE;
267     }
268     return zxrio_connect(dir, h, fuchsia_io_DirectoryOpenOrdinal, ZX_FS_RIGHT_READABLE |
269                          ZX_FS_RIGHT_WRITABLE, 0755, path);
270 }
271 
272 __EXPORT
fdio_open(const char * path,uint32_t flags,zx_handle_t h)273 zx_status_t fdio_open(const char* path, uint32_t flags, zx_handle_t h) {
274     if (path == NULL) {
275         zx_handle_close(h);
276         return ZX_ERR_INVALID_ARGS;
277     }
278     // Otherwise attempt to connect through the root namespace
279     if (fdio_root_ns != NULL) {
280         return fdio_ns_connect(fdio_root_ns, path, flags, h);
281     }
282     // Otherwise we fail
283     zx_handle_close(h);
284     return ZX_ERR_NOT_FOUND;
285 }
286 
287 __EXPORT
fdio_open_at(zx_handle_t dir,const char * path,uint32_t flags,zx_handle_t h)288 zx_status_t fdio_open_at(zx_handle_t dir, const char* path, uint32_t flags, zx_handle_t h) {
289     if (path == NULL) {
290         zx_handle_close(h);
291         return ZX_ERR_INVALID_ARGS;
292     }
293     if (dir == ZX_HANDLE_INVALID) {
294         zx_handle_close(h);
295         return ZX_ERR_UNAVAILABLE;
296     }
297     return zxrio_connect(dir, h, fuchsia_io_DirectoryOpenOrdinal, flags, 0755, path);
298 }
299 
300 __EXPORT
fdio_service_clone(zx_handle_t svc)301 zx_handle_t fdio_service_clone(zx_handle_t svc) {
302     zx_handle_t cli, srv;
303     zx_status_t r;
304     if (svc == ZX_HANDLE_INVALID) {
305         return ZX_HANDLE_INVALID;
306     }
307     if ((r = zx_channel_create(0, &cli, &srv)) < 0) {
308         return ZX_HANDLE_INVALID;
309     }
310     if ((r = zxrio_connect(svc, srv, fuchsia_io_NodeCloneOrdinal, ZX_FS_RIGHT_READABLE |
311                            ZX_FS_RIGHT_WRITABLE, 0755, "")) < 0) {
312         zx_handle_close(cli);
313         return ZX_HANDLE_INVALID;
314     }
315     return cli;
316 }
317 
318 __EXPORT
fdio_service_clone_to(zx_handle_t svc,zx_handle_t srv)319 zx_status_t fdio_service_clone_to(zx_handle_t svc, zx_handle_t srv) {
320     if (srv == ZX_HANDLE_INVALID) {
321         return ZX_ERR_INVALID_ARGS;
322     }
323     if (svc == ZX_HANDLE_INVALID) {
324         zx_handle_close(srv);
325         return ZX_ERR_INVALID_ARGS;
326     }
327     return zxrio_connect(svc, srv, fuchsia_io_NodeCloneOrdinal, ZX_FS_RIGHT_READABLE |
328                          ZX_FS_RIGHT_WRITABLE, 0755, "");
329 }
330 
fdio_from_channel(zx_handle_t channel,fdio_t ** out_io)331 zx_status_t fdio_from_channel(zx_handle_t channel, fdio_t** out_io) {
332     fuchsia_io_NodeInfo info;
333     memset(&info, 0, sizeof(info));
334     zx_status_t status = fuchsia_io_NodeDescribe(channel, &info);
335     if (status != ZX_OK) {
336         zx_handle_close(channel);
337         return status;
338     }
339 
340     zx_handle_t event = ZX_HANDLE_INVALID;
341     switch (info.tag) {
342     case fuchsia_io_NodeInfoTag_file:
343         event = info.file.event;
344         break;
345     case fuchsia_io_NodeInfoTag_device:
346         event = info.device.event;
347         break;
348     case fuchsia_io_NodeInfoTag_vmofile: {
349         uint64_t seek = 0u;
350         zx_status_t io_status = fuchsia_io_FileSeek(
351             channel, 0, fuchsia_io_SeekOrigin_START, &status, &seek);
352         if (io_status != ZX_OK) {
353             status = io_status;
354         }
355         if (status != ZX_OK) {
356             zx_handle_close(channel);
357             zx_handle_close(info.vmofile.vmo);
358             return status;
359         }
360         *out_io = fdio_vmofile_create(channel, info.vmofile.vmo,
361                                       info.vmofile.offset, info.vmofile.length,
362                                       seek);
363         return ZX_OK;
364     }
365     default:
366         event = ZX_HANDLE_INVALID;
367         break;
368     }
369 
370     *out_io = fdio_remote_create(channel, event);
371     return ZX_OK;
372 }
373 
fdio_from_socket(zx_handle_t socket,fdio_t ** out_io)374 zx_status_t fdio_from_socket(zx_handle_t socket, fdio_t** out_io) {
375     zx_info_socket_t info;
376     memset(&info, 0, sizeof(info));
377     zx_status_t status = zx_object_get_info(socket, ZX_INFO_SOCKET, &info, sizeof(info), NULL, NULL);
378     if (status != ZX_OK) {
379         zx_handle_close(socket);
380         return status;
381     }
382     fdio_t* io = NULL;
383     if ((info.options & ZX_SOCKET_HAS_CONTROL) != 0) {
384         // If the socket has a control plane, then the socket is either
385         // a stream or a datagram socket.
386         if ((info.options & ZX_SOCKET_DATAGRAM) != 0) {
387             io = fdio_socket_create_datagram(socket, IOFLAG_SOCKET_CONNECTED);
388         } else {
389             io = fdio_socket_create_stream(socket, IOFLAG_SOCKET_CONNECTED);
390         }
391     } else {
392         // Without a control plane, the socket is a pipe.
393         io = fdio_pipe_create(socket);
394     }
395     if (!io) {
396         return ZX_ERR_NO_RESOURCES;
397     }
398     *out_io = io;
399     return ZX_OK;
400 }
401 
402 // Create a fdio (if possible) from handles and info.
403 //
404 // The Control channel is provided in |handle|, and auxiliary
405 // handles may be provided in the |info| object.
406 //
407 // This function always takes control of all handles.
408 // They are transferred into the |out| object on success,
409 // or closed on failure.
fdio_from_handles(zx_handle_t handle,fuchsia_io_NodeInfo * info,fdio_t ** out)410 static zx_status_t fdio_from_handles(zx_handle_t handle, fuchsia_io_NodeInfo* info,
411                                      fdio_t** out) {
412     // All failure cases which discard handles set r and break
413     // to the end. All other cases in which handle ownership is moved
414     // on return locally.
415     zx_status_t r;
416     fdio_t* io;
417     switch (info->tag) {
418     case fuchsia_io_NodeInfoTag_directory:
419     case fuchsia_io_NodeInfoTag_service:
420         if (handle == ZX_HANDLE_INVALID) {
421             r = ZX_ERR_INVALID_ARGS;
422             break;
423         }
424         io = fdio_remote_create(handle, 0);
425         xprintf("rio (%x,%x) -> %p\n", handle, 0, io);
426         if (io == NULL) {
427             return ZX_ERR_NO_RESOURCES;
428         }
429         *out = io;
430         return ZX_OK;
431     case fuchsia_io_NodeInfoTag_file:
432         if (info->file.event == ZX_HANDLE_INVALID) {
433             io = fdio_remote_create(handle, 0);
434             xprintf("rio (%x,%x) -> %p\n", handle, 0, io);
435         } else {
436             io = fdio_remote_create(handle, info->file.event);
437             xprintf("rio (%x,%x) -> %p\n", handle, info->file.event, io);
438         }
439         if (io == NULL) {
440             return ZX_ERR_NO_RESOURCES;
441         }
442         *out = io;
443         return ZX_OK;
444     case fuchsia_io_NodeInfoTag_device:
445         if (info->device.event == ZX_HANDLE_INVALID) {
446             io = fdio_remote_create(handle, 0);
447             xprintf("rio (%x,%x) -> %p\n", handle, 0, io);
448         } else {
449             io = fdio_remote_create(handle, info->device.event);
450             xprintf("rio (%x,%x) -> %p\n", handle, info->device.event, io);
451         }
452         if (io == NULL) {
453             return ZX_ERR_NO_RESOURCES;
454         }
455         *out = io;
456         return ZX_OK;
457     case fuchsia_io_NodeInfoTag_vmofile: {
458         if (info->vmofile.vmo == ZX_HANDLE_INVALID) {
459             r = ZX_ERR_INVALID_ARGS;
460             break;
461         }
462         *out = fdio_vmofile_create(handle, info->vmofile.vmo, info->vmofile.offset,
463                                    info->vmofile.length, 0u);
464         if (*out == NULL) {
465             return ZX_ERR_NO_RESOURCES;
466         }
467         return ZX_OK;
468     }
469     case fuchsia_io_NodeInfoTag_pipe: {
470         if (info->pipe.socket == ZX_HANDLE_INVALID) {
471             r = ZX_ERR_INVALID_ARGS;
472             break;
473         }
474         zx_handle_close(handle);
475         return fdio_from_socket(info->pipe.socket, out);
476     }
477     default:
478         printf("fdio_from_handles: Not supported\n");
479         r = ZX_ERR_NOT_SUPPORTED;
480         break;
481     }
482     zx_handle_t extra;
483     if (zxrio_object_extract_handle(info, &extra) == ZX_OK) {
484         zx_handle_close(extra);
485     }
486     zx_handle_close(handle);
487     return r;
488 }
489 
490 __EXPORT
fdio_create_fd(zx_handle_t * handles,uint32_t * types,size_t hcount,int * fd_out)491 zx_status_t fdio_create_fd(zx_handle_t* handles, uint32_t* types, size_t hcount,
492                            int* fd_out) {
493     fdio_t* io;
494     zx_status_t r;
495     int fd;
496     fuchsia_io_NodeInfo info;
497 
498     // Pack additional handles into |info|, if possible.
499     switch (PA_HND_TYPE(types[0])) {
500     case PA_FDIO_REMOTE:
501         switch (hcount) {
502         case 1:
503             io = fdio_remote_create(handles[0], 0);
504             goto bind;
505         case 2:
506             io = fdio_remote_create(handles[0], handles[1]);
507             goto bind;
508         default:
509             r = ZX_ERR_INVALID_ARGS;
510             goto fail;
511         }
512     case PA_FDIO_SOCKET:
513         info.tag = fuchsia_io_NodeInfoTag_pipe;
514         // Expected: Single socket handle
515         if (hcount != 1) {
516             r = ZX_ERR_INVALID_ARGS;
517             goto fail;
518         }
519         info.pipe.socket = handles[0];
520         break;
521     default:
522         r = ZX_ERR_IO;
523         goto fail;
524     }
525 
526     if ((r = fdio_from_handles(ZX_HANDLE_INVALID, &info, &io)) != ZX_OK) {
527         return r;
528     }
529 
530 bind:
531     fd = fdio_bind_to_fd(io, -1, 0);
532     if (fd < 0) {
533         fdio_close(io);
534         fdio_release(io);
535         return ZX_ERR_BAD_STATE;
536     }
537 
538     *fd_out = fd;
539     return ZX_OK;
540 fail:
541     zx_handle_close_many(handles, hcount);
542     return r;
543 }
544 
545 // Synchronously (non-pipelined) open an object
546 // The svc handle is only used to send a message
zxrio_sync_open_connection(zx_handle_t svc,uint32_t op,uint32_t flags,uint32_t mode,const char * path,size_t pathlen,fdio_on_open_msg_t * info,zx_handle_t * out)547 static zx_status_t zxrio_sync_open_connection(zx_handle_t svc, uint32_t op,
548                                               uint32_t flags, uint32_t mode,
549                                               const char* path, size_t pathlen,
550                                               fdio_on_open_msg_t* info,
551                                               zx_handle_t* out) {
552     if (!(flags & ZX_FS_FLAG_DESCRIBE)) {
553         return ZX_ERR_INVALID_ARGS;
554     }
555 
556     zx_status_t r;
557     zx_handle_t h;
558     zx_handle_t cnxn;
559     if ((r = zx_channel_create(0, &h, &cnxn)) != ZX_OK) {
560         return r;
561     }
562 
563     switch (op) {
564     case fuchsia_io_NodeCloneOrdinal:
565         r = fuchsia_io_NodeClone(svc, flags, cnxn);
566         break;
567     case fuchsia_io_DirectoryOpenOrdinal:
568         r = fuchsia_io_DirectoryOpen(svc, flags, mode, path, pathlen, cnxn);
569         break;
570     default:
571         zx_handle_close(cnxn);
572         r = ZX_ERR_NOT_SUPPORTED;
573     }
574 
575     if (r != ZX_OK) {
576         zx_handle_close(h);
577         return r;
578     }
579 
580     if ((r = zxrio_process_open_response(h, info)) != ZX_OK) {
581         zx_handle_close(h);
582         return r;
583     }
584     *out = h;
585     return ZX_OK;
586 }
587 
588 // Acquires a new connection to an object.
589 //
590 // Returns a description of the opened object in |info|, and
591 // the control channel to the object in |out|.
592 //
593 // |info| may contain an additional handle.
zxrio_getobject(zx_handle_t rio_h,uint32_t op,const char * name,uint32_t flags,uint32_t mode,fdio_on_open_msg_t * info,zx_handle_t * out)594 static zx_status_t zxrio_getobject(zx_handle_t rio_h, uint32_t op, const char* name,
595                                    uint32_t flags, uint32_t mode,
596                                    fdio_on_open_msg_t* info, zx_handle_t* out) {
597     if (name == NULL) {
598         return ZX_ERR_INVALID_ARGS;
599     }
600 
601     size_t len = strlen(name);
602     if (len >= PATH_MAX) {
603         return ZX_ERR_BAD_PATH;
604     }
605 
606     if (flags & ZX_FS_FLAG_DESCRIBE) {
607         return zxrio_sync_open_connection(rio_h, op, flags, mode, name, len, info, out);
608     } else {
609         zx_handle_t h0, h1;
610         zx_status_t r;
611         if ((r = zx_channel_create(0, &h0, &h1)) < 0) {
612             return r;
613         }
614         if ((r = zxrio_connect(rio_h, h1, op, flags, mode, name)) < 0) {
615             zx_handle_close(h0);
616             return r;
617         }
618         // fake up a reply message since pipelined opens don't generate one
619         info->primary.s = ZX_OK;
620         info->extra.tag = fuchsia_io_NodeInfoTag_service;
621         *out = h0;
622         return ZX_OK;
623     }
624 }
625 
zxrio_open_handle(zx_handle_t h,const char * path,uint32_t flags,uint32_t mode,fdio_t ** out)626 zx_status_t zxrio_open_handle(zx_handle_t h, const char* path, uint32_t flags,
627                               uint32_t mode, fdio_t** out) {
628     zx_handle_t control_channel = ZX_HANDLE_INVALID;
629     fdio_on_open_msg_t info;
630     zx_status_t r = zxrio_getobject(h, fuchsia_io_DirectoryOpenOrdinal, path, flags, mode, &info, &control_channel);
631     if (r < 0) {
632         return r;
633     }
634     return fdio_from_handles(control_channel, &info.extra, out);
635 }
636