blob: 3ae73eb7e9cc6e4e7ac2579b8dfbd4c9bb6c13a9 [file] [log] [blame] [edit]
// 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 <fuchsia/device/llcpp/fidl.h>
#include <fuchsia/io/llcpp/fidl.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/device/vfs.h>
#include <block-client/cpp/remote-block-device.h>
namespace fio = llcpp::fuchsia::io;
namespace block_client {
namespace {
zx_status_t BlockGetFifo(const zx::channel& device, zx::fifo* out_fifo) {
zx_status_t status, io_status;
io_status =
fuchsia_hardware_block_BlockGetFifo(device.get(), &status, out_fifo->reset_and_get_address());
if (io_status != ZX_OK) {
return io_status;
}
return status;
}
zx_status_t BlockCloseFifo(const zx::channel& device) {
zx_status_t status, io_status;
io_status = fuchsia_hardware_block_BlockCloseFifo(device.get(), &status);
if (io_status != ZX_OK) {
return io_status;
}
return status;
}
} // namespace
zx_status_t RemoteBlockDevice::ReadBlock(uint64_t block_num, uint64_t block_size,
void* block) const {
uint64_t offset = block_num * block_size;
fidl::Buffer<fio::File::ReadAtRequest> request_buffer;
fidl::Buffer<fio::File::ReadAtResponse> response_buffer;
auto result = fio::File::Call::ReadAt(device_.borrow(), request_buffer.view(), block_size, offset,
response_buffer.view());
if (result.status() != ZX_OK) {
return result.status();
}
if (result->s != ZX_OK) {
return result->s;
}
if (result->data.count() != block_size) {
return ZX_ERR_IO;
}
memcpy(block, result->data.data(), result->data.count());
return ZX_OK;
}
zx_status_t RemoteBlockDevice::FifoTransaction(block_fifo_request_t* requests, size_t count) {
return fifo_client_.Transaction(requests, count);
}
zx_status_t RemoteBlockDevice::GetDevicePath(size_t buffer_len, char* out_name,
size_t* out_len) const {
if (buffer_len == 0) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
zx_status_t status, io_status;
auto resp = ::llcpp::fuchsia::device::Controller::Call::GetTopologicalPath(
zx::unowned_channel(device_.get()));
io_status = resp.status();
if (io_status != ZX_OK) {
return io_status;
}
if (resp->result.is_err()) {
status = resp->result.err();
} else {
auto& r = resp->result.response();
*out_len = r.path.size();
memcpy(out_name, r.path.data(), r.path.size());
status = ZX_OK;
}
if (status != ZX_OK) {
return status;
}
// Ensure null-terminated
out_name[*out_len] = 0;
// Account for the null byte in the length, since callers expect it.
(*out_len)++;
return ZX_OK;
}
zx_status_t RemoteBlockDevice::BlockGetInfo(fuchsia_hardware_block_BlockInfo* out_info) const {
zx_status_t status;
zx_status_t io_status = fuchsia_hardware_block_BlockGetInfo(device_.get(), &status, out_info);
if (io_status != ZX_OK) {
return io_status;
}
return status;
}
zx_status_t RemoteBlockDevice::BlockAttachVmo(const zx::vmo& vmo, storage::Vmoid* out_vmoid) {
zx::vmo xfer_vmo;
zx_status_t status = vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &xfer_vmo);
if (status != ZX_OK) {
return status;
}
fuchsia_hardware_block_VmoId vmoid;
zx_status_t io_status =
fuchsia_hardware_block_BlockAttachVmo(device_.get(), xfer_vmo.release(), &status, &vmoid);
if (io_status != ZX_OK) {
return io_status;
}
*out_vmoid = storage::Vmoid(vmoid.id);
return status;
}
zx_status_t RemoteBlockDevice::VolumeQuery(
fuchsia_hardware_block_volume_VolumeInfo* out_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.
zx::channel connection, server;
zx_status_t status = zx::channel::create(0, &connection, &server);
if (status != ZX_OK) {
return status;
}
uint32_t flags = ZX_FS_FLAG_CLONE_SAME_RIGHTS;
auto result = fio::Node::Call::Clone(device_.borrow(), flags, std::move(server));
if (result.status() != ZX_OK) {
return result.status();
}
zx_status_t io_status =
fuchsia_hardware_block_volume_VolumeQuery(connection.get(), &status, out_info);
if (io_status != ZX_OK) {
return io_status;
}
return status;
}
zx_status_t RemoteBlockDevice::VolumeQuerySlices(
const uint64_t* slices, size_t slices_count,
fuchsia_hardware_block_volume_VsliceRange* out_ranges, size_t* out_ranges_count) const {
zx_status_t status, io_status;
io_status = fuchsia_hardware_block_volume_VolumeQuerySlices(
device_.get(), slices, slices_count, &status, out_ranges, out_ranges_count);
if (io_status != ZX_OK) {
return io_status;
}
return status;
}
zx_status_t RemoteBlockDevice::VolumeExtend(uint64_t offset, uint64_t length) {
zx_status_t status, io_status;
io_status = fuchsia_hardware_block_volume_VolumeExtend(device_.get(), offset, length, &status);
if (io_status != ZX_OK) {
return io_status;
}
return status;
}
zx_status_t RemoteBlockDevice::VolumeShrink(uint64_t offset, uint64_t length) {
zx_status_t status, io_status;
io_status = fuchsia_hardware_block_volume_VolumeShrink(device_.get(), offset, length, &status);
if (io_status != ZX_OK) {
return io_status;
}
return status;
}
zx_status_t RemoteBlockDevice::Create(zx::channel device, std::unique_ptr<RemoteBlockDevice>* out) {
zx::fifo fifo;
zx_status_t status = BlockGetFifo(device, &fifo);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Could not acquire block fifo: " << status;
return status;
}
block_client::Client fifo_client;
status = block_client::Client::Create(std::move(fifo), &fifo_client);
if (status != ZX_OK) {
return status;
}
*out = std::unique_ptr<RemoteBlockDevice>(
new RemoteBlockDevice(std::move(device), std::move(fifo_client)));
return ZX_OK;
}
RemoteBlockDevice::RemoteBlockDevice(zx::channel device, block_client::Client fifo_client)
: device_(std::move(device)), fifo_client_(std::move(fifo_client)) {}
RemoteBlockDevice::~RemoteBlockDevice() { BlockCloseFifo(device_); }
} // namespace block_client