| // Copyright 2021 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 <fuchsia/io/llcpp/fidl.h> |
| #include <lib/fdio/directory.h> |
| #include <zircon/device/vfs.h> |
| |
| #include <fbl/auto_lock.h> |
| |
| #include "fdio_unistd.h" |
| #include "internal.h" |
| |
| namespace fio = fuchsia_io; |
| |
| __EXPORT |
| zx_status_t fdio_service_connect(const char* path, zx_handle_t h) { |
| return fdio_open(path, ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE, h); |
| } |
| |
| __EXPORT |
| zx_status_t fdio_service_connect_at(zx_handle_t dir, const char* path, zx_handle_t request_raw) { |
| auto request = fidl::ServerEnd<fio::Node>(zx::channel(request_raw)); |
| auto directory = fidl::UnownedClientEnd<fio::Directory>(dir); |
| if (!directory.is_valid()) { |
| return ZX_ERR_UNAVAILABLE; |
| } |
| |
| size_t length = 0u; |
| zx_status_t status = fdio_validate_path(path, &length); |
| if (status != ZX_OK) { |
| return status; |
| } |
| uint32_t flags = fio::wire::OPEN_RIGHT_READABLE | fio::wire::OPEN_RIGHT_WRITABLE; |
| return fidl::WireCall(directory) |
| .Open(flags, FDIO_CONNECT_MODE, fidl::StringView::FromExternal(path, length), |
| std::move(request)) |
| .status(); |
| } |
| |
| __EXPORT |
| zx_status_t fdio_service_connect_by_name(const char name[], zx_handle_t request) { |
| static zx::channel service_root; |
| |
| { |
| static std::once_flag once; |
| static zx_status_t status; |
| std::call_once(once, [&]() { |
| zx::channel request; |
| status = zx::channel::create(0, &service_root, &request); |
| if (status != ZX_OK) { |
| return; |
| } |
| // TODO(abarth): Use "/svc/" once that actually works. |
| status = fdio_service_connect("/svc/.", request.release()); |
| }); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| |
| return fdio_service_connect_at(service_root.get(), name, request); |
| } |
| |
| __EXPORT |
| zx_status_t fdio_open(const char* path, uint32_t flags, zx_handle_t request) { |
| auto handle = zx::handle(request); |
| // TODO: fdio_validate_path? |
| if (path == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| // Otherwise attempt to connect through the root namespace |
| fdio_ns_t* ns; |
| zx_status_t status = fdio_ns_get_installed(&ns); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return fdio_ns_connect(ns, path, flags, handle.release()); |
| } |
| |
| __EXPORT |
| zx_status_t fdio_open_at(zx_handle_t dir, const char* path, uint32_t flags, |
| zx_handle_t raw_request) { |
| auto request = fidl::ServerEnd<fio::Node>(zx::channel(raw_request)); |
| auto directory = fidl::UnownedClientEnd<fio::Directory>(dir); |
| if (!directory.is_valid()) { |
| return ZX_ERR_UNAVAILABLE; |
| } |
| |
| size_t length; |
| zx_status_t status = fdio_validate_path(path, &length); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| if (flags & ZX_FS_FLAG_DESCRIBE) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| return fidl::WireCall(directory) |
| .Open(flags, FDIO_CONNECT_MODE, fidl::StringView::FromExternal(path, length), |
| std::move(request)) |
| .status(); |
| } |
| |
| namespace { |
| |
| zx_status_t fdio_open_fd_common(const fdio_ptr& iodir, const char* path, uint32_t flags, |
| int* out_fd) { |
| // We're opening a file descriptor rather than just a channel (like fdio_open), so we always want |
| // to Describe (or listen for an OnOpen event on) the opened connection. This ensures that the fd |
| // is valid before returning from here, and mimics how open() and openat() behave |
| // (fdio_flags_to_zxio always add _FLAG_DESCRIBE). |
| flags |= ZX_FS_FLAG_DESCRIBE; |
| |
| zx::status io = iodir->open(path, flags, FDIO_CONNECT_MODE); |
| if (io.is_error()) { |
| return io.status_value(); |
| } |
| |
| std::optional fd = bind_to_fd(io.value()); |
| if (fd.has_value()) { |
| *out_fd = fd.value(); |
| return ZX_OK; |
| } |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| } // namespace |
| |
| __EXPORT |
| zx_status_t fdio_open_fd(const char* path, uint32_t flags, int* out_fd) { |
| zx_status_t status = fdio_validate_path(path, nullptr); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| // Since we are sending a request to the root handle, require that we start at '/'. (In fdio_open |
| // above this is done by fdio_ns_connect.) |
| if (path[0] != '/') { |
| return ZX_ERR_NOT_FOUND; |
| } |
| path++; |
| |
| return fdio_open_fd_common( |
| []() { |
| fbl::AutoLock lock(&fdio_lock); |
| return fdio_root_handle.get(); |
| }(), |
| path, flags, out_fd); |
| } |
| |
| __EXPORT |
| zx_status_t fdio_open_fd_at(int dir_fd, const char* path, uint32_t flags, int* out_fd) { |
| zx_status_t status = fdio_validate_path(path, nullptr); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| fdio_ptr iodir = fd_to_io(dir_fd); |
| if (iodir == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| return fdio_open_fd_common(iodir, path, flags, out_fd); |
| } |
| |
| __EXPORT |
| zx_handle_t fdio_service_clone(zx_handle_t handle) { |
| zx::status endpoints = fidl::CreateEndpoints<fio::Node>(); |
| if (endpoints.is_error()) { |
| return ZX_HANDLE_INVALID; |
| } |
| zx_status_t status = fdio_service_clone_to(handle, endpoints->server.channel().release()); |
| if (status != ZX_OK) { |
| return ZX_HANDLE_INVALID; |
| } |
| return endpoints->client.channel().release(); |
| } |
| |
| __EXPORT |
| zx_status_t fdio_service_clone_to(zx_handle_t handle, zx_handle_t request_raw) { |
| auto request = fidl::ServerEnd<fio::Node>(zx::channel(request_raw)); |
| auto node = fidl::UnownedClientEnd<fio::Node>(handle); |
| if (!node.is_valid()) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| uint32_t flags = ZX_FS_FLAG_CLONE_SAME_RIGHTS; |
| return fidl::WireCall(node).Clone(flags, std::move(request)).status(); |
| } |