blob: d58c741a656beed30595b36043b5e8a397943207 [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/storage/lib/paver/skip-block.h"
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.hardware.block/cpp/wire.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/service/llcpp/service.h>
#include <libgen.h>
#include <fbl/string_buffer.h>
#include <gpt/gpt.h>
#include "src/lib/uuid/uuid.h"
#include "src/storage/lib/paver/fvm.h"
#include "src/storage/lib/paver/pave-logging.h"
#include "src/storage/lib/paver/utils.h"
namespace paver {
namespace {
using uuid::Uuid;
namespace block = fuchsia_hardware_block;
namespace device = fuchsia_device;
namespace skipblock = fuchsia_hardware_skipblock;
} // namespace
zx::status<std::unique_ptr<SkipBlockPartitionClient>> SkipBlockDevicePartitioner::FindPartition(
const Uuid& type) const {
auto status = OpenSkipBlockPartition(devfs_root_, type, ZX_SEC(5));
if (status.is_error()) {
return status.take_error();
}
return zx::ok(new SkipBlockPartitionClient(std::move(status.value())));
}
zx::status<std::unique_ptr<PartitionClient>> SkipBlockDevicePartitioner::FindFvmPartition() const {
// FVM partition is managed so it should expose a normal block device.
auto status = OpenBlockPartition(devfs_root_, std::nullopt, Uuid(GUID_FVM_VALUE), ZX_SEC(5));
if (status.is_error()) {
return status.take_error();
}
return zx::ok(new BlockPartitionClient(std::move(status.value())));
}
zx::status<> SkipBlockDevicePartitioner::WipeFvm() const {
const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
auto status = OpenBlockPartition(devfs_root_, std::nullopt, Uuid(fvm_type), ZX_SEC(3));
if (status.is_error()) {
ERROR("Warning: Could not open partition to wipe: %s\n", status.status_string());
return zx::ok();
}
// Note: converting from |fuchsia.hardware.block.partition/Partition| to
// |fuchsia.device/Controller| works because devfs connections compose |Controller|.
fidl::WireSyncClient<device::Controller> block_client(
fidl::ClientEnd<device::Controller>(status.value().TakeChannel()));
auto result = block_client->GetTopologicalPath();
if (!result.ok()) {
ERROR("Warning: Could not get name for partition: %s\n", zx_status_get_string(result.status()));
return zx::error(result.status());
}
const auto& response = result.value();
if (response.is_error()) {
ERROR("Warning: Could not get name for partition: %s\n",
zx_status_get_string(response.error_value()));
return zx::error(response.error_value());
}
fbl::StringBuffer<PATH_MAX> name_buffer;
name_buffer.Append(response.value()->path.data(),
static_cast<size_t>(response.value()->path.size()));
{
auto status = zx::make_status(FvmUnbind(devfs_root_, name_buffer.data()));
if (status.is_error()) {
// The driver may refuse to bind to a corrupt volume.
ERROR("Warning: Failed to unbind FVM: %s\n", status.status_string());
}
}
// TODO(fxbug.dev/39761): Clean this up.
const char* parent = dirname(name_buffer.data());
constexpr char kDevRoot[] = "/dev/";
constexpr size_t kDevRootLen = sizeof(kDevRoot) - 1;
if (strncmp(parent, kDevRoot, kDevRootLen) != 0) {
ERROR("Warning: Unrecognized partition name: %s\n", parent);
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
parent += kDevRootLen;
zx::channel local, remote;
{
auto status = zx::make_status(zx::channel::create(0, &local, &remote));
if (status.is_error()) {
ERROR("Warning: Failed to create channel pair: %s\n", status.status_string());
return status.take_error();
}
}
fdio_cpp::UnownedFdioCaller caller(devfs_root_.get());
{
auto status =
zx::make_status(fdio_service_connect_at(caller.borrow_channel(), parent, remote.release()));
if (status.is_error()) {
ERROR("Warning: Unable to open block parent device: %s\n", status.status_string());
return status.take_error();
}
}
fidl::WireSyncClient<block::Ftl> client(std::move(local));
auto result2 = client->Format();
return zx::make_status(result2.ok() ? result2.value().status : result2.status());
}
zx::status<> SkipBlockPartitionClient::ReadPartitionInfo() {
if (!partition_info_) {
auto result = partition_->GetPartitionInfo();
auto status = zx::make_status(result.ok() ? result.value().status : result.status());
if (status.is_error()) {
ERROR("Failed to get partition info with status: %s\n", status.status_string());
return status.take_error();
}
partition_info_ = result.value().partition_info;
}
return zx::ok();
}
zx::status<size_t> SkipBlockPartitionClient::GetBlockSize() {
auto status = ReadPartitionInfo();
if (status.is_error()) {
return status.take_error();
}
return zx::ok(static_cast<size_t>(partition_info_->block_size_bytes));
}
zx::status<size_t> SkipBlockPartitionClient::GetPartitionSize() {
auto status = ReadPartitionInfo();
if (status.is_error()) {
return status.take_error();
}
return zx::ok(partition_info_->block_size_bytes * partition_info_->partition_block_count);
}
zx::status<> SkipBlockPartitionClient::Read(const zx::vmo& vmo, size_t size) {
auto status = SkipBlockPartitionClient::GetBlockSize();
if (status.is_error()) {
return status.take_error();
}
const size_t block_size = status.value();
zx::vmo dup;
if (auto status = zx::make_status(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup)); status.is_error()) {
ERROR("Couldn't duplicate buffer vmo\n");
return status.take_error();
}
skipblock::wire::ReadWriteOperation operation = {
.vmo = std::move(dup),
.vmo_offset = 0,
.block = 0,
.block_count = static_cast<uint32_t>(size / block_size),
};
auto result = partition_->Read(std::move(operation));
{
auto status = zx::make_status(result.ok() ? result.value().status : result.status());
if (status.is_error()) {
ERROR("Error reading partition data: %s\n", status.status_string());
return status.take_error();
}
}
return zx::ok();
}
zx::status<> SkipBlockPartitionClient::Write(const zx::vmo& vmo, size_t size) {
auto status = SkipBlockPartitionClient::GetBlockSize();
if (status.is_error()) {
return status.take_error();
}
size_t block_size = status.value();
zx::vmo dup;
if (auto status = zx::make_status(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup)); status.is_error()) {
ERROR("Couldn't duplicate buffer vmo\n");
return status.take_error();
}
skipblock::wire::ReadWriteOperation operation = {
.vmo = std::move(dup),
.vmo_offset = 0,
.block = 0,
.block_count = static_cast<uint32_t>(size / block_size),
};
auto result = partition_->Write(std::move(operation));
{
auto status = zx::make_status(result.ok() ? result.value().status : result.status());
if (status.is_error()) {
ERROR("Error writing partition data: %s\n", status.status_string());
return status.take_error();
}
}
return zx::ok();
}
zx::status<> SkipBlockPartitionClient::WriteBytes(const zx::vmo& vmo, zx_off_t offset,
size_t size) {
zx::vmo dup;
if (auto status = zx::make_status(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup)); status.is_error()) {
ERROR("Couldn't duplicate buffer vmo\n");
return status.take_error();
}
skipblock::wire::WriteBytesOperation operation = {
.vmo = std::move(dup),
.vmo_offset = 0,
.offset = offset,
.size = size,
.mode = skipblock::wire::WriteBytesMode::kReadModifyEraseWrite,
};
auto result = partition_->WriteBytes(std::move(operation));
auto status = zx::make_status(result.ok() ? result.value().status : result.status());
if (status.is_error()) {
ERROR("Error writing partition data: %s\n", status.status_string());
return status.take_error();
}
return zx::ok();
}
zx::status<> SkipBlockPartitionClient::Trim() { return zx::error(ZX_ERR_NOT_SUPPORTED); }
zx::status<> SkipBlockPartitionClient::Flush() { return zx::ok(); }
fidl::ClientEnd<fuchsia_hardware_skipblock::SkipBlock> SkipBlockPartitionClient::GetChannel() {
return service::MaybeClone(partition_.client_end(), service::AssumeProtocolComposesNode);
}
fbl::unique_fd SkipBlockPartitionClient::block_fd() { return fbl::unique_fd(); }
} // namespace paver