| // 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/component/incoming/cpp/clone.h> |
| #include <lib/component/incoming/cpp/protocol.h> |
| #include <lib/fdio/cpp/caller.h> |
| #include <lib/fdio/directory.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::result<std::unique_ptr<SkipBlockPartitionClient>> SkipBlockDevicePartitioner::FindPartition( |
| const Uuid& type) const { |
| zx::result partition = OpenSkipBlockPartition(devfs_root_, type, ZX_SEC(5)); |
| if (partition.is_error()) { |
| return partition.take_error(); |
| } |
| |
| return zx::ok(new SkipBlockPartitionClient( |
| fidl::ClientEnd<fuchsia_hardware_skipblock::SkipBlock>(std::move(partition->device)))); |
| } |
| |
| zx::result<std::unique_ptr<PartitionClient>> SkipBlockDevicePartitioner::FindFvmPartition() const { |
| // FVM partition is managed so it should expose a normal block device. |
| zx::result partition = |
| OpenBlockPartition(devfs_root_, std::nullopt, Uuid(GUID_FVM_VALUE), ZX_SEC(5)); |
| if (partition.is_error()) { |
| return partition.take_error(); |
| } |
| return zx::ok(new BlockPartitionClient(std::move(*partition))); |
| } |
| |
| zx::result<> SkipBlockDevicePartitioner::WipeFvm() const { |
| const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| zx::result partition = OpenBlockPartition(devfs_root_, std::nullopt, Uuid(fvm_type), ZX_SEC(3)); |
| if (partition.is_error()) { |
| ERROR("Warning: Could not open partition to wipe: %s\n", partition.status_string()); |
| return zx::ok(); |
| } |
| |
| fidl::WireSyncClient<device::Controller> block_client(std::move(partition->controller)); |
| |
| 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 - 1> name_buffer; |
| name_buffer.Append(response.value()->path.data(), |
| static_cast<size_t>(response.value()->path.size())); |
| |
| { |
| auto status = zx::make_result(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(https://fxbug.dev/42115657): 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; |
| |
| fdio_cpp::UnownedFdioCaller caller(devfs_root_); |
| zx::result channel = component::ConnectAt<block::Ftl>(caller.directory(), parent); |
| if (channel.is_error()) { |
| ERROR("Warning: Unable to open block parent device: %s\n", channel.status_string()); |
| return channel.take_error(); |
| } |
| |
| fidl::WireSyncClient<block::Ftl> client(std::move(channel.value())); |
| auto result2 = client->Format(); |
| |
| return zx::make_result(result2.ok() ? result2.value().status : result2.status()); |
| } |
| |
| zx::result<> SkipBlockPartitionClient::ReadPartitionInfo() { |
| if (!partition_info_) { |
| auto result = partition_->GetPartitionInfo(); |
| auto status = zx::make_result(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::result<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::result<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::result<> 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_result(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_result(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::result<> 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_result(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_result(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::result<> SkipBlockPartitionClient::WriteBytes(const zx::vmo& vmo, zx_off_t offset, |
| size_t size) { |
| zx::vmo dup; |
| if (auto status = zx::make_result(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_result(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::result<> SkipBlockPartitionClient::Trim() { return zx::error(ZX_ERR_NOT_SUPPORTED); } |
| |
| zx::result<> SkipBlockPartitionClient::Flush() { return zx::ok(); } |
| |
| } // namespace paver |