blob: 9b36dce01840ad0aacad50e38206735312c67646 [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/utils.h"
#include <dirent.h>
#include <fidl/fuchsia.hardware.block.partition/cpp/wire.h>
#include <fidl/fuchsia.hardware.skipblock/cpp/wire.h>
#include <fidl/fuchsia.sysinfo/cpp/wire.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/watcher.h>
#include <string_view>
#include <fbl/algorithm.h>
#include <gpt/gpt.h>
#include "src/lib/uuid/uuid.h"
#include "src/storage/lib/paver/partition-client.h"
#include "src/storage/lib/paver/pave-logging.h"
namespace paver {
namespace {
using uuid::Uuid;
namespace partition = fuchsia_hardware_block_partition;
namespace skipblock = fuchsia_hardware_skipblock;
} // namespace
// Not static so test can manipulate it.
zx_duration_t g_wipe_timeout = ZX_SEC(3);
zx::result<std::unique_ptr<VolumeConnector>> OpenBlockPartition(const paver::BlockDevices& devices,
std::optional<Uuid> unique_guid,
std::optional<Uuid> type_guid,
zx_duration_t timeout) {
ZX_ASSERT(unique_guid || type_guid);
auto cb = [&](const zx::channel& chan) {
if (type_guid) {
auto result = fidl::WireCall(fidl::UnownedClientEnd<partition::Partition>(chan.borrow()))
->GetTypeGuid();
if (!result.ok()) {
ERROR("Failed to GetTypeGuid: %s\n", result.status_string());
return false;
}
auto& response = result.value();
if (response.status != ZX_OK || type_guid != Uuid(response.guid->value.data())) {
if (response.status != ZX_OK && response.status != ZX_ERR_NOT_SUPPORTED) {
ERROR("Failed to GetTypeGuid: %s\n", zx_status_get_string(response.status));
}
return false;
}
}
if (unique_guid) {
auto result = fidl::WireCall(fidl::UnownedClientEnd<partition::Partition>(chan.borrow()))
->GetInstanceGuid();
if (!result.ok()) {
ERROR("Failed to GetInstanceGuid: %s\n", result.status_string());
return false;
}
const auto& response = result.value();
if (response.status != ZX_OK || unique_guid != Uuid(response.guid->value.data())) {
if (response.status != ZX_OK) {
ERROR("Failed to GetInstanceGuid: %s\n", zx_status_get_string(response.status));
}
return false;
}
}
return true;
};
return devices.WaitForPartition(cb, timeout);
}
constexpr char kSkipBlockDevPath[] = "class/skip-block";
zx::result<std::unique_ptr<VolumeConnector>> OpenSkipBlockPartition(
const paver::BlockDevices& devices, const Uuid& type_guid, zx_duration_t timeout) {
auto cb = [&](const zx::channel& chan) {
auto result = fidl::WireCall(fidl::UnownedClientEnd<skipblock::SkipBlock>(chan.borrow()))
->GetPartitionInfo();
if (!result.ok()) {
return false;
}
const auto& response = result.value();
return response.status == ZX_OK &&
type_guid == Uuid(response.partition_info.partition_guid.data());
};
return devices.WaitForPartition(cb, timeout, kSkipBlockDevPath);
}
bool HasSkipBlockDevice(const paver::BlockDevices& devices) {
// Our proxy for detected a skip-block device is by checking for the
// existence of a device enumerated under the skip-block class.
return OpenSkipBlockPartition(devices, GUID_ZIRCON_A_VALUE, ZX_SEC(1)).is_ok();
}
// Attempts to open and overwrite the first block of the underlying
// partition. Does not rebind partition drivers.
//
// At most one of |unique_guid| and |type_guid| may be nullptr.
zx::result<> WipeBlockPartition(const paver::BlockDevices& devices, std::optional<Uuid> unique_guid,
std::optional<Uuid> type_guid) {
zx::result partition = OpenBlockPartition(devices, unique_guid, type_guid, g_wipe_timeout);
if (partition.is_error()) {
ERROR("Warning: Could not open partition to wipe: %s\n", partition.status_string());
return partition.take_error();
}
// Overwrite the first block to (hackily) ensure the destroyed partition
// doesn't "reappear" in place.
zx::result block_partition = BlockPartitionClient::Create(std::move(*partition));
if (block_partition.is_error()) {
return block_partition.take_error();
}
auto status2 = block_partition->GetBlockSize();
if (status2.is_error()) {
ERROR("Warning: Could not get block size of partition: %s\n", status2.status_string());
return status2.take_error();
}
const size_t block_size = status2.value();
// Rely on vmos being 0 initialized.
zx::vmo vmo;
{
auto status = zx::make_result(
zx::vmo::create(fbl::round_up(block_size, zx_system_get_page_size()), 0, &vmo));
if (status.is_error()) {
ERROR("Warning: Could not create vmo: %s\n", status.status_string());
return status.take_error();
}
}
if (auto status = block_partition->Write(vmo, block_size); status.is_error()) {
ERROR("Warning: Could not write to block device: %s\n", status.status_string());
return status.take_error();
}
if (auto status = block_partition->Flush(); status.is_error()) {
ERROR("Warning: Failed to synchronize block device: %s\n", status.status_string());
return status.take_error();
}
return zx::ok();
}
zx::result<> IsBoard(fidl::UnownedClientEnd<fuchsia_io::Directory> svc_root,
std::string_view board_name) {
zx::result status =
component::ConnectAt<fuchsia_sysinfo::SysInfo>(svc_root, "fuchsia.sysinfo.SysInfo");
if (status.is_error()) {
return status.take_error();
}
fidl::WireResult result = fidl::WireCall(status.value())->GetBoardName();
if (!result.ok()) {
return zx::error(result.status());
}
fidl::WireResponse response = result.value();
if (zx_status_t status = response.status; status != ZX_OK) {
return zx::error(status);
}
if (response.name.get() == board_name) {
return zx::ok();
}
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
} // namespace paver