blob: 59ab5cbb506b952bd498557b9f7c3461619df8c1 [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 <fcntl.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/fdio/directory.h>
#include <fbl/auto_lock.h>
#include <fbl/no_destructor.h>
#include "sdk/lib/fdio/directory_internal.h"
#include "sdk/lib/fdio/fdio_unistd.h"
#include "sdk/lib/fdio/internal.h"
#include "sdk/lib/fdio/unistd.h"
namespace fio = fuchsia_io;
__EXPORT
zx_status_t fdio_service_connect(const char* path, zx_handle_t h) {
zx::handle handle{h};
fdio_ns_t* ns;
if (zx_status_t status = fdio_ns_get_installed(&ns); status != ZX_OK) {
return status;
}
return fdio_ns_service_connect(ns, path, handle.release());
}
__EXPORT
zx_status_t fdio_service_connect_at(zx_handle_t dir, const char* path, zx_handle_t h) {
return fdio_open_at(dir, path, 0, h);
}
__EXPORT
zx_status_t fdio_service_connect_by_name(const char* name, zx_handle_t request) {
// We can't destroy |service_root| at static destruction time as some multithreaded programs call
// exit() from one thread while other threads are calling in to fdio functions. Destroying
// |service_root| in this scenario would result in crashes on those threads. See
// https://fxbug.dev/42069066 for details.
static fbl::NoDestructor<zx::result<zx::channel>> service_root = []() -> zx::result<zx::channel> {
zx::channel client, request;
zx_status_t status = zx::channel::create(0, &client, &request);
if (status != ZX_OK) {
return zx::error(status);
}
status = fdio_service_connect("/svc", request.release());
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(client));
}();
if (service_root->is_error()) {
return service_root->status_value();
}
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) {
zx::handle handle{request};
fdio_ns_t* ns;
if (zx_status_t status = fdio_ns_get_installed(&ns); status != ZX_OK) {
return status;
}
return fdio_ns_open(ns, path, flags, handle.release());
}
namespace fdio_internal {
// TODO(https://fxbug.dev/42180154): This should reuse the logic used by openat().
zx_status_t fdio_open_at(fidl::UnownedClientEnd<fio::Directory> directory, std::string_view path,
fuchsia_io::wire::OpenFlags flags, fidl::ServerEnd<fio::Node> request) {
if (!directory.is_valid()) {
return ZX_ERR_UNAVAILABLE;
}
return fidl::WireCall(directory)
->Open(flags, {}, fidl::StringView::FromExternal(path), std::move(request))
.status();
}
} // namespace fdio_internal
__EXPORT
zx_status_t fdio_open_at(zx_handle_t dir, const char* path, uint32_t flags,
zx_handle_t raw_request) {
size_t length;
zx_status_t status = fdio_validate_path(path, &length);
if (status != ZX_OK) {
return status;
}
fidl::UnownedClientEnd<fio::Directory> directory(dir);
fidl::ServerEnd<fio::Node> request((zx::channel(raw_request)));
auto fio_flags = static_cast<fio::wire::OpenFlags>(flags);
return fdio_internal::fdio_open_at(directory, std::string_view(path, length), fio_flags,
std::move(request));
}
namespace {
zx_status_t fdio_open_fd_at_internal(int dirfd, const char* dirty_path, fio::wire::OpenFlags flags,
bool allow_absolute_path, 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 |= fio::wire::OpenFlags::kDescribe;
zx::result io = fdio_internal::open_at_impl(dirfd, dirty_path, flags,
{
.disallow_directory = false,
.allow_absolute_path = allow_absolute_path,
});
if (io.is_error()) {
return io.status_value();
}
std::optional fd = bind_to_fd(io.value());
if (!fd.has_value()) {
return ZX_ERR_BAD_STATE;
}
*out_fd = fd.value();
return ZX_OK;
}
} // namespace
__EXPORT
zx_status_t fdio_open_fd(const char* path, uint32_t flags, int* out_fd) {
return fdio_open_fd_at_internal(AT_FDCWD, path, static_cast<fio::wire::OpenFlags>(flags), true,
out_fd);
}
__EXPORT
zx_status_t fdio_open_fd_at(int dirfd, const char* path, uint32_t flags, int* out_fd) {
return fdio_open_fd_at_internal(dirfd, path, static_cast<fio::wire::OpenFlags>(flags), false,
out_fd);
}
__EXPORT
zx_handle_t fdio_service_clone(zx_handle_t handle) {
zx::result 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;
}
fio::wire::OpenFlags flags = fio::wire::OpenFlags::kCloneSameRights;
return fidl::WireCall(node)->Clone(flags, std::move(request)).status();
}