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