| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| |
| #include <zircon/assert.h> |
| #include <zircon/device/device.h> |
| #include <zircon/device/ioctl.h> |
| #include <zircon/device/vfs.h> |
| #include <zircon/syscalls.h> |
| |
| #include <fuchsia/io/c/fidl.h> |
| #include <lib/fdio/debug.h> |
| #include <lib/fdio/io.h> |
| #include <lib/fdio/remoteio.h> |
| #include <lib/fdio/util.h> |
| #include <lib/fdio/vfs.h> |
| |
| #include "private-fidl.h" |
| |
| #define MXDEBUG 0 |
| |
| static zx_status_t txn_reply(fidl_txn_t* txn, const fidl_msg_t* msg) { |
| zxfidl_connection_t* cnxn = (void*) txn; |
| fidl_message_header_t* hdr = msg->bytes; |
| hdr->txid = cnxn->txid; |
| return zx_channel_write(cnxn->channel, 0, msg->bytes, msg->num_bytes, |
| msg->handles, msg->num_handles); |
| }; |
| |
| // Don't actually send anything on a channel when completing this operation. |
| // This is useful for mocking out "close" requests. |
| static zx_status_t txn_null_reply(fidl_txn_t* reply, const fidl_msg_t* msg) { |
| return ZX_OK; |
| } |
| |
| static zx_status_t handle_rpc_close(zxfidl_cb_t cb, void* cookie) { |
| fuchsia_io_NodeCloseRequest request; |
| memset(&request, 0, sizeof(request)); |
| request.hdr.ordinal = fuchsia_io_NodeCloseOrdinal; |
| fidl_msg_t msg = { |
| .bytes = &request, |
| .handles = NULL, |
| .num_bytes = sizeof(request), |
| .num_handles = 0u, |
| }; |
| |
| zxfidl_connection_t cnxn = { |
| .txn = { |
| .reply = txn_null_reply, |
| }, |
| .channel = ZX_HANDLE_INVALID, |
| .txid = 0, |
| }; |
| |
| // Remote side was closed. |
| cb(&msg, &cnxn.txn, cookie); |
| return ERR_DISPATCHER_DONE; |
| } |
| |
| static zx_status_t handle_rpc(zx_handle_t h, zxfidl_cb_t cb, void* cookie) { |
| uint8_t bytes[ZXFIDL_MAX_MSG_BYTES]; |
| zx_handle_t handles[ZXFIDL_MAX_MSG_HANDLES]; |
| fidl_msg_t msg = { |
| .bytes = bytes, |
| .handles = handles, |
| .num_bytes = 0, |
| .num_handles = 0, |
| }; |
| |
| zx_status_t r = zx_channel_read(h, 0, bytes, handles, countof(bytes), |
| countof(handles), &msg.num_bytes, |
| &msg.num_handles); |
| if (r != ZX_OK) { |
| return r; |
| } |
| |
| if (msg.num_bytes < sizeof(fidl_message_header_t)) { |
| zx_handle_close_many(msg.handles, msg.num_handles); |
| return ZX_ERR_IO; |
| } |
| |
| fidl_message_header_t* hdr = msg.bytes; |
| zxfidl_connection_t cnxn = { |
| .txn = { |
| .reply = txn_reply, |
| }, |
| .channel = h, |
| .txid = hdr->txid, |
| }; |
| |
| // Callback is responsible for decoding the message, and closing |
| // any associated handles. |
| return cb(&msg, &cnxn.txn, cookie); |
| } |
| |
| __EXPORT |
| zx_status_t zxfidl_handler(zx_handle_t h, zxfidl_cb_t cb, void* cookie) { |
| if (h == ZX_HANDLE_INVALID) { |
| return handle_rpc_close(cb, cookie); |
| } else { |
| ZX_ASSERT(zx_object_get_info(h, ZX_INFO_HANDLE_VALID, NULL, 0, |
| NULL, NULL) == ZX_OK); |
| return handle_rpc(h, cb, cookie); |
| } |
| } |
| |
| // Always consumes cnxn. Exported for the sake of vs-vnode tests |
| __EXPORT |
| zx_status_t fidl_clone_request(zx_handle_t srv, zx_handle_t cnxn, uint32_t flags) { |
| return fuchsia_io_NodeClone(srv, flags, cnxn); |
| } |
| |
| // Always consumes cnxn. |
| zx_status_t fidl_open_request(zx_handle_t srv, zx_handle_t cnxn, uint32_t flags, |
| uint32_t mode, const char* path, size_t pathlen) { |
| return fuchsia_io_DirectoryOpen(srv, flags, mode, path, pathlen, cnxn); |
| } |
| |
| zx_status_t fidl_ioctl(zx_handle_t h, uint32_t op, const void* in_buf, |
| size_t in_len, void* out_buf, size_t out_len, |
| size_t* out_actual) { |
| size_t in_handle_count = 0; |
| size_t out_handle_count = 0; |
| switch (IOCTL_KIND(op)) { |
| case IOCTL_KIND_GET_HANDLE: |
| out_handle_count = 1; |
| break; |
| case IOCTL_KIND_GET_TWO_HANDLES: |
| out_handle_count = 2; |
| break; |
| case IOCTL_KIND_GET_THREE_HANDLES: |
| out_handle_count = 3; |
| break; |
| case IOCTL_KIND_SET_HANDLE: |
| in_handle_count = 1; |
| break; |
| case IOCTL_KIND_SET_TWO_HANDLES: |
| in_handle_count = 2; |
| break; |
| } |
| |
| if (in_len < in_handle_count * sizeof(zx_handle_t)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (out_len < out_handle_count * sizeof(zx_handle_t)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_handle_t hbuf[out_handle_count]; |
| size_t out_handle_actual; |
| zx_status_t io_status, status; |
| if ((io_status = fuchsia_io_NodeIoctl(h, op, |
| out_len, (zx_handle_t*) in_buf, |
| in_handle_count, in_buf, |
| in_len, &status, hbuf, |
| out_handle_count, &out_handle_actual, |
| out_buf, out_len, out_actual)) != ZX_OK) { |
| return io_status; |
| } |
| |
| if (status != ZX_OK) { |
| zx_handle_close_many(hbuf, out_handle_actual); |
| return status; |
| } |
| if (out_handle_actual != out_handle_count) { |
| zx_handle_close_many(hbuf, out_handle_actual); |
| return ZX_ERR_IO; |
| } |
| |
| memcpy(out_buf, hbuf, out_handle_count * sizeof(zx_handle_t)); |
| return ZX_OK; |
| } |