blob: 0105b71569ed5595a83e7ed72b5ce8e6403522f3 [file] [log] [blame]
// 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::kOpenRightReadable | fio::wire::kOpenRightWritable;
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();
}