blob: 9b72377977bd78b6d07944401cf9d0ee1f309f2d [file] [log] [blame] [edit]
// Copyright 2022 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/storage/lib/fs_management/cpp/component.h"
#include <fidl/fuchsia.component/cpp/wire.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <lib/component/incoming/cpp/protocol.h>
namespace fs_management {
namespace {
constexpr bool IsMultiVolume(DiskFormat df) {
return df == kDiskFormatFxfs || df == kDiskFormatFvm;
}
std::string GenerateUniqueName() {
static std::atomic<int> instance = 0;
static zx_koid_t our_koid = [] {
zx_info_handle_basic_t handle_info = {};
size_t actual = 0;
ZX_ASSERT(zx::process::self()->get_info(ZX_INFO_HANDLE_BASIC, &handle_info, sizeof(handle_info),
&actual, nullptr) == ZX_OK &&
actual == 1);
return handle_info.koid;
}();
return std::string("fs-") + std::to_string(our_koid) + "." + std::to_string(++instance);
}
} // namespace
FsComponent FsComponent::FromDiskFormat(DiskFormat df) {
std::string_view url = DiskFormatComponentUrl(df);
ZX_ASSERT(!url.empty());
return FsComponent(url, GenerateUniqueName(), "fs-collection", IsMultiVolume(df));
}
FsComponent FsComponent::FromUrl(std::string_view url, bool is_multi_volume) {
return FsComponent(url, GenerateUniqueName(), "fs-collection", is_multi_volume);
}
FsComponent FsComponent::StaticChild(std::string_view child_name, DiskFormat df) {
std::string_view url = DiskFormatComponentUrl(df);
ZX_ASSERT(!url.empty());
return FsComponent(url, child_name, std::nullopt, IsMultiVolume(df));
}
FsComponent::FsComponent(FsComponent&& other) { *this = std::move(other); }
FsComponent& FsComponent::operator=(FsComponent&& other) {
[[maybe_unused]] zx::result result = DestroyChild();
url_ = other.url_;
child_name_ = other.child_name_;
collection_name_ = other.collection_name_;
is_multi_volume_ = other.is_multi_volume_;
connected_ = other.connected_;
other.connected_ = false;
return *this;
}
FsComponent::~FsComponent() { [[maybe_unused]] zx::result result = DestroyChild(); }
zx::result<fidl::ClientEnd<fuchsia_io::Directory>> FsComponent::Connect() {
zx::result result = ConnectFsComponent(url_, child_name_, collection_name_);
if (result.is_ok())
connected_ = true;
return result;
}
zx::result<fidl::ClientEnd<fuchsia_io::Directory>> ConnectFsComponent(
std::string_view component_url, std::string_view component_child_name,
std::optional<std::string_view> component_collection_name) {
auto realm_client_end = component::Connect<fuchsia_component::Realm>();
if (realm_client_end.is_error())
return realm_client_end.take_error();
fidl::WireSyncClient realm{std::move(*realm_client_end)};
fidl::ClientEnd<fuchsia_io::Directory> client_end;
fuchsia_component_decl::wire::ChildRef child_ref{
.name = fidl::StringView::FromExternal(component_child_name)};
if (component_collection_name)
child_ref.collection = fidl::StringView::FromExternal(*component_collection_name);
auto exposed_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (exposed_endpoints.is_error())
return exposed_endpoints.take_error();
auto open_exposed_res = realm->OpenExposedDir(child_ref, std::move(exposed_endpoints->server));
if (!open_exposed_res.ok())
return zx::error(open_exposed_res.status());
if (open_exposed_res->is_ok()) {
client_end = std::move(exposed_endpoints->client);
} else if (open_exposed_res->error_value() == fuchsia_component::wire::Error::kInstanceNotFound) {
if (!component_collection_name)
return zx::error(ZX_ERR_NOT_FOUND);
// If the error was INSTANCE_NOT_FOUND, and it's expected to be in a collection, try launching
// the component ourselves.
fidl::Arena allocator;
fuchsia_component_decl::wire::CollectionRef collection_ref{
.name = fidl::StringView::FromExternal(*component_collection_name)};
auto child_decl = fuchsia_component_decl::wire::Child::Builder(allocator)
.name(component_child_name)
.url(component_url)
.startup(fuchsia_component_decl::wire::StartupMode::kLazy)
.Build();
fuchsia_component::wire::CreateChildArgs child_args;
auto create_res = realm->CreateChild(collection_ref, child_decl, child_args);
if (!create_res.ok())
return zx::error(create_res.status());
if (create_res->is_error())
return zx::error(ZX_ERR_INVALID_ARGS);
auto exposed_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (exposed_endpoints.is_error())
return exposed_endpoints.take_error();
auto open_exposed_res = realm->OpenExposedDir(child_ref, std::move(exposed_endpoints->server));
if (!open_exposed_res.ok()) {
return zx::error(open_exposed_res.status());
}
if (open_exposed_res->is_error())
return zx::error(ZX_ERR_INVALID_ARGS);
client_end = std::move(exposed_endpoints->client);
} else {
return zx::error(ZX_ERR_INVALID_ARGS);
}
return zx::ok(std::move(client_end));
}
zx::result<> FsComponent::DestroyChild() {
if (!connected_)
return zx::ok();
connected_ = false;
return collection_name_ ? DestroyFsComponent(child_name_, *collection_name_) : zx::ok();
}
zx::result<> DestroyFsComponent(std::string_view component_child_name,
std::string_view component_collection_name) {
auto realm_client_end = component::Connect<fuchsia_component::Realm>();
if (realm_client_end.is_error())
return realm_client_end.take_error();
fidl::WireSyncClient realm{std::move(*realm_client_end)};
fuchsia_component_decl::wire::ChildRef child_ref{
.name = fidl::StringView::FromExternal(component_child_name),
.collection = fidl::StringView::FromExternal(component_collection_name),
};
auto res = realm->DestroyChild(child_ref);
if (!res.ok())
return zx::error(res.status());
// If the instance was not found, that's fine. We "destroyed" it.
if (res->is_error() && res->error_value() != fuchsia_component::wire::Error::kInstanceNotFound)
return zx::error(ZX_ERR_INVALID_ARGS);
return zx::ok();
}
} // namespace fs_management