blob: 64db639eca205734a99c5991eb182a2e5e35612e [file] [log] [blame]
// Copyright 2020 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/package_resolver.h"
#include <fuchsia/io/llcpp/fidl.h>
#include <fuchsia/pkg/llcpp/fidl.h>
#include <lib/fdio/directory.h>
#include <fbl/string_printf.h>
#include "src/devices/lib/log/log.h"
#include "src/lib/pkg_url/fuchsia_pkg_url.h"
#include "src/lib/storage/vfs/cpp/vfs.h"
namespace fio = fuchsia_io;
namespace internal {
zx::status<PackageResolver::FetchDriverVmoResult> PackageResolver::FetchDriverVmo(
const std::string& package_url) {
if (!resolver_client_.channel().is_valid()) {
zx_status_t status = ConnectToResolverService();
if (status != ZX_OK) {
LOGF(ERROR, "Failed to connect to package resolver service");
return zx::error(status);
}
}
auto result = Resolve(package_url);
if (!result.is_ok()) {
LOGF(ERROR, "Failed to resolve package url %s, err %d", package_url.c_str(),
result.status_value());
return zx::error(result.status_value());
}
return LoadDriverPackage(&result.value() /* package_dir */);
}
zx_status_t PackageResolver::ConnectToResolverService() {
zx::channel local, remote;
zx_status_t status = zx::channel::create(0u, &local, &remote);
if (status != ZX_OK) {
return status;
}
const auto path =
fbl::StringPrintf("/svc/%s", fidl::DiscoverableProtocolName<fuchsia_pkg::PackageResolver>);
status = fdio_service_connect(path.c_str(), remote.release());
if (status != ZX_OK) {
return status;
}
resolver_client_ = fidl::WireSyncClient<fuchsia_pkg::PackageResolver>(std::move(local));
return ZX_OK;
}
zx::status<fidl::WireSyncClient<fio::Directory>> PackageResolver::Resolve(
const std::string_view& package_url) {
zx::channel local, remote;
zx_status_t status = zx::channel::create(0u, &local, &remote);
if (status != ZX_OK) {
return zx::error(status);
}
component::FuchsiaPkgUrl fp;
bool parse = fp.Parse(std::string(package_url));
if (!parse) {
LOGF(ERROR, "Failed to parse package url: %s", package_url.data());
return zx::error(ZX_ERR_INTERNAL);
}
::fidl::VectorView<::fidl::StringView> selectors;
// This is synchronous for now so we can get the proof of concept working.
// Eventually we will want to do this asynchronously.
auto result = resolver_client_.Resolve(
::fidl::StringView(fidl::StringView::FromExternal(fp.package_path())), std::move(selectors),
std::move(remote));
if (!result.ok() || result.Unwrap()->result.is_err()) {
LOGF(ERROR, "Failed to resolve package");
return zx::error(!result.ok() ? ZX_ERR_INTERNAL : result.Unwrap()->result.err());
}
return zx::ok(fidl::WireSyncClient<fio::Directory>(std::move(local)));
}
zx::status<PackageResolver::FetchDriverVmoResult> PackageResolver::LoadDriverPackage(
fidl::WireSyncClient<fio::Directory>* package_dir) {
const uint32_t kFileRights = fio::wire::OPEN_RIGHT_READABLE | fio::wire::OPEN_RIGHT_EXECUTABLE;
const uint32_t kDriverVmoFlags = fio::wire::VMO_FLAG_READ | fio::wire::VMO_FLAG_EXEC;
constexpr char kLibDir[] = "lib";
// Open the lib directory.
zx::channel local, remote;
zx_status_t status = zx::channel::create(0u, &local, &remote);
if (status != ZX_OK) {
return zx::error(status);
}
auto open_result = package_dir->Open(kFileRights, 0u, kLibDir, std::move(remote));
if (!open_result.ok()) {
LOGF(ERROR, "Failed to open driver package directory");
return zx::error(ZX_ERR_INTERNAL);
}
// This could be simplified by using POSIX APIs instead.
fidl::WireSyncClient<fio::Directory> lib_dir(std::move(local));
auto libname_result = GetDriverLibname(&lib_dir);
if (!libname_result.is_ok()) {
LOGF(ERROR, "Failed to get driver libname");
return zx::error(libname_result.status_value());
}
// Open and duplicate the driver vmo.
status = zx::channel::create(0u, &local, &remote);
if (status != ZX_OK) {
return zx::error(status);
}
auto file_open_result =
lib_dir.Open(kFileRights, 0u /* mode */,
::fidl::StringView(fidl::StringView::FromExternal(libname_result.value())),
std::move(remote));
if (!file_open_result.ok()) {
LOGF(ERROR, "Failed to open driver file: %s", libname_result.value().c_str());
return zx::error(ZX_ERR_INTERNAL);
}
fidl::WireSyncClient<fio::File> file_client(std::move(local));
auto file_res = file_client.GetBuffer(kDriverVmoFlags);
if (!file_res.ok() || file_res.Unwrap()->s != ZX_OK) {
LOGF(ERROR, "Failed to get driver vmo");
return zx::error(ZX_ERR_INTERNAL);
}
auto buf = file_res.Unwrap()->buffer.get();
if (!buf->vmo.is_valid()) {
return zx::error(ZX_ERR_INTERNAL);
}
return zx::ok(PackageResolver::FetchDriverVmoResult{libname_result.value(), std::move(buf->vmo)});
}
zx::status<std::string> PackageResolver::GetDriverLibname(
fidl::WireSyncClient<fio::Directory>* lib_dir) {
auto dirent_result = lib_dir->ReadDirents(fio::wire::MAX_BUF);
if (!dirent_result.ok() || dirent_result.status() != ZX_OK) {
return zx::error(dirent_result.status());
}
fidl::WireResponse<fio::Directory::ReadDirents>* dirent_response = dirent_result.Unwrap();
if (dirent_response->dirents.count() == 0) {
return zx::error(ZX_ERR_INTERNAL);
}
size_t offset = 0;
auto data_ptr = dirent_response->dirents.data();
// For now just assume only one driver shared library is included in the package.
while (sizeof(vdirent_t) < dirent_response->dirents.count() - offset) {
const vdirent_t* entry = reinterpret_cast<const vdirent_t*>(data_ptr + offset);
std::string entry_name(entry->name, entry->size);
offset += sizeof(vdirent_t) + entry->size;
if (entry_name == "." || entry_name == "..") {
continue;
}
return zx::ok(std::move(entry_name));
}
return zx::error(ZX_ERR_NOT_FOUND);
}
} // namespace internal