blob: e5913d29b0f92d6a202870628f179e3c5fe5fefc [file] [log] [blame]
// Copyright 2023 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 <fidl/fuchsia.io/cpp/wire.h>
#include <lib/component/incoming/cpp/constants.h>
#include <lib/component/incoming/cpp/internal.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fdio/directory.h>
#include <zircon/assert.h>
#include <cstring>
namespace component::internal {
namespace {
namespace fio = fuchsia_io;
constexpr size_t kMaxComponentLength = fio::wire::kMaxNameLength;
// Buffer used to hold the maximum possible path of a service instance. A service instance path is
// made from two path components, each separated by '/', and must be terminated with a null byte.
using ServiceInstancePathBuffer = char[(2 * kMaxComponentLength) + 1 + 1];
// Combines `service` and `instance` with a '/' separator. Guarantees the result ends with a null
// byte so it can be safely passed to functions that require C strings.
zx::result<std::string_view> MakeInstancePath(ServiceInstancePathBuffer& buffer,
std::string_view service, std::string_view instance) {
if (service.empty() || service.size() > kMaxComponentLength) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (instance.size() > kMaxComponentLength) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (service[0] == '/') {
return zx::error(ZX_ERR_INVALID_ARGS);
}
const uint64_t path_size = service.size() + instance.size() + 1;
ZX_ASSERT(path_size < sizeof buffer);
std::memcpy(buffer, service.data(), service.size());
buffer[service.size()] = '/';
std::memcpy(buffer + service.size() + 1, instance.data(), instance.size());
buffer[path_size] = '\0';
return zx::ok(std::string_view(buffer, path_size));
}
zx::result<fio::wire::Flags> EnsureDirectoryProtocol(fio::wire::Flags flags) {
const fio::wire::Flags protocols = flags & fio::wire::kMaskKnownProtocols;
if (protocols && (protocols != fio::wire::Flags::kProtocolDirectory)) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
return zx::ok(flags | fio::wire::Flags::kProtocolDirectory);
}
} // namespace
zx::result<> ConnectRaw(zx::channel server_end, std::string_view path) {
std::string owned_path(path);
if (zx_status_t status = fdio_service_connect(owned_path.c_str(), server_end.release());
status != ZX_OK) {
return zx::error(status);
}
return zx::ok();
}
zx::result<> ConnectAtRaw(fidl::UnownedClientEnd<fio::Directory> svc_dir, zx::channel server_end,
std::string_view protocol_name) {
std::string path(protocol_name);
if (zx_status_t status =
fdio_service_connect_at(svc_dir.handle()->get(), path.c_str(), server_end.release());
status != ZX_OK) {
return zx::error(status);
}
return zx::ok();
}
zx::result<fidl::ClientEnd<fio::Directory>> OpenDirectory(std::string_view path,
fio::wire::Flags flags) {
zx::result directory_flags = EnsureDirectoryProtocol(flags);
if (directory_flags.is_error()) {
return directory_flags.take_error();
}
auto [client, server] = fidl::Endpoints<fio::Directory>::Create();
std::string owned_path(path);
if (zx_status_t status = fdio_open3(owned_path.c_str(), uint64_t{*directory_flags},
server.TakeChannel().release());
status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(client));
}
zx::result<fidl::ClientEnd<fio::Directory>> OpenDirectoryAt(
fidl::UnownedClientEnd<fio::Directory> dir, std::string_view path, fio::wire::Flags flags) {
zx::result directory_flags = EnsureDirectoryProtocol(flags);
if (directory_flags.is_error()) {
return directory_flags.take_error();
}
auto [client, server] = fidl::Endpoints<fio::Directory>::Create();
std::string owned_path(path);
if (zx_status_t status =
fdio_open3_at(dir.handle()->get(), owned_path.c_str(), uint64_t{*directory_flags},
server.TakeChannel().release());
status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(client));
}
zx::result<> CloneRaw(fidl::UnownedClientEnd<fuchsia_unknown::Cloneable>&& cloneable,
zx::channel server_end) {
const fidl::Status result = fidl::WireCall(cloneable)->Clone(
fidl::ServerEnd<fuchsia_unknown::Cloneable>(std::move(server_end)));
if (!result.ok()) {
return zx::error(result.status());
}
return zx::ok();
}
zx::result<> OpenNamedServiceRaw(std::string_view service, std::string_view instance,
zx::channel remote) {
auto client = OpenServiceRoot();
if (client.is_error()) {
return client.take_error();
}
return OpenNamedServiceAtRaw(*client, service, instance, std::move(remote));
}
zx::result<> OpenNamedServiceAtRaw(fidl::UnownedClientEnd<fio::Directory> dir,
std::string_view service, std::string_view instance,
zx::channel remote) {
ServiceInstancePathBuffer path_buffer;
zx::result<std::string_view> path = MakeInstancePath(path_buffer, service, instance);
if (!path.is_ok()) {
return path.take_error();
}
return zx::make_result(fdio_open3_at(dir.channel()->get(), path->data(),
uint64_t{kServiceRootFlags}, remote.release()));
}
zx::result<> ProtocolOpenFunc(zx::unowned_channel dir, fidl::StringView path,
fidl::internal::AnyTransport remote) {
std::string owned_path(path.get());
return zx::make_result(
fdio_service_connect_at(dir->get(), owned_path.c_str(),
remote.release<fidl::internal::ChannelTransport>().release()));
}
} // namespace component::internal