blob: c9b44b0cb3d40bc6d7e9b5c90ec5346b4915aaaa [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/gpt.h"
#include <dirent.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.fshost/cpp/wire.h>
#include <fidl/fuchsia.storage.partitions/cpp/wire.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/incoming/cpp/service.h>
#include <lib/fdio/directory.h>
#include <lib/fidl/cpp/wire/status.h>
#include <lib/fit/defer.h>
#include <lib/zx/result.h>
#include <algorithm>
#include <cinttypes>
#include <string_view>
#include <fbl/algorithm.h>
#include <fbl/unique_fd.h>
#include <gpt/c/gpt.h>
#include "src/storage/lib/block_client/cpp/remote_block_device.h"
#include "src/storage/lib/paver/block-devices.h"
#include "src/storage/lib/paver/pave-logging.h"
#include "src/storage/lib/paver/utils.h"
#include "zircon/status.h"
namespace paver {
namespace {
using uuid::Uuid;
constexpr size_t ReservedHeaderBlocks(size_t blk_size) {
constexpr size_t kReservedEntryBlocks{static_cast<size_t>(16) * 1024};
return (kReservedEntryBlocks + 2 * blk_size) / blk_size;
}
zx::result<> RebindGptDriver(fidl::UnownedClientEnd<fuchsia_io::Directory> svc_root,
block_client::BlockDevice& device) {
return device.Rebind("gpt.cm");
}
zx::result<GptPartitionMetadata> QueryGptPartitionMetadata(
fidl::UnownedClientEnd<fuchsia_hardware_block_partition::Partition> volume) {
using fuchsia_hardware_block_partition::Partition;
GptPartitionMetadata metadata;
fidl::WireResult result = fidl::WireCall<Partition>(volume)->GetMetadata();
if (!result.ok()) {
return zx::error(result.status());
}
if (result->is_error()) {
return result->take_error();
}
if (!result.value()->has_name() || !result.value()->has_type_guid() ||
!result.value()->has_instance_guid()) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
return zx::ok(GptPartitionMetadata{
.name = std::string(result.value()->name().cbegin(), result.value()->name().cend()),
.type_guid = Uuid(result.value()->type_guid().value.data()),
.instance_guid = Uuid(result.value()->instance_guid().value.data()),
});
}
zx::result<std::unique_ptr<GptDevice>> CreateGptConnection(
fidl::UnownedClientEnd<fuchsia_device::Controller> controller) {
auto [controller_clone, controller_server] =
fidl::Endpoints<fuchsia_device::Controller>::Create();
if (fidl::OneWayError response =
fidl::WireCall(controller)->ConnectToController(std::move(controller_server));
!response.ok()) {
ERROR("Warning: failed to connect to GPT controller: %s\n",
response.FormatDescription().c_str());
return zx::error(response.status());
}
auto [volume, volume_server] = fidl::Endpoints<fuchsia_hardware_block_volume::Volume>::Create();
if (fidl::OneWayError response =
fidl::WireCall(controller)->ConnectToDeviceFidl(volume_server.TakeChannel());
!response.ok()) {
ERROR("Warning: failed to connect to GPT block protocol: %s\n",
response.FormatDescription().c_str());
return zx::error(response.status());
}
const fidl::WireResult result = fidl::WireCall(volume)->GetInfo();
if (!result.ok()) {
ERROR("Warning: Could not acquire GPT block info: %s\n", result.FormatDescription().c_str());
return zx::error(result.status());
}
fit::result response = result.value();
if (response.is_error()) {
ERROR("Warning: Could not acquire GPT block info: %s\n",
zx_status_get_string(response.error_value()));
return response.take_error();
}
const fuchsia_hardware_block::wire::BlockInfo& info = response.value()->info;
zx::result remote_device =
block_client::RemoteBlockDevice::Create(std::move(volume), std::move(controller_clone));
if (!remote_device.is_ok()) {
return remote_device.take_error();
}
return GptDevice::Create(std::move(remote_device.value()), info.block_size, info.block_count);
}
} // namespace
using PartitionInitSpec = GptDevicePartitioner::PartitionInitSpec;
PartitionInitSpec PartitionInitSpec::ForKnownPartition(Partition partition, PartitionScheme scheme,
size_t size_bytes) {
const char* name = PartitionName(partition, scheme);
std::optional<Uuid> type = PartitionTypeGuid(partition, scheme);
ZX_ASSERT(name && type);
return PartitionInitSpec{
.name = name,
.type = *type,
.start_block = 0,
.size_bytes = size_bytes,
};
}
bool FilterByName(const GptPartitionMetadata& part, std::string_view name) {
if (name.length() != part.name.length()) {
return false;
}
// We use a case-insensitive comparison to be compatible with the previous naming scheme.
// On a ChromeOS device, all of the kernel partitions share a common GUID type, so we
// distinguish Zircon kernel partitions based on name.
return strncasecmp(part.name.data(), name.data(), name.length()) == 0;
}
bool FilterByTypeAndName(const GptPartitionMetadata& part, const Uuid& type,
std::string_view name) {
return type == part.type_guid && FilterByName(part, name);
}
bool IsFuchsiaSystemPartition(const PaverConfig& config, const GptPartitionMetadata& part) {
if (IsFvmPartition(part)) {
return true;
}
for (const auto& name : config.system_partition_names) {
if (FilterByName(part, name)) {
return true;
}
}
return false;
}
zx::result<std::vector<GptDevicePartitioner::GptClients>> GptDevicePartitioner::FindGptDevices(
const fbl::unique_fd& devfs_root) {
fbl::unique_fd block_fd;
if (zx_status_t status = fdio_open3_fd_at(devfs_root.get(), "class/block",
static_cast<uint64_t>(fuchsia_io::wire::kPermReadable),
block_fd.reset_and_get_address());
status != ZX_OK) {
ERROR("Cannot inspect block devices: %s\n", zx_status_get_string(status));
return zx::error(status);
}
DIR* d = fdopendir(block_fd.duplicate().release());
if (d == nullptr) {
ERROR("Cannot inspect block devices: %s\n", strerror(errno));
return zx::error(ZX_ERR_INTERNAL);
}
const auto closer = fit::defer([d]() { closedir(d); });
fdio_cpp::FdioCaller block_caller(std::move(block_fd));
struct dirent* de;
std::vector<GptClients> found_devices;
while ((de = readdir(d)) != nullptr) {
if (std::string_view{de->d_name} == ".") {
continue;
}
zx::result block_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
if (block_endpoints.is_error()) {
return zx::error(ZX_ERR_INTERNAL);
}
if (zx_status_t status =
fdio_service_connect_at(block_caller.borrow_channel(), de->d_name,
block_endpoints->server.TakeChannel().release());
status != ZX_OK) {
ERROR("Cannot connect %s: %s\n", de->d_name, zx_status_get_string(status));
continue;
}
{
const fidl::WireResult result = fidl::WireCall(block_endpoints->client)->GetInfo();
if (!result.ok()) {
ERROR("Cannot get block info from %s: %s\n", de->d_name,
result.FormatDescription().c_str());
continue;
}
const fit::result response = result.value();
if (response.is_error()) {
ERROR("Cannot get block info from %s: %s\n", de->d_name,
zx_status_get_string(response.error_value()));
continue;
}
if (response.value()->info.flags & fuchsia_hardware_block::wire::Flag::kRemovable) {
continue;
}
}
zx::result controller_endpoints = fidl::CreateEndpoints<fuchsia_device::Controller>();
if (controller_endpoints.is_error()) {
return zx::error(ZX_ERR_INTERNAL);
}
std::string controller_path = std::string(de->d_name) + "/device_controller";
if (zx_status_t status =
fdio_service_connect_at(block_caller.borrow_channel(), controller_path.c_str(),
controller_endpoints->server.TakeChannel().release());
status != ZX_OK) {
ERROR("Cannot connect %s: %s\n", de->d_name, zx_status_get_string(status));
continue;
}
const fidl::WireResult result =
fidl::WireCall(controller_endpoints->client)->GetTopologicalPath();
if (!result.ok()) {
ERROR("Cannot get topological path from %s: %s\n", de->d_name,
result.FormatDescription().c_str());
continue;
}
const fit::result response = result.value();
if (response.is_error()) {
ERROR("Cannot get topological path from %s: %s\n", de->d_name,
zx_status_get_string(response.error_value()));
continue;
}
std::string path_str(response.value()->path.get());
// The GPT which will be a non-removable block device that isn't a partition or fvm created
// partition itself.
if (path_str.find("part-") == std::string::npos &&
path_str.find("/fvm/") == std::string::npos) {
found_devices.emplace_back(GptClients{
.topological_path = path_str,
.block = std::move(block_endpoints->client),
.controller = std::move(controller_endpoints->client),
});
}
}
if (found_devices.empty()) {
ERROR("No candidate GPT found\n");
return zx::error(ZX_ERR_NOT_FOUND);
}
return zx::ok(std::move(found_devices));
}
zx::result<std::unique_ptr<GptDevicePartitioner>> GptDevicePartitioner::InitializeProvidedGptDevice(
const paver::BlockDevices& devices, fidl::UnownedClientEnd<fuchsia_io::Directory> svc_root,
fidl::UnownedClientEnd<fuchsia_device::Controller> gpt_device) {
// Connect to the controller protocol.
zx::result controller_endpoints = fidl::CreateEndpoints<fuchsia_device::Controller>();
if (controller_endpoints.is_error()) {
ERROR("Warning: failed to create controller endpoints: %s\n",
controller_endpoints.status_string())
return controller_endpoints.take_error();
}
auto& [controller, controller_server] = controller_endpoints.value();
if (fidl::OneWayError response =
fidl::WireCall(gpt_device)->ConnectToController(std::move(controller_server));
!response.ok()) {
ERROR("Warning: failed to connect to GPT controller protocol: %s\n",
response.FormatDescription().c_str());
return zx::error(response.status());
}
zx::result gpt_result = CreateGptConnection(controller.borrow());
if (gpt_result.is_error()) {
ERROR("Failed to connect to GPT: %s\n", gpt_result.status_string());
return zx::error(ZX_ERR_BAD_STATE);
}
std::unique_ptr<GptDevice>& gpt = gpt_result.value();
if (!gpt->Valid()) {
ERROR("Located GPT is invalid; Attempting to initialize\n");
if (gpt->RemoveAllPartitions() != ZX_OK) {
ERROR("Failed to create empty GPT\n");
return zx::error(ZX_ERR_BAD_STATE);
}
if (gpt->Sync() != ZX_OK) {
ERROR("Failed to sync empty GPT\n");
return zx::error(ZX_ERR_BAD_STATE);
}
if (zx::result status = RebindGptDriver(svc_root, gpt->device()); status.is_error()) {
ERROR("Failed to rebind GPT\n");
return status.take_error();
}
LOG("Rebound GPT driver successfully\n");
}
return zx::ok(new GptDevicePartitioner(devices.Duplicate(), svc_root, gpt->TotalBlockCount(),
static_cast<uint32_t>(gpt->BlockSize()), std::move(gpt),
std::move(controller)));
}
zx::result<GptDevicePartitioner::InitializeGptResult> GptDevicePartitioner::InitializeGpt(
const paver::BlockDevices& devices, fidl::UnownedClientEnd<fuchsia_io::Directory> svc_root,
const PaverConfig& config, fidl::ClientEnd<fuchsia_device::Controller> block_controller) {
zx::result admin = component::ConnectAt<fuchsia_fshost::Admin>(svc_root);
if (admin.is_error()) {
return admin.take_error();
}
bool storage_host_enabled = false;
{
fidl::WireResult storage_host = fidl::WireCall(*admin)->StorageHostEnabled();
if (!storage_host.ok()) {
ERROR("Failed to query fshost for storage-host: %s\n",
storage_host.FormatDescription().c_str());
} else {
storage_host_enabled = storage_host->enabled;
}
}
if (block_controller) {
if (storage_host_enabled) {
// On storage-host, we get the GPT block device from fshost, so it doesn't make sense to have
// an explicitly provided device. The ability to explicitly specify a block device should
// eventually be removed, but for now just return ZX_ERR_NOT_SUPPORTED.
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
return InitializeProvidedGptDevice(devices, svc_root, block_controller);
}
std::vector<std::tuple<std::unique_ptr<GptDevicePartitioner>, std::string>> candidate_gpts;
if (storage_host_enabled) {
// Fshost takes care of finding the GPT block device.
zx::result gpt_device_source = BlockDevices::CreateFromPartitionService(svc_root);
if (gpt_device_source.is_error()) {
ERROR("Failed to connect to storage host: %s\n", gpt_device_source.status_string());
return gpt_device_source.take_error();
}
zx::result manager =
component::ConnectAt<fuchsia_storage_partitions::PartitionsManager>(svc_root);
if (manager.is_error()) {
return manager.take_error();
}
fidl::WireResult info = fidl::WireCall(*manager)->GetBlockInfo();
if (!info.ok()) {
ERROR("Warning: Could not acquire GPT block info: %s\n", info.FormatDescription().c_str());
return zx::error(info.status());
}
if (info.value().is_error()) {
ERROR("Warning: Could not acquire GPT block info: %s\n",
zx_status_get_string((info.value().error_value())));
return info.value().take_error();
}
auto partitioner = WrapUnique(new GptDevicePartitioner(std::move(*gpt_device_source), svc_root,
info->value()->block_count,
info->value()->block_size, {}, {}));
candidate_gpts.emplace_back(std::move(partitioner), "<from fshost>");
} else {
zx::result gpt_devices = FindGptDevices(devices.devfs_root());
if (gpt_devices.is_error()) {
ERROR("Failed to find GPT: %s\n", gpt_devices.status_string());
return gpt_devices.take_error();
}
for (auto& gpt_device : gpt_devices.value()) {
fidl::WireResult info = fidl::WireCall(gpt_device.block)->GetInfo();
if (!info.ok()) {
ERROR("Warning: Could not acquire GPT block info: %s\n", info.FormatDescription().c_str());
return zx::error(info.status());
}
if (info.value().is_error()) {
ERROR("Warning: Could not acquire GPT block info: %s\n",
zx_status_get_string(info.value().error_value()));
return info.value().take_error();
}
auto [controller, controller_server] = fidl::Endpoints<fuchsia_device::Controller>::Create();
if (fidl::OneWayStatus status = fidl::WireCall(gpt_device.controller)
->ConnectToController(std::move(controller_server));
!status.ok()) {
ERROR("Failed to connect to new controller %s\n", status.FormatDescription().c_str());
continue;
}
zx::result result = CreateGptConnection(controller.borrow());
if (result.is_error()) {
ERROR("Failed to connect to GPT: %s\n", result.status_string());
return zx::error(ZX_ERR_BAD_STATE);
}
if (!result->Valid()) {
continue;
}
auto partitioner = WrapUnique(new GptDevicePartitioner(
devices.Duplicate(), svc_root, info->value()->info.block_count,
info->value()->info.block_size, std::move(result.value()), std::move(controller)));
candidate_gpts.emplace_back(std::move(partitioner), gpt_device.topological_path);
}
}
if (candidate_gpts.empty()) {
ERROR("No candidate GPTs found.\n");
return zx::error(ZX_ERR_NOT_FOUND);
}
std::unique_ptr<GptDevicePartitioner> partitioner;
bool initialize_partition_tables = false;
if (candidate_gpts.size() == 1) {
// If there's only one GPT, but it's missing the necessary partitions, bubble that up so the
// caller can decide whether to reset the partition tables.
partitioner = std::move(std::get<0>(candidate_gpts[0]));
if (zx::result find = partitioner->FindPartition([&config](const GptPartitionMetadata& part) {
return IsFuchsiaSystemPartition(config, part);
});
find.is_error()) {
if (find.status_value() != ZX_ERR_NOT_FOUND) {
ERROR("Failed to look up FVM partition in GPT: %s\n", find.status_string());
}
ERROR(
"Unable to find a GPT on this device with the expected partitions.\n"
"Attempting to reinitialize partition tables; this only works on recovery builds!\n"
"If this fails, please run init-partition-tables to re-initialize the device.\n"
"Device path: %s\n",
std::get<1>(candidate_gpts[0]).c_str());
initialize_partition_tables = true;
}
} else {
for (auto& candidate : candidate_gpts) {
if (std::get<0>(candidate)
->FindPartition([&config](const GptPartitionMetadata& part) {
return IsFuchsiaSystemPartition(config, part);
})
.is_ok()) {
if (partitioner) {
ERROR("Found multiple block devices with valid GPTs. Unsuppported.\n");
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
partitioner = std::move(std::get<0>(candidate));
}
}
}
if (partitioner) {
return zx::ok(InitializeGptResult{
.gpt = std::move(partitioner),
.initialize_partition_tables = initialize_partition_tables,
});
}
ERROR("Unable to find a valid GPT on this device with the expected partitions.\n");
return zx::error(ZX_ERR_NOT_FOUND);
}
zx::result<std::unique_ptr<GptDevice>> GptDevicePartitioner::ConnectToGpt() const {
return CreateGptConnection(gpt_controller_.borrow());
}
struct PartitionPosition {
size_t start; // Block, inclusive
size_t length; // In Blocks
};
zx::result<std::unique_ptr<BlockPartitionClient>> GptDevicePartitioner::FindPartition(
FilterCallback filter) const {
if (!StorageHostDetected()) {
zx::result result = FindPartitionLegacy(std::move(filter));
if (result.is_error()) {
return result.take_error();
}
return zx::ok(std::move(result->partition));
}
zx::result result = devices_.OpenPartition([&](const zx::channel& chan) -> bool {
auto client =
fidl::UnownedClientEnd<fuchsia_hardware_block_partition::Partition>(chan.borrow());
zx::result metadata = QueryGptPartitionMetadata(client);
if (metadata.is_error()) {
if (metadata.status_value() != ZX_ERR_NOT_SUPPORTED) {
ERROR("Failed to query GPT partition metadata: %s\n", metadata.status_string());
}
return false;
}
return filter(*metadata);
});
if (result.is_error()) {
return result.take_error();
}
return BlockPartitionClient::Create(std::move(*result));
}
zx::result<std::vector<std::unique_ptr<BlockPartitionClient>>>
GptDevicePartitioner::FindAllPartitions(GptDevicePartitioner::FilterCallback filter) const {
zx::result result = devices_.OpenAllPartitions([&](const zx::channel& chan) -> bool {
auto client =
fidl::UnownedClientEnd<fuchsia_hardware_block_partition::Partition>((chan.borrow()));
zx::result metadata = QueryGptPartitionMetadata(client);
if (metadata.is_error()) {
if (metadata.status_value() != ZX_ERR_NOT_SUPPORTED) {
ERROR("Failed to query GPT partition metadata: %s\n", metadata.status_string());
}
return false;
}
return filter(*metadata);
});
if (result.is_error()) {
return result.take_error();
}
std::vector<std::unique_ptr<BlockPartitionClient>> clients;
for (auto& connector : *result) {
zx::result result = BlockPartitionClient::Create(std::move((connector)));
if (result.is_error()) {
return result.take_error();
}
clients.push_back(std::move(*result));
}
return zx::ok(std::move(clients));
}
zx::result<GptDevicePartitioner::FindPartitionDetailsResult>
GptDevicePartitioner::FindPartitionDetails(FilterCallback filter) const {
if (StorageHostDetected()) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
return FindPartitionLegacy(std::move(filter));
}
zx::result<GptDevicePartitioner::FindPartitionDetailsResult>
GptDevicePartitioner::FindPartitionLegacy(FilterCallback filter) const {
for (uint32_t i = 0; i < gpt::kPartitionCount; i++) {
zx::result<gpt_partition_t*> p = gpt_->GetPartition(i);
if (p.is_error()) {
continue;
}
GptPartitionMetadata metadata{};
char name[(GPT_NAME_LEN / 2) + 1] = {'\0'};
paver::utf16_to_cstring(name, p->name, GPT_NAME_LEN);
metadata.name = std::string(name, strlen(name));
metadata.instance_guid = Uuid((*p)->guid);
metadata.type_guid = Uuid((*p)->type);
if (filter(metadata)) {
LOG("Found partition in GPT, partition %u\n", i);
auto status = OpenBlockPartition(devices_, Uuid((*p)->guid), Uuid((*p)->type), ZX_SEC(5));
if (status.is_error()) {
ERROR("Couldn't open partition: %s\n", status.status_string());
return status.take_error();
}
zx::result part = BlockPartitionClient::Create(std::move(status.value()));
if (part.is_error()) {
ERROR("Failed to create partition client: %s\n", part.status_string());
return part.take_error();
}
return zx::ok(FindPartitionDetailsResult{std::move(*part), i});
}
}
return zx::error(ZX_ERR_NOT_FOUND);
}
zx::result<> GptDevicePartitioner::WipeFvm() const {
return WipeBlockPartition(devices_, std::nullopt, Uuid(GUID_FVM_VALUE));
}
zx::result<> GptDevicePartitioner::ResetPartitionTables(
std::vector<GptDevicePartitioner::PartitionInitSpec> partitions) const {
// Assign offsets and instance GUIDs as needed.
uint64_t metadata_blocks = ReservedHeaderBlocks(block_size_);
uint64_t last_available_block = block_count_ - metadata_blocks;
struct Range {
uint64_t start;
uint64_t end;
};
std::vector<Range> allocations = {
Range{.start = 0, .end = metadata_blocks},
Range{.start = last_available_block, .end = block_count_},
};
// Returns the position to insert at, and the block offset to use.
auto find_first_fit = [&](uint64_t num_blocks) -> zx::result<std::tuple<size_t, uint64_t>> {
for (size_t i = 1; i < allocations.size(); ++i) {
const auto& prev = allocations[i - 1];
const auto& next = allocations[i];
if (next.start - prev.end >= num_blocks) {
return zx::ok(std::make_tuple(i, prev.end));
}
}
return zx::error(ZX_ERR_NO_SPACE);
};
for (auto& partition : partitions) {
if (partition.size_bytes == 0) {
continue;
}
if (partition.size_bytes % block_size_ > 0) {
ERROR("Misaligned partition\n");
return zx::error(ZX_ERR_INVALID_ARGS);
}
uint64_t num_blocks = partition.size_bytes / block_size_;
constexpr const Uuid kNilGuid;
if (partition.instance == kNilGuid) {
partition.instance = Uuid::Generate();
}
auto pos = allocations.end();
if (partition.start_block == 0) {
zx::result result = find_first_fit(num_blocks);
if (result.is_error()) {
return result.take_error();
}
auto [index, off] = *result;
partition.start_block = off;
pos = std::next(allocations.begin(), static_cast<int64_t>(index));
LOG("Allocated partition %s @ %" PRIu64 "\n", partition.name.c_str(), off);
} else {
pos = std::lower_bound(
allocations.begin(), allocations.end(), partition.start_block,
[](const Range& range, uint64_t offset) -> bool { return range.start < offset; });
if (pos == allocations.end()) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
allocations.insert(pos, Range{
.start = partition.start_block,
.end = partition.start_block + num_blocks,
});
}
// Check to see if we're using storage-host. If not, we'll fall back to writing the GPT
// manually.
// TODO(https://fxbug.dev/339491886): Remove fallback once products are using storage-host.
if (!StorageHostDetected()) {
LOG("Legacy mode; manually overwriting the GPT...\n");
return ResetPartitionTablesLegacy(std::move(partitions));
}
std::vector<fuchsia_storage_partitions::wire::PartitionInfo> infos{
partitions.size(), fuchsia_storage_partitions::wire::PartitionInfo{}};
for (size_t i = 0; i < partitions.size(); ++i) {
const auto& partition = partitions[i];
if (partition.size_bytes == 0) {
continue;
}
fuchsia_storage_partitions::wire::PartitionInfo info{
.name = fidl::StringView::FromExternal(partition.name),
.start_block = partition.start_block,
.num_blocks = partition.size_bytes / block_size_,
.flags = partition.flags,
};
std::copy(partition.type.cbegin(), partition.type.cend(), info.type_guid.value.data());
std::copy(partition.instance.cbegin(), partition.instance.cend(),
info.instance_guid.value.data());
infos[i] = info;
}
zx::result recovery = component::ConnectAt<fuchsia_fshost::Recovery>(svc_root_.borrow());
if (recovery.is_error()) {
return recovery.take_error();
}
fidl::WireResult result = fidl::WireCall(*recovery)->InitSystemPartitionTable(
fidl::VectorView<fuchsia_storage_partitions::wire::PartitionInfo>::FromExternal(infos));
if (result.status() != ZX_OK) {
ERROR("Failed to reset partitions table: %s\n", result.FormatDescription().c_str());
return zx::error(result.status());
}
if (result->is_error()) {
ERROR("Failed to reset partitions table: %s\n", zx_status_get_string(result->error_value()));
return zx::error(result->error_value());
}
return zx::ok();
}
zx::result<> GptDevicePartitioner::ResetPartitionTablesLegacy(
std::span<const PartitionInitSpec> partitions) const {
if (zx_status_t status = gpt_->RemoveAllPartitions(); status != ZX_OK) {
ERROR("Failed to remove GPT partitions: %s\n", zx_status_get_string(status));
return zx::error(status);
}
for (const auto& partition : partitions) {
if (partition.size_bytes == 0) {
continue;
}
zx::result result = gpt_->AddPartition(partition.name.c_str(), partition.type.bytes(),
partition.instance.bytes(), partition.start_block,
partition.size_bytes / block_size_, partition.flags);
if (result.is_error()) {
ERROR("Failed to add partition %s at off %" PRIu64 ": %s\n", partition.name.c_str(),
partition.start_block, result.status_string());
return result.take_error();
}
}
if (zx_status_t status = gpt_->Sync(); status != ZX_OK) {
return zx::error(status);
}
zx::result result = RebindGptDriver(svc_root_, gpt_->device());
if (result.is_error()) {
ERROR("Failed to rebind GPT driver: %s\n", result.status_string());
}
return result;
}
} // namespace paver