blob: 2779651e702b51060869d76ce87158b440ba240d [file] [log] [blame]
// Copyright 2025 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 "src/devices/bin/driver_manager/dictionary_util.h"
#include <fidl/fuchsia.component.sandbox/cpp/common_types_format.h>
#include <fidl/fuchsia.component.sandbox/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/component/incoming/cpp/directory.h>
#include <lib/component/incoming/cpp/directory_watcher.h>
#include <lib/fdio/directory.h>
#include "src/devices/bin/driver_manager/async_sharder.h"
#include "src/devices/lib/log/log.h"
namespace driver_manager {
void DictionaryUtil::ImportDictionary(
fuchsia_component_sandbox::DictionaryRef dictionary,
fit::callback<void(zx::result<fuchsia_component_sandbox::NewCapabilityId>)> callback) {
ImportDictionaryWire(
fuchsia_component_sandbox::wire::DictionaryRef{.token = std::move(dictionary.token())},
std::move(callback));
}
void DictionaryUtil::ImportDictionaryWire(
fuchsia_component_sandbox::wire::DictionaryRef dictionary,
fit::callback<void(zx::result<fuchsia_component_sandbox::wire::NewCapabilityId>)> callback) {
fuchsia_component_sandbox::NewCapabilityId imported = cap_id_++;
store_
->Import(imported,
fuchsia_component_sandbox::wire::Capability::WithDictionary(std::move(dictionary)))
.Then([imported, callback = std::move(callback)](
fidl::WireUnownedResult<fuchsia_component_sandbox::CapabilityStore::Import>&
result) mutable {
if (!result.ok()) {
fdf_log::warn("failed to call import dictionary ref {}", result.FormatDescription());
callback(zx::error(result.status()));
return;
}
if (result->is_error()) {
fdf_log::warn("failed to import dictionary ref {}", result->error_value());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
callback(zx::ok(imported));
});
}
void DictionaryUtil::CopyExportDictionary(
fuchsia_component_sandbox::CapabilityId dictionary,
fit::callback<void(zx::result<fuchsia_component_sandbox::DictionaryRef>)> callback) {
uint64_t dest = cap_id_++;
store_->DictionaryCopy(dictionary, dest)
.Then([this, callback = std::move(callback), dest](
fidl::WireUnownedResult<fuchsia_component_sandbox::CapabilityStore::DictionaryCopy>&
result) mutable {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to copy dictionary. {}", result.FormatDescription());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
store_->Export(dest).Then(
[callback = std::move(callback)](
fidl::WireUnownedResult<fuchsia_component_sandbox::CapabilityStore::Export>&
result) mutable {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to export dictionary. {}", result.FormatDescription());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
callback(zx::ok(fuchsia_component_sandbox::DictionaryRef{
std::move(result->value()->capability.dictionary().token)}));
});
});
}
void DictionaryUtil::DictionaryDirConnectorOpen(
fuchsia_component_sandbox::CapabilityId dictionary, std::string_view key,
fit::callback<void(zx::result<fidl::ClientEnd<fuchsia_io::Directory>>)> callback) {
fuchsia_component_sandbox::NewCapabilityId dest = cap_id_++;
store_->DictionaryGet(dictionary, fidl::StringView::FromExternal(key), dest)
.Then([this, callback = std::move(callback), dest](
fidl::WireUnownedResult<fuchsia_component_sandbox::CapabilityStore::DictionaryGet>&
result) mutable {
if (!result.ok()) {
fdf_log::error("Failed to get dictionary. {}", result.FormatDescription());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
if (result->is_error()) {
fdf_log::error("Failed to get dictionary. {}", result.value().error_value());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
store_->Export(dest).Then(
[this, callback = std::move(callback)](
fidl::WireUnownedResult<fuchsia_component_sandbox::CapabilityStore::Export>&
result) mutable {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to export dictionary. {}", result.FormatDescription());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
auto cap = result->value();
if (cap->capability.is_dir_connector_router()) {
fidl::WireClient<fuchsia_component_sandbox::DirConnectorRouter>
dir_connector_router_client(std::move(cap->capability.dir_connector_router()),
dispatcher_);
dir_connector_router_client->Route(fuchsia_component_sandbox::wire::RouteRequest{})
.Then([this, callback = std::move(callback),
dir_connector_router_client = std::move(dir_connector_router_client)](
fidl::WireUnownedResult<
fuchsia_component_sandbox::DirConnectorRouter::Route>&
result) mutable {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to route dir connector. {}",
result.FormatDescription());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
OpenDirConnector(std::move(result->value()->dir_connector()),
std::move(callback));
});
} else if (cap->capability.is_dir_connector()) {
OpenDirConnector(std::move(cap->capability.dir_connector()), std::move(callback));
} else {
callback(zx::error(ZX_ERR_INTERNAL));
}
});
});
}
void DictionaryUtil::CreateDictionaryWith(
std::unordered_map<std::string, fidl::ClientEnd<fuchsia_component_sandbox::DirReceiver>>
receivers,
fit::callback<void(zx::result<fuchsia_component_sandbox::CapabilityId>)> callback) {
fuchsia_component_sandbox::NewCapabilityId dest = cap_id_++;
store_->DictionaryCreate(dest).Then(
[this, callback = std::move(callback), dest, receivers = std::move(receivers)](
fidl::WireUnownedResult<fuchsia_component_sandbox::CapabilityStore::DictionaryCreate>&
result) mutable {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to create dictionary. {}", result.FormatDescription());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
std::shared_ptr<AsyncSharder> sharder = std::make_shared<AsyncSharder>(
receivers.size(), [callback = std::move(callback), dest](zx::result<> result) mutable {
if (result.is_error()) {
callback(result.take_error());
return;
}
callback(zx::ok(dest));
});
for (auto& [key, receiver] : receivers) {
fuchsia_component_sandbox::NewCapabilityId connector_dest = cap_id_++;
store_->DirConnectorCreate(connector_dest, std::move(receiver))
.Then(
[this, dest, key, connector_dest, sharder](
fidl::WireUnownedResult<
fuchsia_component_sandbox::CapabilityStore::DirConnectorCreate>& result) {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to create dir connector. {}",
result.FormatDescription());
sharder->CompleteShardError(ZX_ERR_INTERNAL);
return;
}
store_
->DictionaryInsert(dest,
fuchsia_component_sandbox::wire::DictionaryItem{
.key = fidl::StringView::FromExternal(key),
.value = connector_dest})
.Then(
[sharder](fidl::WireUnownedResult<
fuchsia_component_sandbox::CapabilityStore::DictionaryInsert>&
result) {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to insert dictionary item. {}",
result.FormatDescription());
sharder->CompleteShardError(ZX_ERR_INTERNAL);
return;
}
sharder->CompleteShard();
});
});
}
});
}
void DictionaryUtil::OpenDirConnector(
fuchsia_component_sandbox::wire::DirConnector connector,
fit::callback<void(zx::result<fidl::ClientEnd<fuchsia_io::Directory>>)> callback) {
fuchsia_component_sandbox::NewCapabilityId imported = cap_id_++;
store_
->Import(imported,
fuchsia_component_sandbox::wire::Capability::WithDirConnector(std::move(connector)))
.Then([this, callback = std::move(callback),
imported](fidl::WireUnownedResult<fuchsia_component_sandbox::CapabilityStore::Import>&
result) mutable {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to import dir connector. {}", result.FormatDescription());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
auto [client, server] = fidl::Endpoints<fuchsia_io::Directory>::Create();
fidl::Arena arena;
store_
->DirConnectorOpen(
fuchsia_component_sandbox::wire::CapabilityStoreDirConnectorOpenRequest::Builder(
arena)
.id(imported)
.server_end(std::move(server))
.flags(fuchsia_io::Flags::kProtocolDirectory)
.Build())
.Then([callback = std::move(callback), client = std::move(client)](
fidl::WireUnownedResult<
fuchsia_component_sandbox::CapabilityStore::DirConnectorOpen>&
result) mutable {
if (!result.ok() || result->is_error()) {
fdf_log::error("Failed to open dir connector. {}", result.FormatDescription());
callback(zx::error(ZX_ERR_INTERNAL));
return;
}
callback(zx::ok(std::move(client)));
});
});
}
void DirReceiverImpl::Receive(ReceiveRequestView request, ReceiveCompleter::Sync& completer) {
if (!request->has_subdir()) {
fdf_log::error("no subdir");
return;
}
if (request->subdir().get() == ".") {
if (dir_infos_.size() != 1 || !dir_infos_[0].is_primary) {
fdf_log::error("invalid subdir");
return;
}
zx_handle_t directory = dir_infos_[0].dir.handle()->get();
zx_status_t status =
fdio_open3_at(directory, "/",
request->has_flags() ? uint64_t{request->flags()}
: uint64_t{fuchsia_io::Flags::kProtocolDirectory},
request->channel().release());
if (status != ZX_OK) {
fdf_log::error("Failed to open directory: {}", status);
}
return;
}
auto subdir = std::string(request->subdir().get());
auto slash = subdir.find('/');
if (slash == std::string::npos) {
fdf_log::error("invalid subdir");
return;
}
std::string instance = subdir.substr(0, slash);
std::string proto = subdir.substr(slash + 1);
zx_handle_t directory = ZX_HANDLE_INVALID;
std::string source_instance;
for (auto& dir_info : dir_infos_) {
if (instance == "default") {
if (dir_info.is_primary) {
source_instance = dir_info.target_to_source_instance_mapping["default"];
directory = dir_info.dir.handle()->get();
break;
}
} else if (dir_info.parent_name == instance) {
source_instance = dir_info.target_to_source_instance_mapping["default"];
directory = dir_info.dir.handle()->get();
break;
} else {
if (dir_info.target_to_source_instance_mapping.contains(instance)) {
source_instance = dir_info.target_to_source_instance_mapping[instance];
directory = dir_info.dir.handle()->get();
break;
}
}
}
if (directory == ZX_HANDLE_INVALID) {
fdf_log::error("unknown instance");
return;
}
std::string new_subdir = std::format("{}/{}", source_instance, proto);
// This seems to be an inconsistency in the CF APIs. The requested type is for a
// fidl::ClientEnd<fuchsia_io::Directory>,
// but it seems to expect to connect directly to the protocol at the subdir.
// (aka fidl::ClientEnd<user::Protocol>).
zx_status_t status =
fdio_open3_at(directory, new_subdir.c_str(),
request->has_flags() ? uint64_t{request->flags()}
: uint64_t{fuchsia_io::Flags::kProtocolService},
request->channel().release());
if (status != ZX_OK) {
fdf_log::error("Failed to open directory: {}", status);
return;
}
}
void DirReceiverImpl::handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_component_sandbox::DirReceiver> metadata,
fidl::UnknownMethodCompleter::Sync& completer) {
std::string method_type;
switch (metadata.unknown_method_type) {
case fidl::UnknownMethodType::kOneWay:
method_type = "one-way";
break;
case fidl::UnknownMethodType::kTwoWay:
method_type = "two-way";
break;
};
fdf_log::warn("fuchsia_component_sandbox::DirReceiver received unknown {} method. Ordinal: {}",
method_type, metadata.method_ordinal);
}
} // namespace driver_manager