blob: 903ca8c8c5e80caa3f0c29c8fc864d8feb573229 [file] [log] [blame]
// Copyright 2019 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/lib/storage/block_client/cpp/remote_block_device.h"
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.hardware.block.volume/cpp/wire.h>
#include <fidl/fuchsia.hardware.block/cpp/wire.h>
#include <lib/component/incoming/cpp/service_client.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/vmo.h>
namespace block_client {
zx_status_t RemoteBlockDevice::FifoTransaction(block_fifo_request_t* requests, size_t count) {
return fifo_client_.Transaction(requests, count);
}
zx::result<std::string> RemoteBlockDevice::GetDevicePath() const {
// TODO(https://fxbug.dev/112484): this relies on multiplexing.
const fidl::WireResult result =
fidl::WireCall(fidl::UnownedClientEnd<fuchsia_device::Controller>(device_.channel().borrow()))
->GetTopologicalPath();
if (!result.ok()) {
return zx::error(result.status());
}
fit::result response = result.value();
if (response.is_error()) {
return response.take_error();
}
return zx::ok(response->path.get());
}
zx_status_t RemoteBlockDevice::BlockGetInfo(
fuchsia_hardware_block::wire::BlockInfo* out_info) const {
const fidl::WireResult result = fidl::WireCall(device_)->GetInfo();
if (!result.ok()) {
return result.status();
}
const fit::result response = result.value();
if (response.is_error()) {
return response.error_value();
}
*out_info = response.value()->info;
return ZX_OK;
}
zx_status_t RemoteBlockDevice::BlockAttachVmo(const zx::vmo& vmo, storage::Vmoid* out_vmoid) {
zx::result vmoid = fifo_client_.RegisterVmo(vmo);
if (vmoid.is_error()) {
return vmoid.error_value();
}
*out_vmoid = std::move(vmoid.value());
return ZX_OK;
}
zx_status_t RemoteBlockDevice::VolumeGetInfo(
fuchsia_hardware_block_volume::wire::VolumeManagerInfo* out_manager_info,
fuchsia_hardware_block_volume::wire::VolumeInfo* out_volume_info) const {
// Querying may be used to confirm if the underlying connection is capable of
// communicating the FVM protocol. Clone the connection, since if the block
// device does NOT speak the Volume protocol, the connection is terminated.
//
// TODO(https://fxbug.dev/112484): this relies on multiplexing.
zx::result clone = component::Clone(device_, component::AssumeProtocolComposesNode);
if (clone.is_error()) {
return clone.status_value();
}
fidl::ClientEnd<fuchsia_hardware_block_volume::Volume> volume(clone.value().TakeChannel());
const fidl::WireResult result = fidl::WireCall(volume)->GetVolumeInfo();
if (!result.ok()) {
return result.status();
}
const fidl::WireResponse response = result.value();
if (zx_status_t status = response.status; status != ZX_OK) {
return status;
}
*out_manager_info = *response.manager;
*out_volume_info = *response.volume;
return ZX_OK;
}
zx_status_t RemoteBlockDevice::VolumeQuerySlices(
const uint64_t* slices, size_t slices_count,
fuchsia_hardware_block_volume::wire::VsliceRange* out_ranges, size_t* out_ranges_count) const {
fidl::UnownedClientEnd<fuchsia_hardware_block_volume::Volume> volume(device_.channel().borrow());
const fidl::WireResult result = fidl::WireCall(volume)->QuerySlices(
fidl::VectorView<uint64_t>::FromExternal(const_cast<uint64_t*>(slices), slices_count));
if (!result.ok()) {
return result.status();
}
const fidl::WireResponse response = result.value();
if (zx_status_t status = response.status; status != ZX_OK) {
return status;
}
std::copy_n(response.response.data(), response.response_count, out_ranges);
*out_ranges_count = response.response_count;
return ZX_OK;
}
zx_status_t RemoteBlockDevice::VolumeExtend(uint64_t offset, uint64_t length) {
fidl::UnownedClientEnd<fuchsia_hardware_block_volume::Volume> volume(device_.channel().borrow());
const fidl::WireResult result = fidl::WireCall(volume)->Extend(offset, length);
if (!result.ok()) {
return result.status();
}
const fidl::WireResponse response = result.value();
return response.status;
}
zx_status_t RemoteBlockDevice::VolumeShrink(uint64_t offset, uint64_t length) {
fidl::UnownedClientEnd<fuchsia_hardware_block_volume::Volume> volume(device_.channel().borrow());
const fidl::WireResult result = fidl::WireCall(volume)->Shrink(offset, length);
if (!result.ok()) {
return result.status();
}
const fidl::WireResponse response = result.value();
return response.status;
}
zx_status_t RemoteBlockDevice::Create(fidl::ClientEnd<fuchsia_hardware_block::Block> device,
std::unique_ptr<RemoteBlockDevice>* out) {
zx::result endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Session>();
if (endpoints.is_error()) {
return endpoints.error_value();
}
auto& [session, server] = endpoints.value();
if (fidl::Status result = fidl::WireCall(device)->OpenSession(std::move(server)); !result.ok()) {
return result.status();
}
const fidl::WireResult result = fidl::WireCall(session)->GetFifo();
if (!result.ok()) {
return result.status();
}
const fit::result response = result.value();
if (response.is_error()) {
return response.error_value();
}
*out = std::unique_ptr<RemoteBlockDevice>(
new RemoteBlockDevice(std::move(device), std::move(session), std::move(response->fifo)));
return ZX_OK;
}
zx::result<std::unique_ptr<RemoteBlockDevice>> RemoteBlockDevice::Create(int fd) {
fdio_cpp::UnownedFdioCaller caller(fd);
// TODO(https://fxbug.dev/112484): this relies on multiplexing.
zx::result clone = component::Clone(caller.borrow_as<fuchsia_hardware_block::Block>(),
component::AssumeProtocolComposesNode);
if (clone.is_error()) {
return clone.take_error();
}
std::unique_ptr<block_client::RemoteBlockDevice> client;
zx_status_t status = block_client::RemoteBlockDevice::Create(std::move(clone.value()), &client);
return zx::make_result(status, std::move(client));
}
RemoteBlockDevice::RemoteBlockDevice(fidl::ClientEnd<fuchsia_hardware_block::Block> device,
fidl::ClientEnd<fuchsia_hardware_block::Session> session,
zx::fifo fifo)
: device_(std::move(device)), fifo_client_(std::move(session), std::move(fifo)) {}
zx_status_t ReadWriteBlocks(fidl::UnownedClientEnd<fuchsia_hardware_block::Block> device,
void* buffer, size_t buffer_length, size_t offset, bool write) {
// Get the Block info for block size calculations:
const fidl::WireResult result = fidl::WireCall(device)->GetInfo();
if (!result.ok()) {
return result.status();
}
const fit::result response = result.value();
if (response.is_error()) {
return response.error_value();
}
zx::vmo vmo;
if (zx_status_t status = zx::vmo::create(buffer_length, 0, &vmo); status != ZX_OK) {
return status;
}
size_t block_size = response.value()->info.block_size;
if (!buffer || buffer_length % block_size != 0 || offset % block_size != 0) {
return ZX_ERR_INVALID_ARGS;
}
zx::vmo read_vmo;
if (write) {
if (zx_status_t status = vmo.write(buffer, 0, buffer_length); status != ZX_OK) {
return status;
}
const fidl::WireResult result =
fidl::WireCall(device)->WriteBlocks(std::move(vmo), buffer_length, offset, 0);
if (!result.ok()) {
return result.status();
}
const fit::result response = result.value();
if (response.is_error()) {
return response.error_value();
}
} else {
// if reading, duplicate the vmo so we will retain a copy
if (zx_status_t status = vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &read_vmo); status != ZX_OK) {
return status;
}
const fidl::WireResult result =
fidl::WireCall(device)->ReadBlocks(std::move(vmo), buffer_length, offset, 0);
if (!result.ok()) {
return result.status();
}
const fit::result response = result.value();
if (response.is_error()) {
return response.error_value();
}
}
if (!write) {
return read_vmo.read(buffer, 0, buffer_length);
}
return ZX_OK;
}
zx_status_t SingleReadBytes(fidl::UnownedClientEnd<fuchsia_hardware_block::Block> device,
void* buffer, size_t buffer_size, size_t offset) {
return ReadWriteBlocks(device, buffer, buffer_size, offset, false);
}
zx_status_t SingleWriteBytes(fidl::UnownedClientEnd<fuchsia_hardware_block::Block> device,
void* buffer, size_t buffer_size, size_t offset) {
return ReadWriteBlocks(device, buffer, buffer_size, offset, true);
}
} // namespace block_client