| // Copyright 2016 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 <limits.h> |
| #include <poll.h> |
| #include <pthread.h> |
| #include <stdatomic.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <threads.h> |
| |
| #include <zircon/assert.h> |
| #include <zircon/device/device.h> |
| #include <zircon/device/ioctl.h> |
| #include <zircon/device/vfs.h> |
| #include <zircon/processargs.h> |
| #include <zircon/syscalls.h> |
| |
| #include <fdio/debug.h> |
| #include <fdio/io.fidl2.h> |
| #include <fdio/io.h> |
| #include <fdio/namespace.h> |
| #include <fdio/remoteio.h> |
| #include <fdio/util.h> |
| #include <fdio/vfs.h> |
| |
| #include "private-fidl.h" |
| #include "private-remoteio.h" |
| |
| #define ZXDEBUG 0 |
| |
| // POLL_MASK and POLL_SHIFT intend to convert the lower five POLL events into |
| // ZX_USER_SIGNALs and vice-versa. Other events need to be manually converted to |
| // an zx_signal_t, if they are desired. |
| #define POLL_SHIFT 24 |
| #define POLL_MASK 0x1F |
| |
| static_assert(ZX_USER_SIGNAL_0 == (1 << POLL_SHIFT), ""); |
| static_assert((POLLIN << POLL_SHIFT) == DEVICE_SIGNAL_READABLE, ""); |
| static_assert((POLLPRI << POLL_SHIFT) == DEVICE_SIGNAL_OOB, ""); |
| static_assert((POLLOUT << POLL_SHIFT) == DEVICE_SIGNAL_WRITABLE, ""); |
| static_assert((POLLERR << POLL_SHIFT) == DEVICE_SIGNAL_ERROR, ""); |
| static_assert((POLLHUP << POLL_SHIFT) == DEVICE_SIGNAL_HANGUP, ""); |
| |
| static const char* _opnames[] = ZXRIO_OPNAMES; |
| const char* fdio_opname(uint32_t op) { |
| op = ZXRIO_OPNAME(op); |
| if (op < ZXRIO_NUM_OPS) { |
| return _opnames[op]; |
| } else { |
| return "unknown"; |
| } |
| } |
| |
| static void discard_handles(zx_handle_t* handles, unsigned count) { |
| while (count-- > 0) { |
| zx_handle_close(*handles++); |
| } |
| } |
| |
| zx_status_t zxrio_handle_rpc(zx_handle_t h, zxrio_msg_t* msg, zxrio_cb_t cb, void* cookie) { |
| zx_status_t r = zxrio_read_request(h, msg); |
| if (r != ZX_OK) { |
| return r; |
| } |
| bool is_close = (ZXRIO_OP(msg->op) == ZXRIO_CLOSE) || |
| (ZXRIO_OP(msg->op) == ZXFIDL_CLOSE); |
| |
| r = cb(msg, cookie); |
| switch (r) { |
| case ERR_DISPATCHER_INDIRECT: |
| // callback is handling the reply itself |
| // and took ownership of the reply handle |
| return ZX_OK; |
| case ERR_DISPATCHER_ASYNC: |
| // Same as the indirect case, but also identify that |
| // the callback will asynchronously re-trigger the |
| // dispatcher. |
| return ERR_DISPATCHER_ASYNC; |
| } |
| |
| r = zxrio_write_response(h, r, msg); |
| |
| if (is_close) { |
| // signals to not perform a close callback |
| return ERR_DISPATCHER_DONE; |
| } else { |
| return r; |
| } |
| } |
| |
| zx_status_t zxrio_handle_close(zxrio_cb_t cb, void* cookie) { |
| zxrio_msg_t msg; |
| |
| // remote side was closed; |
| #ifdef ZXRIO_FIDL |
| ObjectCloseRequest* request = (ObjectCloseRequest*) &msg; |
| memset(request, 0, sizeof(ObjectCloseRequest)); |
| request->hdr.ordinal = ZXFIDL_CLOSE; |
| #else |
| msg.op = ZXRIO_CLOSE; |
| msg.arg = 0; |
| msg.datalen = 0; |
| msg.hcount = 0; |
| #endif |
| cb(&msg, cookie); |
| return ZX_OK; |
| } |
| |
| zx_status_t zxrio_handler(zx_handle_t h, zxrio_cb_t cb, void* cookie) { |
| if (h == ZX_HANDLE_INVALID) { |
| return zxrio_handle_close(cb, cookie); |
| } else { |
| char buffer[ZX_CHANNEL_MAX_MSG_BYTES]; |
| return zxrio_handle_rpc(h, (zxrio_msg_t*) buffer, cb, cookie); |
| } |
| } |
| |
| zx_status_t zxrio_txn_handoff(zx_handle_t srv, zx_handle_t reply, zxrio_msg_t* msg) { |
| msg->txid = 0; |
| uint32_t dsize; |
| switch (msg->op) { |
| case ZXFIDL_OPEN: { |
| DirectoryOpenRequest* request = (DirectoryOpenRequest*) msg; |
| request->object = FIDL_HANDLE_PRESENT; |
| dsize = FIDL_ALIGN(sizeof(DirectoryOpenRequest)) + FIDL_ALIGN(request->path.size); |
| break; |
| } |
| case ZXFIDL_CLONE: { |
| ObjectCloneRequest* request = (ObjectCloneRequest*) msg; |
| request->object = FIDL_HANDLE_PRESENT; |
| dsize = sizeof(ObjectCloneRequest); |
| break; |
| } |
| default: |
| ZX_DEBUG_ASSERT(!ZXRIO_FIDL_MSG(msg->op)); |
| msg->handle[0] = reply; |
| msg->hcount = 1; |
| dsize = ZXRIO_HDR_SZ + msg->datalen; |
| } |
| |
| zx_status_t r; |
| if ((r = zx_channel_write(srv, 0, msg, dsize, &reply, 1)) != ZX_OK) { |
| printf("zxrio_txn_handoff: Failed to write\n"); |
| // The caller may or may not be expecting a response. Either way, |
| // we need to close the channel, since it will not arrive at its |
| // intended destination. |
| zx_handle_close(reply); |
| } |
| return r; |
| } |
| |
| // on success, msg->hcount indicates number of valid handles in msg->handle |
| // on error there are never any handles |
| static zx_status_t zxrio_txn(zxrio_t* rio, zxrio_msg_t* msg) { |
| if (!is_rio_message_valid(msg)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| msg->txid = atomic_fetch_add(&rio->txid, 1); |
| xprintf("txn h=%x txid=%x op=%d len=%u\n", rio->h, msg->txid, msg->op, msg->datalen); |
| |
| zx_status_t r; |
| zx_status_t rs = ZX_ERR_INTERNAL; |
| uint32_t dsize; |
| |
| zx_channel_call_args_t args; |
| args.wr_bytes = msg; |
| args.wr_handles = msg->handle; |
| args.rd_bytes = msg; |
| args.rd_handles = msg->handle; |
| args.wr_num_bytes = ZXRIO_HDR_SZ + msg->datalen; |
| args.wr_num_handles = msg->hcount; |
| args.rd_num_bytes = ZXRIO_HDR_SZ + FDIO_CHUNK_SIZE; |
| args.rd_num_handles = FDIO_MAX_HANDLES; |
| |
| r = zx_channel_call(rio->h, 0, ZX_TIME_INFINITE, &args, &dsize, &msg->hcount, &rs); |
| if (r < 0) { |
| if (r == ZX_ERR_CALL_FAILED) { |
| // read phase failed, true status is in rs |
| msg->hcount = 0; |
| return rs; |
| } else { |
| // write phase failed, we must discard the handles |
| goto fail_discard_handles; |
| } |
| } |
| |
| // check for protocol errors |
| if (!is_rio_message_reply_valid(msg, dsize) || |
| (ZXRIO_OP(msg->op) != ZXRIO_STATUS)) { |
| r = ZX_ERR_IO; |
| goto fail_discard_handles; |
| } |
| // check for remote error |
| if ((r = msg->arg) < 0) { |
| goto fail_discard_handles; |
| } |
| return r; |
| |
| fail_discard_handles: |
| // We failed either writing at all (still have the handles) |
| // or after reading (need to abandon any handles we received) |
| discard_handles(msg->handle, msg->hcount); |
| msg->hcount = 0; |
| return r; |
| } |
| |
| void zxrio_new_txid(zxrio_t* rio, zx_txid_t* txid) { |
| *txid = atomic_fetch_add(&rio->txid, 1); |
| } |
| |
| zx_handle_t zxrio_handle(zxrio_t* rio) { |
| return rio->h; |
| } |
| |
| zx_status_t zxrio_object_extract_handle(const zxrio_object_info_t* info, |
| zx_handle_t* out) { |
| switch (info->tag) { |
| case FDIO_PROTOCOL_FILE: |
| if (info->file.e != ZX_HANDLE_INVALID) { |
| *out = info->file.e; |
| return ZX_OK; |
| } |
| break; |
| case FDIO_PROTOCOL_SOCKET_CONNECTED: |
| case FDIO_PROTOCOL_SOCKET: |
| if (info->socket.s != ZX_HANDLE_INVALID) { |
| *out = info->socket.s; |
| return ZX_OK; |
| } |
| break; |
| case FDIO_PROTOCOL_PIPE: |
| if (info->pipe.s != ZX_HANDLE_INVALID) { |
| *out = info->pipe.s; |
| return ZX_OK; |
| } |
| break; |
| case FDIO_PROTOCOL_VMOFILE: |
| if (info->vmofile.v != ZX_HANDLE_INVALID) { |
| *out = info->vmofile.v; |
| return ZX_OK; |
| } |
| break; |
| case FDIO_PROTOCOL_DEVICE: |
| if (info->device.e != ZX_HANDLE_INVALID) { |
| *out = info->device.e; |
| return ZX_OK; |
| } |
| break; |
| } |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| #ifdef ZXRIO_FIDL |
| |
| zx_status_t zxrio_close(fdio_t* io) { |
| zxrio_t* rio = (zxrio_t*)io; |
| |
| zx_status_t r = fidl_close(rio); |
| zx_handle_t h = rio->h; |
| rio->h = 0; |
| zx_handle_close(h); |
| if (rio->h2 > 0) { |
| h = rio->h2; |
| rio->h2 = 0; |
| zx_handle_close(h); |
| } |
| return r; |
| } |
| |
| // Synchronously (non-pipelined) open an object |
| // The svc handle is only used to send a message |
| static zx_status_t zxrio_sync_open_connection(zx_handle_t svc, uint32_t op, |
| uint32_t flags, uint32_t mode, |
| const char* path, size_t pathlen, |
| zxrio_describe_t* info, zx_handle_t* out) { |
| if (!(flags & ZX_FS_FLAG_DESCRIBE)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_status_t r; |
| zx_handle_t h; |
| zx_handle_t cnxn; |
| if ((r = zx_channel_create(0, &h, &cnxn)) != ZX_OK) { |
| return r; |
| } |
| |
| switch (op) { |
| case ZXRIO_CLONE: |
| r = fidl_clone_request(svc, cnxn, flags); |
| break; |
| case ZXRIO_OPEN: |
| r = fidl_open_request(svc, cnxn, flags, mode, path, pathlen); |
| break; |
| default: |
| zx_handle_close(cnxn); |
| r = ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| if (r != ZX_OK) { |
| zx_handle_close(h); |
| return r; |
| } |
| |
| if ((r = zxrio_process_open_response(h, info)) != ZX_OK) { |
| zx_handle_close(h); |
| return r; |
| } |
| *out = h; |
| return ZX_OK; |
| } |
| |
| // Open an object without waiting for the response. |
| // This function always consumes the cnxn handle |
| // The svc handle is only used to send a message |
| static zx_status_t zxrio_connect(zx_handle_t svc, zx_handle_t cnxn, |
| uint32_t op, uint32_t flags, uint32_t mode, |
| const char* name) { |
| size_t len = strlen(name); |
| if (len >= PATH_MAX) { |
| zx_handle_close(cnxn); |
| return ZX_ERR_BAD_PATH; |
| } |
| if (flags & ZX_FS_FLAG_DESCRIBE) { |
| zx_handle_close(cnxn); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_status_t r; |
| switch (op) { |
| case ZXRIO_CLONE: |
| r = fidl_clone_request(svc, cnxn, flags); |
| break; |
| case ZXRIO_OPEN: |
| r = fidl_open_request(svc, cnxn, flags, mode, name, len); |
| break; |
| default: |
| zx_handle_close(cnxn); |
| r = ZX_ERR_NOT_SUPPORTED; |
| } |
| return r; |
| } |
| |
| static ssize_t zxrio_write(fdio_t* io, const void* data, size_t len) { |
| zxrio_t* rio = (zxrio_t*) io; |
| zx_status_t status = ZX_OK; |
| uint64_t count = 0; |
| uint64_t xfer; |
| while (len > 0) { |
| xfer = (len > FDIO_CHUNK_SIZE) ? FDIO_CHUNK_SIZE : len; |
| uint64_t actual = 0; |
| if ((status = fidl_write(rio, data, xfer, &actual)) != ZX_OK) { |
| return status; |
| } |
| count += actual; |
| data += actual; |
| len -= actual; |
| if (xfer != actual) { |
| break; |
| } |
| } |
| if (count == 0) { |
| return status; |
| } |
| return count; |
| } |
| |
| static ssize_t zxrio_write_at(fdio_t* io, const void* data, size_t len, off_t offset) { |
| zxrio_t* rio = (zxrio_t*) io; |
| zx_status_t status = ZX_ERR_IO; |
| uint64_t count = 0; |
| uint64_t xfer; |
| while (len > 0) { |
| xfer = (len > FDIO_CHUNK_SIZE) ? FDIO_CHUNK_SIZE : len; |
| uint64_t actual = 0; |
| if ((status = fidl_writeat(rio, data, xfer, offset, &actual)) != ZX_OK) { |
| return status; |
| } |
| count += actual; |
| data += actual; |
| offset += actual; |
| len -= actual; |
| if (xfer != actual) { |
| break; |
| } |
| } |
| if (count == 0) { |
| return status; |
| } |
| return count; |
| } |
| |
| static ssize_t zxrio_read(fdio_t* io, void* data, size_t len) { |
| zxrio_t* rio = (zxrio_t*) io; |
| zx_status_t status; |
| uint64_t count = 0; |
| uint64_t xfer; |
| while (len > 0) { |
| xfer = (len > FDIO_CHUNK_SIZE) ? FDIO_CHUNK_SIZE : len; |
| uint64_t actual = 0; |
| if ((status = fidl_read(rio, data, xfer, &actual)) != ZX_OK) { |
| return status; |
| } |
| count += actual; |
| data += actual; |
| len -= actual; |
| if (xfer != actual) { |
| break; |
| } |
| } |
| if (count == 0) { |
| return status; |
| } |
| return count; |
| } |
| |
| static ssize_t zxrio_read_at(fdio_t* io, void* data, size_t len, off_t offset) { |
| zxrio_t* rio = (zxrio_t*) io; |
| zx_status_t status; |
| uint64_t count = 0; |
| uint64_t xfer; |
| while (len > 0) { |
| xfer = (len > FDIO_CHUNK_SIZE) ? FDIO_CHUNK_SIZE : len; |
| uint64_t actual = 0; |
| if ((status = fidl_readat(rio, data, xfer, offset, &actual)) != ZX_OK) { |
| return status; |
| } |
| offset += actual; |
| count += actual; |
| data += actual; |
| len -= actual; |
| if (xfer != actual) { |
| break; |
| } |
| } |
| if (count == 0) { |
| return status; |
| } |
| return count; |
| } |
| |
| static off_t zxrio_seek(fdio_t* io, off_t offset, int whence) { |
| zxrio_t* rio = (zxrio_t*)io; |
| zx_status_t status = fidl_seek(rio, offset, whence, &offset); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return offset; |
| } |
| |
| ssize_t zxrio_ioctl(fdio_t* io, uint32_t op, const void* in_buf, |
| size_t in_len, void* out_buf, size_t out_len) { |
| zxrio_t* rio = (zxrio_t*)io; |
| if (in_len > FDIO_IOCTL_MAX_INPUT || out_len > FDIO_CHUNK_SIZE) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| size_t actual; |
| zx_status_t status = fidl_ioctl(rio, op, in_buf, in_len, out_buf, out_len, &actual); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return actual; |
| } |
| |
| #else // ZXRIO_FIDL |
| |
| zx_status_t zxrio_close(fdio_t* io) { |
| zxrio_t* rio = (zxrio_t*)io; |
| zxrio_msg_t msg; |
| zx_status_t r; |
| |
| memset(&msg, 0, ZXRIO_HDR_SZ); |
| msg.op = ZXRIO_CLOSE; |
| |
| if ((r = zxrio_txn(rio, &msg)) >= 0) { |
| discard_handles(msg.handle, msg.hcount); |
| } |
| |
| zx_handle_t h = rio->h; |
| rio->h = 0; |
| zx_handle_close(h); |
| if (rio->h2 > 0) { |
| h = rio->h2; |
| rio->h2 = 0; |
| zx_handle_close(h); |
| } |
| |
| return r; |
| } |
| |
| // Synchronously (non-pipelined) open an object |
| // The svc handle is only used to send a message |
| static zx_status_t zxrio_sync_open_connection(zx_handle_t svc, uint32_t op, |
| uint32_t flags, uint32_t mode, |
| const char* path, size_t pathlen, |
| zxrio_describe_t* info, zx_handle_t* out) { |
| zxrio_msg_t msg; |
| memset(&msg, 0, ZXRIO_HDR_SZ); |
| msg.op = op; |
| msg.datalen = pathlen; |
| msg.arg = flags; |
| msg.arg2.mode = mode; |
| memcpy(msg.data, path, pathlen); |
| |
| zx_status_t r; |
| zx_handle_t h; |
| if ((r = zx_channel_create(0, &h, &msg.handle[0])) < 0) { |
| return r; |
| } |
| msg.hcount = 1; |
| |
| // Write the (one-way) request message |
| if ((r = zx_channel_write(svc, 0, &msg, ZXRIO_HDR_SZ + msg.datalen, |
| msg.handle, msg.hcount)) < 0) { |
| zx_handle_close(msg.handle[0]); |
| zx_handle_close(h); |
| return r; |
| } |
| |
| if ((r = zxrio_process_open_response(h, info)) != ZX_OK) { |
| zx_handle_close(h); |
| return r; |
| } |
| *out = h; |
| return ZX_OK; |
| } |
| |
| // Open an object without waiting for the response. |
| // This function always consumes the cnxn handle |
| // The svc handle is only used to send a message |
| static zx_status_t zxrio_connect(zx_handle_t svc, zx_handle_t cnxn, |
| uint32_t op, uint32_t flags, uint32_t mode, |
| const char* name) { |
| size_t len = strlen(name); |
| if (len >= PATH_MAX) { |
| zx_handle_close(cnxn); |
| return ZX_ERR_BAD_PATH; |
| } |
| if (flags & ZX_FS_FLAG_DESCRIBE) { |
| zx_handle_close(cnxn); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zxrio_msg_t msg; |
| memset(&msg, 0, ZXRIO_HDR_SZ); |
| msg.op = op; |
| msg.datalen = len; |
| msg.arg = flags; |
| msg.arg2.mode = mode; |
| msg.hcount = 1; |
| msg.handle[0] = cnxn; |
| memcpy(msg.data, name, len); |
| |
| zx_status_t r; |
| if ((r = zx_channel_write(svc, 0, &msg, ZXRIO_HDR_SZ + msg.datalen, msg.handle, 1)) < 0) { |
| zx_handle_close(cnxn); |
| return r; |
| } |
| |
| return ZX_OK; |
| } |
| |
| static ssize_t write_common(uint32_t op, fdio_t* io, const void* _data, size_t len, off_t offset) { |
| zxrio_t* rio = (zxrio_t*)io; |
| const uint8_t* data = _data; |
| ssize_t count = 0; |
| zx_status_t r = 0; |
| zxrio_msg_t msg; |
| ssize_t xfer; |
| |
| while (len > 0) { |
| xfer = (len > FDIO_CHUNK_SIZE) ? FDIO_CHUNK_SIZE : len; |
| |
| memset(&msg, 0, ZXRIO_HDR_SZ); |
| msg.op = op; |
| msg.datalen = xfer; |
| if (op == ZXRIO_WRITE_AT) |
| msg.arg2.off = offset; |
| memcpy(msg.data, data, xfer); |
| |
| if ((r = zxrio_txn(rio, &msg)) < 0) { |
| break; |
| } |
| discard_handles(msg.handle, msg.hcount); |
| |
| if (r > xfer) { |
| r = ZX_ERR_IO; |
| break; |
| } |
| count += r; |
| data += r; |
| len -= r; |
| if (op == ZXRIO_WRITE_AT) |
| offset += r; |
| // stop at short read |
| if (r < xfer) { |
| break; |
| } |
| } |
| return count ? count : r; |
| } |
| |
| static ssize_t zxrio_write(fdio_t* io, const void* _data, size_t len) { |
| return write_common(ZXRIO_WRITE, io, _data, len, 0); |
| } |
| |
| static ssize_t zxrio_write_at(fdio_t* io, const void* _data, size_t len, off_t offset) { |
| return write_common(ZXRIO_WRITE_AT, io, _data, len, offset); |
| } |
| |
| static ssize_t read_common(uint32_t op, fdio_t* io, void* _data, size_t len, off_t offset) { |
| zxrio_t* rio = (zxrio_t*)io; |
| uint8_t* data = _data; |
| ssize_t count = 0; |
| zx_status_t r = 0; |
| zxrio_msg_t msg; |
| ssize_t xfer; |
| |
| while (len > 0) { |
| xfer = (len > FDIO_CHUNK_SIZE) ? FDIO_CHUNK_SIZE : len; |
| |
| memset(&msg, 0, ZXRIO_HDR_SZ); |
| msg.op = op; |
| msg.arg = xfer; |
| if (op == ZXRIO_READ_AT) |
| msg.arg2.off = offset; |
| |
| if ((r = zxrio_txn(rio, &msg)) < 0) { |
| break; |
| } |
| discard_handles(msg.handle, msg.hcount); |
| |
| if ((r > (int)msg.datalen) || (r > xfer)) { |
| r = ZX_ERR_IO; |
| break; |
| } |
| memcpy(data, msg.data, r); |
| count += r; |
| data += r; |
| len -= r; |
| if (op == ZXRIO_READ_AT) |
| offset += r; |
| |
| // stop at short read |
| if (r < xfer) { |
| break; |
| } |
| } |
| return count ? count : r; |
| } |
| |
| static ssize_t zxrio_read(fdio_t* io, void* _data, size_t len) { |
| return read_common(ZXRIO_READ, io, _data, len, 0); |
| } |
| |
| static ssize_t zxrio_read_at(fdio_t* io, void* _data, size_t len, off_t offset) { |
| return read_common(ZXRIO_READ_AT, io, _data, len, offset); |
| } |
| |
| static off_t zxrio_seek(fdio_t* io, off_t offset, int whence) { |
| zxrio_t* rio = (zxrio_t*)io; |
| zxrio_msg_t msg; |
| zx_status_t r; |
| |
| memset(&msg, 0, ZXRIO_HDR_SZ); |
| msg.op = ZXRIO_SEEK; |
| msg.arg2.off = offset; |
| msg.arg = whence; |
| |
| if ((r = zxrio_txn(rio, &msg)) < 0) { |
| return r; |
| } |
| |
| discard_handles(msg.handle, msg.hcount); |
| return msg.arg2.off; |
| } |
| |
| ssize_t zxrio_ioctl(fdio_t* io, uint32_t op, const void* in_buf, |
| size_t in_len, void* out_buf, size_t out_len) { |
| zxrio_t* rio = (zxrio_t*)io; |
| const uint8_t* data = in_buf; |
| zx_status_t r = 0; |
| zxrio_msg_t msg; |
| |
| if (in_len > FDIO_IOCTL_MAX_INPUT || out_len > FDIO_CHUNK_SIZE) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| memset(&msg, 0, ZXRIO_HDR_SZ); |
| msg.op = ZXRIO_IOCTL; |
| msg.datalen = in_len; |
| msg.arg = out_len; |
| msg.arg2.op = op; |
| |
| switch (IOCTL_KIND(op)) { |
| case IOCTL_KIND_GET_HANDLE: |
| if (out_len < sizeof(zx_handle_t)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| break; |
| case IOCTL_KIND_GET_TWO_HANDLES: |
| if (out_len < 2 * sizeof(zx_handle_t)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| break; |
| case IOCTL_KIND_GET_THREE_HANDLES: |
| if (out_len < 3 * sizeof(zx_handle_t)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| break; |
| case IOCTL_KIND_SET_HANDLE: |
| msg.op = ZXRIO_IOCTL_1H; |
| if (in_len < sizeof(zx_handle_t)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| msg.hcount = 1; |
| msg.handle[0] = *((zx_handle_t*) in_buf); |
| break; |
| case IOCTL_KIND_SET_TWO_HANDLES: |
| msg.op = ZXRIO_IOCTL_2H; |
| if (in_len < 2 * sizeof(zx_handle_t)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| msg.hcount = 2; |
| msg.handle[0] = *((zx_handle_t*) in_buf); |
| msg.handle[1] = *(((zx_handle_t*) in_buf) + 1); |
| break; |
| } |
| |
| memcpy(msg.data, data, in_len); |
| |
| if ((r = zxrio_txn(rio, &msg)) < 0) { |
| return r; |
| } |
| |
| size_t copy_len = msg.datalen; |
| if (msg.datalen > out_len) { |
| copy_len = out_len; |
| } |
| |
| memcpy(out_buf, msg.data, copy_len); |
| |
| int handles = 0; |
| switch (IOCTL_KIND(op)) { |
| case IOCTL_KIND_GET_HANDLE: |
| handles = (msg.hcount > 0 ? 1 : 0); |
| if (handles) { |
| memcpy(out_buf, msg.handle, sizeof(zx_handle_t)); |
| } else { |
| memset(out_buf, 0, sizeof(zx_handle_t)); |
| } |
| break; |
| case IOCTL_KIND_GET_TWO_HANDLES: |
| handles = (msg.hcount > 2 ? 2 : msg.hcount); |
| if (handles) { |
| memcpy(out_buf, msg.handle, handles * sizeof(zx_handle_t)); |
| } |
| if (handles < 2) { |
| memset(out_buf, 0, (2 - handles) * sizeof(zx_handle_t)); |
| } |
| break; |
| case IOCTL_KIND_GET_THREE_HANDLES: |
| handles = (msg.hcount > 3 ? 3 : msg.hcount); |
| if (handles) { |
| memcpy(out_buf, msg.handle, handles * sizeof(zx_handle_t)); |
| } |
| if (handles < 3) { |
| memset(out_buf, 0, (3 - handles) * sizeof(zx_handle_t)); |
| } |
| break; |
| } |
| discard_handles(msg.handle + handles, msg.hcount - handles); |
| |
| LOG(1, "rio: close(%p)\n", io); |
| return r; |
| } |
| |
| #endif // ZXRIO_FIDL |
| |
| zx_status_t zxrio_process_open_response(zx_handle_t h, zxrio_describe_t* info) { |
| zx_object_wait_one(h, ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, |
| ZX_TIME_INFINITE, NULL); |
| |
| // Attempt to read the description from open |
| uint32_t dsize = sizeof(*info); |
| zx_handle_t extra_handle = ZX_HANDLE_INVALID; |
| uint32_t actual_handles; |
| zx_status_t r = zx_channel_read(h, 0, info, &extra_handle, dsize, 1, &dsize, |
| &actual_handles); |
| if (r != ZX_OK) { |
| return r; |
| } |
| if (dsize < ZXRIO_DESCRIBE_HDR_SZ || info->op != ZXRIO_ON_OPEN) { |
| r = ZX_ERR_IO; |
| } else { |
| r = info->status; |
| } |
| |
| if (dsize != sizeof(zxrio_describe_t)) { |
| r = (r != ZX_OK) ? r : ZX_ERR_IO; |
| } |
| |
| if (r != ZX_OK) { |
| if (extra_handle != ZX_HANDLE_INVALID) { |
| zx_handle_close(extra_handle); |
| } |
| return r; |
| } |
| |
| // Confirm that the objects "zxrio_describe_t" and "ObjectOnOpenEvent" |
| // are aligned enough to be compatible. |
| // |
| // This is somewhat complicated by the fact that the "ObjectOnOpenEvent" |
| // object has an optional "ObjectInfo" secondary which exists immediately |
| // following the struct. |
| static_assert(__builtin_offsetof(zxrio_describe_t, extra) == |
| FIDL_ALIGN(sizeof(ObjectOnOpenEvent)), |
| "RIO Description message doesn't align with FIDL response secondary"); |
| static_assert(sizeof(zxrio_object_info_t) == sizeof(ObjectInfo), |
| "RIO Object Info doesn't align with FIDL object info"); |
| static_assert(__builtin_offsetof(zxrio_object_info_t, file.e) == |
| __builtin_offsetof(ObjectInfo, file.event), "Unaligned File"); |
| static_assert(__builtin_offsetof(zxrio_object_info_t, pipe.s) == |
| __builtin_offsetof(ObjectInfo, pipe.socket), "Unaligned Pipe"); |
| static_assert(__builtin_offsetof(zxrio_object_info_t, vmofile.v) == |
| __builtin_offsetof(ObjectInfo, vmofile.vmo), "Unaligned Vmofile"); |
| static_assert(__builtin_offsetof(zxrio_object_info_t, device.e) == |
| __builtin_offsetof(ObjectInfo, device.event), "Unaligned Device"); |
| |
| switch (info->extra.tag) { |
| // Case: No extra handles expected |
| case FDIO_PROTOCOL_SERVICE: |
| case FDIO_PROTOCOL_DIRECTORY: |
| if (extra_handle != ZX_HANDLE_INVALID) { |
| zx_handle_close(extra_handle); |
| return ZX_ERR_IO; |
| } |
| break; |
| // Case: Extra handles optional |
| case FDIO_PROTOCOL_FILE: |
| info->extra.file.e = extra_handle; |
| break; |
| case FDIO_PROTOCOL_DEVICE: |
| info->extra.device.e = extra_handle; |
| break; |
| case FDIO_PROTOCOL_SOCKET: |
| info->extra.socket.s = extra_handle; |
| break; |
| // Case: Extra handles required |
| case FDIO_PROTOCOL_PIPE: |
| if (extra_handle == ZX_HANDLE_INVALID) { |
| return ZX_ERR_IO; |
| } |
| info->extra.pipe.s = extra_handle; |
| break; |
| case FDIO_PROTOCOL_VMOFILE: |
| if (extra_handle == ZX_HANDLE_INVALID) { |
| return ZX_ERR_IO; |
| } |
| info->extra.vmofile.v = extra_handle; |
| break; |
| default: |
| printf("Unexpected protocol type opening connection\n"); |
| if (extra_handle != ZX_HANDLE_INVALID) { |
| zx_handle_close(extra_handle); |
| } |
| return ZX_ERR_IO; |
| } |
| |
| return r; |
| } |
| |
| zx_status_t fdio_service_connect(const char* svcpath, zx_handle_t h) { |
| if (svcpath == NULL) { |
| zx_handle_close(h); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| // Otherwise attempt to connect through the root namespace |
| if (fdio_root_ns != NULL) { |
| return fdio_ns_connect(fdio_root_ns, svcpath, |
| ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE, h); |
| } |
| // Otherwise we fail |
| zx_handle_close(h); |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| zx_status_t fdio_service_connect_at(zx_handle_t dir, const char* path, zx_handle_t h) { |
| if (path == NULL) { |
| zx_handle_close(h); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (dir == ZX_HANDLE_INVALID) { |
| zx_handle_close(h); |
| return ZX_ERR_UNAVAILABLE; |
| } |
| return zxrio_connect(dir, h, ZXRIO_OPEN, ZX_FS_RIGHT_READABLE | |
| ZX_FS_RIGHT_WRITABLE, 0755, path); |
| } |
| |
| zx_status_t fdio_open_at(zx_handle_t dir, const char* path, uint32_t flags, zx_handle_t h) { |
| if (path == NULL) { |
| zx_handle_close(h); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (dir == ZX_HANDLE_INVALID) { |
| zx_handle_close(h); |
| return ZX_ERR_UNAVAILABLE; |
| } |
| return zxrio_connect(dir, h, ZXRIO_OPEN, flags, 0755, path); |
| } |
| |
| |
| zx_handle_t fdio_service_clone(zx_handle_t svc) { |
| zx_handle_t cli, srv; |
| zx_status_t r; |
| if (svc == ZX_HANDLE_INVALID) { |
| return ZX_HANDLE_INVALID; |
| } |
| if ((r = zx_channel_create(0, &cli, &srv)) < 0) { |
| return ZX_HANDLE_INVALID; |
| } |
| if ((r = zxrio_connect(svc, srv, ZXRIO_CLONE, ZX_FS_RIGHT_READABLE | |
| ZX_FS_RIGHT_WRITABLE, 0755, "")) < 0) { |
| zx_handle_close(cli); |
| return ZX_HANDLE_INVALID; |
| } |
| return cli; |
| } |
| |
| zx_status_t fdio_service_clone_to(zx_handle_t svc, zx_handle_t srv) { |
| if (srv == ZX_HANDLE_INVALID) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (svc == ZX_HANDLE_INVALID) { |
| zx_handle_close(srv); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return zxrio_connect(svc, srv, ZXRIO_CLONE, ZX_FS_RIGHT_READABLE | |
| ZX_FS_RIGHT_WRITABLE, 0755, ""); |
| } |
| |
| zx_status_t zxrio_misc(fdio_t* io, uint32_t op, int64_t off, |
| uint32_t maxreply, void* ptr, size_t len) { |
| zxrio_t* rio = (zxrio_t*)io; |
| zx_status_t r; |
| |
| #ifdef ZXRIO_FIDL |
| // Reroute FIDL2 operations |
| switch (op) { |
| case ZXRIO_STAT: { |
| size_t out_sz; |
| if ((r = fidl_stat(rio, maxreply, ptr, &out_sz)) != ZX_OK) { |
| return r; |
| } |
| return out_sz; |
| } |
| case ZXRIO_SETATTR: { |
| if (len != sizeof(vnattr_t)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return fidl_setattr(rio, (const vnattr_t*) ptr); |
| } |
| case ZXRIO_SYNC: { |
| return fidl_sync(rio); |
| } |
| case ZXRIO_READDIR: { |
| switch (off) { |
| case READDIR_CMD_RESET: |
| if ((r = fidl_rewind(rio)) != ZX_OK) { |
| return r; |
| } |
| // Fall-through to CMD_NONE |
| case READDIR_CMD_NONE: { |
| size_t out_sz; |
| if ((r = fidl_readdirents(rio, ptr, maxreply, &out_sz)) != ZX_OK) { |
| return r; |
| } |
| return out_sz; |
| } |
| default: |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| case ZXRIO_UNLINK: { |
| return fidl_unlink(rio, ptr, len); |
| } |
| case ZXRIO_TRUNCATE: { |
| return fidl_truncate(rio, off); |
| } |
| case ZXRIO_RENAME: { |
| size_t srclen = strlen(ptr); |
| size_t dstlen = len - (srclen + 2); |
| const char* src = ptr; |
| const char* dst = ptr + srclen + 1; |
| return fidl_rename(rio, src, srclen, (zx_handle_t) off, dst, dstlen); |
| } |
| case ZXRIO_LINK: { |
| size_t srclen = strlen(ptr); |
| size_t dstlen = len - (srclen + 2); |
| const char* src = ptr; |
| const char* dst = ptr + srclen + 1; |
| return fidl_link(rio, src, srclen, (zx_handle_t) off, dst, dstlen); |
| } |
| case ZXRIO_FCNTL: { |
| // zxrio_misc is extremely overloaded, so the interpretation |
| // of these arguments can seem somewhat obtuse. |
| uint32_t fcntl_op = maxreply; |
| switch (fcntl_op) { |
| case F_GETFL: { |
| uint32_t* outflags = ptr; |
| return fidl_getflags(rio, outflags); |
| } |
| case F_SETFL: { |
| uint32_t flags = off; |
| return fidl_setflags(rio, flags); |
| } |
| default: |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| } |
| case ZXRIO_MMAP: { |
| if (len != sizeof(zxrio_mmap_data_t)) { |
| printf("fdio/remoteio.c: ZXRIO_MMAP: Bad args\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| zxrio_mmap_data_t* data = ptr; |
| zx_handle_t vmo; |
| zx_status_t r = fidl_getvmo(rio, data->flags, &vmo); |
| if (r != ZX_OK) { |
| return r; |
| } |
| return vmo; |
| } |
| } |
| #endif // ZXRIO_FIDL |
| |
| zxrio_msg_t msg; |
| |
| if ((len > FDIO_CHUNK_SIZE) || (maxreply > FDIO_CHUNK_SIZE)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| memset(&msg, 0, ZXRIO_HDR_SZ); |
| msg.op = op; |
| msg.arg = maxreply; |
| msg.arg2.off = off; |
| msg.datalen = len; |
| if (ptr && len > 0) { |
| memcpy(msg.data, ptr, len); |
| } |
| switch (op) { |
| case ZXRIO_RENAME: |
| case ZXRIO_LINK: |
| // As a hack, 'Rename' and 'Link' take token handles through |
| // the offset argument. |
| msg.handle[0] = (zx_handle_t) off; |
| msg.hcount = 1; |
| } |
| |
| if ((r = zxrio_txn(rio, &msg)) < 0) { |
| return r; |
| } |
| |
| switch (op) { |
| case ZXRIO_MMAP: { |
| // Ops which receive single handles: |
| if ((msg.hcount != 1) || (msg.datalen > maxreply)) { |
| discard_handles(msg.handle, msg.hcount); |
| return ZX_ERR_IO; |
| } |
| r = msg.handle[0]; |
| memcpy(ptr, msg.data, msg.datalen); |
| break; |
| } |
| case ZXRIO_FCNTL: |
| // This is a bit of a hack, but for this case, we |
| // return 'msg.arg2.mode' in the data field to simplify |
| // this call for the client. |
| discard_handles(msg.handle, msg.hcount); |
| if (ptr) { |
| memcpy(ptr, &msg.arg2.mode, sizeof(msg.arg2.mode)); |
| } |
| break; |
| default: |
| // Ops which don't receive handles: |
| discard_handles(msg.handle, msg.hcount); |
| if (msg.datalen > maxreply) { |
| return ZX_ERR_IO; |
| } |
| if (ptr && msg.datalen > 0) { |
| memcpy(ptr, msg.data, msg.datalen); |
| } |
| } |
| return r; |
| } |
| |
| zx_status_t fdio_create_fd(zx_handle_t* handles, uint32_t* types, size_t hcount, |
| int* fd_out) { |
| fdio_t* io; |
| zx_status_t r; |
| int fd; |
| zxrio_object_info_t info; |
| zx_handle_t control_channel = ZX_HANDLE_INVALID; |
| |
| // Pack additional handles into |info|, if possible. |
| switch (PA_HND_TYPE(types[0])) { |
| case PA_FDIO_REMOTE: |
| switch (hcount) { |
| case 1: |
| io = fdio_remote_create(handles[0], 0); |
| goto bind; |
| case 2: |
| io = fdio_remote_create(handles[0], handles[1]); |
| goto bind; |
| default: |
| r = ZX_ERR_INVALID_ARGS; |
| goto fail; |
| } |
| case PA_FDIO_PIPE: |
| info.tag = FDIO_PROTOCOL_PIPE; |
| // Expected: Single pipe handle |
| if (hcount != 1) { |
| r = ZX_ERR_INVALID_ARGS; |
| goto fail; |
| } |
| info.pipe.s = handles[0]; |
| break; |
| case PA_FDIO_SOCKET: |
| info.tag = FDIO_PROTOCOL_SOCKET_CONNECTED; |
| // Expected: Single socket handle |
| if (hcount != 1) { |
| r = ZX_ERR_INVALID_ARGS; |
| goto fail; |
| } |
| info.socket.s = handles[0]; |
| break; |
| default: |
| r = ZX_ERR_IO; |
| goto fail; |
| } |
| |
| if ((r = fdio_from_handles(control_channel, &info, &io)) != ZX_OK) { |
| return r; |
| } |
| |
| bind: |
| fd = fdio_bind_to_fd(io, -1, 0); |
| if (fd < 0) { |
| fdio_close(io); |
| fdio_release(io); |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| *fd_out = fd; |
| return ZX_OK; |
| fail: |
| discard_handles(handles, hcount); |
| return r; |
| } |
| |
| zx_status_t fdio_from_handles(zx_handle_t handle, zxrio_object_info_t* info, |
| fdio_t** out) { |
| // All failure cases which require discard_handles set r and break |
| // to the end. All other cases in which handle ownership is moved |
| // on return locally. |
| zx_status_t r; |
| fdio_t* io; |
| switch (info->tag) { |
| case FDIO_PROTOCOL_DIRECTORY: |
| case FDIO_PROTOCOL_SERVICE: |
| if (handle == ZX_HANDLE_INVALID) { |
| r = ZX_ERR_INVALID_ARGS; |
| break; |
| } |
| io = fdio_remote_create(handle, 0); |
| xprintf("rio (%x,%x) -> %p\n", handle, 0, io); |
| if (io == NULL) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| *out = io; |
| return ZX_OK; |
| case FDIO_PROTOCOL_FILE: |
| if (info->file.e == ZX_HANDLE_INVALID) { |
| io = fdio_remote_create(handle, 0); |
| xprintf("rio (%x,%x) -> %p\n", handle, 0, io); |
| } else { |
| io = fdio_remote_create(handle, info->file.e); |
| xprintf("rio (%x,%x) -> %p\n", handle, info->file.e, io); |
| } |
| if (io == NULL) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| *out = io; |
| return ZX_OK; |
| case FDIO_PROTOCOL_DEVICE: |
| if (info->device.e == ZX_HANDLE_INVALID) { |
| io = fdio_remote_create(handle, 0); |
| xprintf("rio (%x,%x) -> %p\n", handle, 0, io); |
| } else { |
| io = fdio_remote_create(handle, info->device.e); |
| xprintf("rio (%x,%x) -> %p\n", handle, info->device.e, io); |
| } |
| if (io == NULL) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| *out = io; |
| return ZX_OK; |
| case FDIO_PROTOCOL_PIPE: |
| if (handle != ZX_HANDLE_INVALID) { |
| r = ZX_ERR_INVALID_ARGS; |
| break; |
| } else if ((*out = fdio_pipe_create(info->pipe.s)) == NULL) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| return ZX_OK; |
| case FDIO_PROTOCOL_VMOFILE: { |
| if (info->vmofile.v == ZX_HANDLE_INVALID) { |
| r = ZX_ERR_INVALID_ARGS; |
| break; |
| } |
| // Currently, VMO Files don't use a client-side control channel. |
| zx_handle_close(handle); |
| *out = fdio_vmofile_create(info->vmofile.v, info->vmofile.offset, |
| info->vmofile.length); |
| if (*out == NULL) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| return ZX_OK; |
| } |
| case FDIO_PROTOCOL_SOCKET_CONNECTED: |
| case FDIO_PROTOCOL_SOCKET: { |
| int flags = (info->tag == FDIO_PROTOCOL_SOCKET_CONNECTED) ? IOFLAG_SOCKET_CONNECTED : 0; |
| #if WITH_NEW_SOCKET |
| if (handle == ZX_HANDLE_INVALID || info->socket.s == ZX_HANDLE_INVALID) { |
| r = ZX_ERR_INVALID_ARGS; |
| break; |
| } |
| zx_handle_close(handle); |
| if ((*out = fdio_socket_create(info->socket.s, flags)) == NULL) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| return ZX_OK; |
| #else |
| if (handle == ZX_HANDLE_INVALID) { |
| r = ZX_ERR_INVALID_ARGS; |
| break; |
| } |
| io = fdio_socket_create(handle, info->socket.s, flags); |
| if (io == NULL) { |
| return ZX_ERR_NO_RESOURCES; |
| } |
| *out = io; |
| return ZX_OK; |
| #endif |
| } |
| default: |
| printf("fdio_from_handles: Not supported\n"); |
| r = ZX_ERR_NOT_SUPPORTED; |
| break; |
| } |
| zx_handle_t extra; |
| if (zxrio_object_extract_handle(info, &extra) == ZX_OK) { |
| zx_handle_close(extra); |
| } |
| zx_handle_close(handle); |
| return r; |
| } |
| |
| zx_status_t zxrio_getobject(zx_handle_t rio_h, uint32_t op, const char* name, |
| uint32_t flags, uint32_t mode, |
| zxrio_describe_t* info, zx_handle_t* out) { |
| if (name == NULL) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| size_t len = strlen(name); |
| if (len >= PATH_MAX) { |
| return ZX_ERR_BAD_PATH; |
| } |
| |
| if (flags & ZX_FS_FLAG_DESCRIBE) { |
| return zxrio_sync_open_connection(rio_h, op, flags, mode, name, len, info, out); |
| } else { |
| zx_handle_t h0, h1; |
| zx_status_t r; |
| if ((r = zx_channel_create(0, &h0, &h1)) < 0) { |
| return r; |
| } |
| if ((r = zxrio_connect(rio_h, h1, ZXRIO_OPEN, flags, mode, name)) < 0) { |
| zx_handle_close(h0); |
| return r; |
| } |
| // fake up a reply message since pipelined opens don't generate one |
| info->status = ZX_OK; |
| info->extra.tag = FDIO_PROTOCOL_SERVICE; |
| *out = h0; |
| return ZX_OK; |
| } |
| } |
| |
| zx_status_t zxrio_open_handle(zx_handle_t h, const char* path, uint32_t flags, |
| uint32_t mode, fdio_t** out) { |
| zx_handle_t control_channel; |
| zxrio_describe_t info; |
| zx_status_t r = zxrio_getobject(h, ZXRIO_OPEN, path, flags, mode, &info, &control_channel); |
| if (r < 0) { |
| return r; |
| } |
| return fdio_from_handles(control_channel, &info.extra, out); |
| } |
| |
| zx_status_t zxrio_open_handle_raw(zx_handle_t h, const char* path, uint32_t flags, |
| uint32_t mode, zx_handle_t *out) { |
| zx_handle_t control_channel; |
| zxrio_describe_t info; |
| zx_status_t r = zxrio_getobject(h, ZXRIO_OPEN, path, flags, mode, &info, &control_channel); |
| if (r < 0) { |
| return r; |
| } |
| if (info.extra.tag == FDIO_PROTOCOL_SERVICE) { |
| *out = control_channel; |
| return ZX_OK; |
| } |
| zx_handle_t extracted; |
| if (zxrio_object_extract_handle(&info.extra, &extracted) == ZX_OK) { |
| zx_handle_close(extracted); |
| } |
| return ZX_ERR_WRONG_TYPE; |
| } |
| |
| zx_status_t zxrio_open(fdio_t* io, const char* path, uint32_t flags, uint32_t mode, fdio_t** out) { |
| zxrio_t* rio = (void*)io; |
| return zxrio_open_handle(rio->h, path, flags, mode, out); |
| } |
| |
| static zx_status_t zxrio_clone(fdio_t* io, zx_handle_t* handles, uint32_t* types) { |
| zxrio_t* rio = (void*)io; |
| zx_handle_t h; |
| zxrio_describe_t info; |
| zx_status_t r = zxrio_getobject(rio->h, ZXRIO_CLONE, "", ZX_FS_FLAG_DESCRIBE, 0, &info, &h); |
| if (r < 0) { |
| return r; |
| } |
| handles[0] = h; |
| types[0] = PA_FDIO_REMOTE; |
| if (zxrio_object_extract_handle(&info.extra, &handles[1]) == ZX_OK) { |
| types[1] = PA_FDIO_REMOTE; |
| return 2; |
| } |
| return 1; |
| } |
| |
| static zx_status_t zxrio_unwrap(fdio_t* io, zx_handle_t* handles, uint32_t* types) { |
| zxrio_t* rio = (void*)io; |
| LOG(1, "fdio: zxrio_unwrap(%p,...)\n"); |
| zx_status_t r; |
| handles[0] = rio->h; |
| types[0] = PA_FDIO_REMOTE; |
| if (rio->h2 != 0) { |
| handles[1] = rio->h2; |
| types[1] = PA_FDIO_REMOTE; |
| r = 2; |
| } else { |
| r = 1; |
| } |
| return r; |
| } |
| |
| static void zxrio_wait_begin(fdio_t* io, uint32_t events, zx_handle_t* handle, zx_signals_t* _signals) { |
| zxrio_t* rio = (void*)io; |
| *handle = rio->h2; |
| |
| zx_signals_t signals = 0; |
| // Manually add signals that don't fit within POLL_MASK |
| if (events & POLLRDHUP) { |
| signals |= ZX_CHANNEL_PEER_CLOSED; |
| } |
| |
| // POLLERR is always detected |
| *_signals = (((POLLERR | events) & POLL_MASK) << POLL_SHIFT) | signals; |
| } |
| |
| static void zxrio_wait_end(fdio_t* io, zx_signals_t signals, uint32_t* _events) { |
| // Manually add events that don't fit within POLL_MASK |
| uint32_t events = 0; |
| if (signals & ZX_CHANNEL_PEER_CLOSED) { |
| events |= POLLRDHUP; |
| } |
| *_events = ((signals >> POLL_SHIFT) & POLL_MASK) | events; |
| } |
| |
| static fdio_ops_t zx_remote_ops = { |
| .read = zxrio_read, |
| .read_at = zxrio_read_at, |
| .write = zxrio_write, |
| .write_at = zxrio_write_at, |
| .recvfrom = fdio_default_recvfrom, |
| .sendto = fdio_default_sendto, |
| .recvmsg = fdio_default_recvmsg, |
| .sendmsg = fdio_default_sendmsg, |
| .misc = zxrio_misc, |
| .seek = zxrio_seek, |
| .close = zxrio_close, |
| .open = zxrio_open, |
| .clone = zxrio_clone, |
| .ioctl = zxrio_ioctl, |
| .wait_begin = zxrio_wait_begin, |
| .wait_end = zxrio_wait_end, |
| .unwrap = zxrio_unwrap, |
| .shutdown = fdio_default_shutdown, |
| .posix_ioctl = fdio_default_posix_ioctl, |
| .get_vmo = fdio_default_get_vmo, |
| }; |
| |
| fdio_t* fdio_remote_create(zx_handle_t h, zx_handle_t e) { |
| zxrio_t* rio = fdio_alloc(sizeof(*rio)); |
| if (rio == NULL) { |
| zx_handle_close(h); |
| zx_handle_close(e); |
| return NULL; |
| } |
| rio->io.ops = &zx_remote_ops; |
| rio->io.magic = FDIO_MAGIC; |
| atomic_init(&rio->io.refcount, 1); |
| rio->h = h; |
| rio->h2 = e; |
| atomic_init(&rio->txid, 1); |
| return &rio->io; |
| } |