blob: 96cdaf662037cea71807d25f34b9cfd125b7ebd1 [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/block/drivers/block-verity/verified-volume-client.h"
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.hardware.block.verified/cpp/wire.h>
#include <lib/device-watcher/cpp/device-watcher.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/fdio.h>
#include <zircon/status.h>
#include <cstring>
#include <fbl/string.h>
#include <fbl/string_buffer.h>
#include "lib/stdcompat/string_view.h"
namespace block_verity {
namespace {
const char* kDriverLib = "block-verity.cm";
zx_status_t BindVerityDriver(fidl::UnownedClientEnd<fuchsia_device::Controller> channel) {
const fidl::WireResult result =
fidl::WireCall(channel)->Bind(fidl::StringView::FromExternal(kDriverLib));
if (!result.ok()) {
return result.status();
}
const fit::result response = result.value();
if (response.is_error()) {
return response.error_value();
}
return ZX_OK;
}
zx::result<fbl::String> RelativeTopologicalPath(
fidl::UnownedClientEnd<fuchsia_device::Controller> channel) {
const fidl::WireResult result = fidl::WireCall(channel)->GetTopologicalPath();
if (!result.ok()) {
return zx::error(result.status());
}
const fit::result response = result.value();
if (response.is_error()) {
return zx::error(response.error_value());
}
std::string_view path = response.value()->path.get();
constexpr std::string_view kSlashDevSlash = "/dev/";
if (!cpp20::starts_with(path, kSlashDevSlash)) {
return zx::error(ZX_ERR_INTERNAL);
}
return zx::ok(path.substr(kSlashDevSlash.size()));
}
} // namespace
VerifiedVolumeClient::VerifiedVolumeClient(
fidl::ClientEnd<fuchsia_hardware_block_verified::DeviceManager> verity_chan,
fidl::ClientEnd<fuchsia_device::Controller> verity_controller, fbl::unique_fd devfs_root_fd)
: verity_chan_(std::move(verity_chan)),
verity_controller_(std::move(verity_controller)),
devfs_root_fd_(std::move(devfs_root_fd)) {}
zx::result<std::unique_ptr<VerifiedVolumeClient>> VerifiedVolumeClient::CreateFromBlockDevice(
fidl::UnownedClientEnd<fuchsia_device::Controller> device, fbl::unique_fd devfs_root_fd,
Disposition disposition, const zx::duration& timeout) {
// Bind the driver if called for by `disposition`.
if (disposition == kDriverNeedsBinding) {
if (zx_status_t status = BindVerityDriver(device); status != ZX_OK) {
printf("VerifiedVolumeClient: couldn't bind driver: %s", zx_status_get_string(status));
return zx::error(status);
}
}
// Compute the path at which we expect to see the verity child device appear.
zx::result block_dev_path = RelativeTopologicalPath(device);
if (block_dev_path.is_error()) {
printf("VerifiedVolumeClient: could not compute relative path: %s\n",
block_dev_path.status_string());
return block_dev_path.take_error();
}
fbl::String verity_path = fbl::String::Concat({block_dev_path.value(), "/verity"});
// Wait for the device to appear.
zx::result channel =
device_watcher::RecursiveWaitForFile(devfs_root_fd.get(), verity_path.c_str(), timeout);
if (channel.is_error()) {
printf("VerifiedVolumeClient: verity device failed to appear: %s\n", channel.status_string());
return channel.take_error();
}
fbl::String controller_path = fbl::String::Concat({verity_path, "/device_controller"});
zx::result controller_channel =
device_watcher::RecursiveWaitForFile(devfs_root_fd.get(), controller_path.c_str(), timeout);
if (controller_channel.is_error()) {
printf("VerifiedVolumeClient: verity controller failed to appear: %s\n",
controller_channel.status_string());
return controller_channel.take_error();
}
return zx::ok(std::make_unique<VerifiedVolumeClient>(
fidl::ClientEnd<fuchsia_hardware_block_verified::DeviceManager>{std::move(channel.value())},
fidl::ClientEnd<fuchsia_device::Controller>{std::move(controller_channel.value())},
std::move(devfs_root_fd)));
}
zx::result<fidl::ClientEnd<fuchsia_hardware_block::Block>> VerifiedVolumeClient::OpenForAuthoring(
const zx::duration& timeout) {
// make FIDL call to open in authoring mode
fidl::Arena allocator;
// Request the device be opened for writes
auto open_resp =
fidl::WireCall(verity_chan_)
->OpenForWrite(
fuchsia_hardware_block_verified::wire::Config::Builder(allocator)
.hash_function(fuchsia_hardware_block_verified::wire::HashFunction::kSha256)
.block_size(fuchsia_hardware_block_verified::wire::BlockSize::kSize4096)
.Build());
if (open_resp.status() != ZX_OK) {
return zx::error(open_resp.status());
}
if (open_resp->is_error()) {
return open_resp->take_error();
}
// Compute path of expected `mutable` child device via relative topological path
zx::result verity_path = RelativeTopologicalPath(verity_controller_);
if (verity_path.is_error()) {
printf("VerifiedVolumeClient: could not compute relative path: %s\n",
verity_path.status_string());
return verity_path.take_error();
}
fbl::String mutable_path = fbl::String::Concat({verity_path.value(), "/mutable"});
// Wait for the `mutable` child device to appear
if (zx::result channel =
device_watcher::RecursiveWaitForFile(devfs_root_fd_.get(), mutable_path.c_str(), timeout);
channel.is_error()) {
printf("VerifiedVolumeClient: mutable device failed to appear: %s\n", channel.status_string());
return channel.take_error();
}
// Then wait for the `block` child of that mutable device
fbl::String mutable_block_path = fbl::String::Concat({mutable_path, "/block"});
zx::result channel = device_watcher::RecursiveWaitForFile(devfs_root_fd_.get(),
mutable_block_path.c_str(), timeout);
if (channel.is_error()) {
printf("VerifiedVolumeClient: mutable block device failed to appear: %s\n",
channel.status_string());
return channel.take_error();
}
// Open child device and return
fdio_cpp::UnownedFdioCaller caller(devfs_root_fd_.get());
return component::ConnectAt<fuchsia_hardware_block::Block>(caller.directory(),
mutable_block_path);
}
zx_status_t VerifiedVolumeClient::Close() {
// Close the device cleanly
auto close_resp = fidl::WireCall(verity_chan_)->Close();
if (close_resp.status() != ZX_OK) {
return close_resp.status();
}
if (close_resp->is_error()) {
return close_resp->error_value();
}
return ZX_OK;
}
zx_status_t VerifiedVolumeClient::CloseAndGenerateSeal(
fidl::AnyArena& arena,
fuchsia_hardware_block_verified::wire::DeviceManagerCloseAndGenerateSealResponse* out) {
// We use the caller-provided buffer FIDL call style because the caller
// needs to do something with the seal returned, so we need to keep the
// response object alive so that the caller can interact with it after this
// function returns.
auto seal_resp = fidl::WireCall(verity_chan_).buffer(arena)->CloseAndGenerateSeal();
if (seal_resp.status() != ZX_OK) {
return seal_resp.status();
}
if (seal_resp->is_error()) {
return seal_resp->error_value();
}
*out = *seal_resp.value().value();
return ZX_OK;
}
zx::result<fidl::ClientEnd<fuchsia_hardware_block::Block>>
VerifiedVolumeClient::OpenForVerifiedRead(const digest::Digest& expected_seal,
const zx::duration& timeout) {
// make FIDL call to open in authoring mode
fidl::Arena allocator;
// Make a copy of the seal to send.
fuchsia_hardware_block_verified::wire::Sha256Seal sha256_seal;
expected_seal.CopyTo(sha256_seal.superblock_hash.begin(), sha256_seal.superblock_hash.size());
// Request the device be opened for verified read
auto open_resp =
fidl::WireCall(verity_chan_)
->OpenForVerifiedRead(
fuchsia_hardware_block_verified::wire::Config::Builder(allocator)
.hash_function(fuchsia_hardware_block_verified::wire::HashFunction::kSha256)
.block_size(fuchsia_hardware_block_verified::wire::BlockSize::kSize4096)
.Build(),
fuchsia_hardware_block_verified::wire::Seal::WithSha256(
fidl::ObjectView<fuchsia_hardware_block_verified::wire::Sha256Seal>::FromExternal(
&sha256_seal)));
if (open_resp.status() != ZX_OK) {
return zx::error(open_resp.status());
}
if (open_resp->is_error()) {
return open_resp->take_error();
}
// Compute path of expected `verified` child device via relative topological path
zx::result verity_path = RelativeTopologicalPath(verity_controller_);
if (verity_path.is_error()) {
printf("VerifiedVolumeClient: could not compute relative path: %s\n",
verity_path.status_string());
return verity_path.take_error();
}
fbl::String verified_path = fbl::String::Concat({verity_path.value(), "/verified"});
// Wait for the `verified` child device to appear
if (zx::result channel = device_watcher::RecursiveWaitForFile(devfs_root_fd_.get(),
verified_path.c_str(), timeout);
channel.is_error()) {
printf("VerifiedVolumeClient: verified device failed to appear: %s\n", channel.status_string());
return channel.take_error();
}
// Then wait for the `block` child of that verified device
fbl::String verified_block_path = fbl::String::Concat({verified_path, "/block"});
zx::result channel = device_watcher::RecursiveWaitForFile(devfs_root_fd_.get(),
verified_block_path.c_str(), timeout);
if (channel.is_error()) {
printf("VerifiedVolumeClient: verified block device failed to appear: %s\n",
channel.status_string());
return channel.take_error();
}
fdio_cpp::UnownedFdioCaller caller(devfs_root_fd_.get());
return component::ConnectAt<fuchsia_hardware_block::Block>(caller.directory(),
verified_block_path);
}
} // namespace block_verity