blob: 1ed13a5768d2341b63fae07cb2f53f2f2474a0ee [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 <lib/driver2/devfs_exporter.h>
#include <lib/fpromise/bridge.h>
namespace fdfs = fuchsia_device_fs;
namespace driver {
namespace {
fpromise::promise<void, zx_status_t> CheckFileExists(
const fidl::WireSharedClient<fuchsia_io::Directory>& dir, std::string_view path,
async_dispatcher_t* dispatcher) {
auto svc_endpoints = fidl::CreateEndpoints<fuchsia_io::Node>();
if (svc_endpoints.is_error()) {
return fpromise::make_error_promise(svc_endpoints.status_value());
}
auto result = dir->Open(fuchsia_io::wire::OpenFlags::kNodeReference, 0,
fidl::StringView::FromExternal(path), std::move(svc_endpoints->server));
if (!result.ok()) {
return fpromise::make_error_promise(result.status());
}
auto file =
fidl::WireSharedClient<fuchsia_io::Node>(std::move(svc_endpoints->client), dispatcher);
// Call something simple on the Node to make sure we actually opened it successfully.
// Otherwise, the open call is pipelined and it could fail silently.
fpromise::bridge<void, zx_status_t> bridge;
file->GetFlags().ThenExactlyOnce(
[file = file.Clone(), completer = std::move(bridge.completer)](
fidl::WireUnownedResult<fuchsia_io::Node::GetFlags>& result) mutable {
if (result.ok()) {
completer.complete_ok();
} else {
zx_status_t status = result.status();
if (status == ZX_ERR_PEER_CLOSED) {
status = ZX_ERR_NOT_FOUND;
}
completer.complete_error(status);
}
});
return bridge.consumer.promise();
}
} // namespace
zx::status<DevfsExporter> DevfsExporter::Create(
const Namespace& ns, async_dispatcher_t* dispatcher,
fidl::WireSharedClient<fuchsia_io::Directory> svc_dir) {
auto result = ns.Connect<fdfs::Exporter>();
if (result.is_error()) {
return result.take_error();
}
fidl::WireSharedClient<fdfs::Exporter> client(std::move(*result), dispatcher);
return zx::ok(DevfsExporter(dispatcher, std::move(client), std::move(svc_dir)));
}
DevfsExporter::DevfsExporter(async_dispatcher_t* dispatcher,
fidl::WireSharedClient<fdfs::Exporter> exporter,
fidl::WireSharedClient<fuchsia_io::Directory> svc_dir)
: dispatcher_(dispatcher), exporter_(std::move(exporter)), svc_dir_(std::move(svc_dir)) {}
fpromise::promise<void, zx_status_t> DevfsExporter::ExportImpl(std::string_view service_path,
std::string_view devfs_path,
uint32_t protocol_id) const {
// Get a connection to svc_dir.
auto svc_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (svc_endpoints.is_error()) {
return fpromise::make_error_promise(svc_endpoints.status_value());
}
auto result =
svc_dir_->Clone(fuchsia_io::wire::OpenFlags::kCloneSameRights,
fidl::ServerEnd<fuchsia_io::Node>(svc_endpoints->server.TakeChannel()));
if (!result.ok()) {
return fpromise::make_error_promise(result.status());
}
// Call the Exporter function.
fpromise::bridge<void, zx_status_t> bridge;
auto callback = [completer = std::move(bridge.completer)](
fidl::WireUnownedResult<fdfs::Exporter::Export>& response) mutable {
if (!response.ok()) {
completer.complete_error(response.status());
} else if (response->is_error()) {
completer.complete_error(response->error_value());
} else {
completer.complete_ok();
}
};
exporter_
->Export(std::move(svc_endpoints->client), fidl::StringView::FromExternal(service_path),
fidl::StringView::FromExternal(devfs_path), protocol_id)
.ThenExactlyOnce(std::move(callback));
return bridge.consumer.promise_or(fpromise::error(ZX_ERR_INTERNAL));
}
fpromise::promise<void, zx_status_t> DevfsExporter::Export(std::string_view service_path,
std::string_view devfs_path,
uint32_t protocol_id) const {
return CheckFileExists(svc_dir_, service_path, dispatcher_)
.and_then([this, service_path = std::string(service_path),
devfs_path = std::string(devfs_path),
protocol_id]() { return ExportImpl(service_path, devfs_path, protocol_id); });
}
} // namespace driver