blob: 4b2a951d7e749930c3ab8e7d226153b45e28d8f0 [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/storage/lib/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/clone.h>
#include <lib/fdio/cpp/caller.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::GetTopologicalPath() const {
const fidl::WireResult result = fidl::WireCall(controller_)->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::result<> RemoteBlockDevice::Rebind(std::string_view url_suffix) const {
const fidl::WireResult result =
fidl::WireCall(controller_)->Rebind(fidl::StringView::FromExternal(url_suffix));
if (!result.ok()) {
return zx::error(result.status());
}
fit::result response = result.value();
if (response.is_error()) {
return response.take_error();
}
return zx::ok();
}
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 = storage::Vmoid(vmoid->TakeId());
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 {
const fidl::WireResult result = fidl::WireCall(device_)->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::result<std::unique_ptr<RemoteBlockDevice>> RemoteBlockDevice::Create(
fidl::ClientEnd<fuchsia_hardware_block_volume::Volume> device,
fidl::ClientEnd<fuchsia_device::Controller> controller) {
auto [session, server] = fidl::Endpoints<fuchsia_hardware_block::Session>::Create();
if (fidl::Status result = fidl::WireCall(device)->OpenSession(std::move(server)); !result.ok()) {
return zx::error(result.status());
}
const fidl::WireResult result = fidl::WireCall(session)->GetFifo();
if (!result.ok()) {
return zx::error(result.status());
}
fit::result response = result.value();
if (response.is_error()) {
return response.take_error();
}
return zx::ok(std::unique_ptr<RemoteBlockDevice>(new RemoteBlockDevice(
std::move(device), std::move(controller), std::move(session), std::move(response->fifo))));
}
RemoteBlockDevice::RemoteBlockDevice(fidl::ClientEnd<fuchsia_hardware_block_volume::Volume> device,
fidl::ClientEnd<fuchsia_device::Controller> controller,
fidl::ClientEnd<fuchsia_hardware_block::Session> session,
zx::fifo fifo)
: device_(std::move(device)),
controller_(std::move(controller)),
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;
}
fidl::ClientEnd<fuchsia_hardware_block_volume::Volume> RemoteBlockDevice::TakeDevice() {
return std::move(device_);
}
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