blob: 9f71f89e3489816974e0b381332535e03a416794 [file] [log] [blame]
// 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 "src/storage/lib/paver/fvm.h"
#include <lib/component/incoming/cpp/clone.h>
#include <lib/driver-integration-test/fixture.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/zx/result.h>
#include <zxtest/zxtest.h>
#include "src/storage/fvm/format.h"
#include "src/storage/fvm/fvm_sparse.h"
#include "src/storage/lib/fs_management/cpp/fvm.h"
#include "src/storage/lib/paver/test/test-utils.h"
namespace {
using device_watcher::RecursiveWaitForFile;
using driver_integration_test::IsolatedDevmgr;
constexpr size_t kSliceSize = kBlockSize * 2;
constexpr uint8_t kFvmType[GPT_GUID_LEN] = GUID_FVM_VALUE;
constexpr fvm::SparseImage SparseHeaderForSliceSize(size_t slice_size) {
fvm::SparseImage header = {};
header.slice_size = slice_size;
return header;
}
constexpr fvm::SparseImage SparseHeaderForSliceSizeAndMaxDiskSize(size_t slice_size,
size_t max_disk_size) {
fvm::SparseImage header = SparseHeaderForSliceSize(slice_size);
header.maximum_disk_size = max_disk_size;
return header;
}
class FvmTest : public zxtest::Test {
public:
FvmTest() {
IsolatedDevmgr::Args args;
args.disable_block_watcher = true;
ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr_));
ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root().get(), "sys/platform/ram-disk/ramctl")
.status_value());
}
void CreateRamdisk() { CreateRamdiskWithBlockCount(); }
void CreateRamdiskWithBlockCount(size_t block_count = kBlockCount) {
ASSERT_NO_FATAL_FAILURE(
BlockDevice::Create(devmgr_.devfs_root(), kFvmType, block_count, &device_));
ASSERT_TRUE(device_);
}
fidl::UnownedClientEnd<fuchsia_hardware_block::Block> block_interface() {
return device_->block_interface();
}
const fbl::unique_fd& devfs_root() { return devmgr_.devfs_root(); }
protected:
IsolatedDevmgr devmgr_;
std::unique_ptr<BlockDevice> device_;
};
TEST_F(FvmTest, FormatFvmEmpty) {
ASSERT_NO_FAILURES(CreateRamdisk());
ASSERT_OK(FvmPartitionFormat(devfs_root(), device_->block_interface(),
device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat));
}
TEST_F(FvmTest, TryBindEmpty) {
ASSERT_NO_FAILURES(CreateRamdisk());
ASSERT_OK(FvmPartitionFormat(devfs_root(), device_->block_interface(),
device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::TryBind));
}
TEST_F(FvmTest, TryBindAlreadyFormatted) {
ASSERT_NO_FAILURES(CreateRamdisk());
ASSERT_OK(fs_management::FvmInit(block_interface(), kSliceSize));
ASSERT_OK(FvmPartitionFormat(devfs_root(), device_->block_interface(),
device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::TryBind));
}
TEST_F(FvmTest, TryBindAlreadyBound) {
ASSERT_NO_FAILURES(CreateRamdisk());
ASSERT_OK(FvmPartitionFormat(devfs_root(), device_->block_interface(),
device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat));
ASSERT_OK(FvmPartitionFormat(devfs_root(), device_->block_interface(),
device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::TryBind));
}
TEST_F(FvmTest, TryBindAlreadyFormattedWrongSliceSize) {
ASSERT_NO_FAILURES(CreateRamdisk());
ASSERT_OK(fs_management::FvmInit(block_interface(), kSliceSize * 2));
ASSERT_OK(FvmPartitionFormat(devfs_root(), device_->block_interface(),
device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::TryBind));
}
TEST_F(FvmTest, TryBindAlreadyFormattedWithSmallerSize) {
constexpr size_t kBlockDeviceInitialSize = 1000 * kSliceSize;
constexpr size_t kBlockDeviceMaxSize = 100000 * kSliceSize;
ASSERT_NO_FAILURES(CreateRamdiskWithBlockCount(kBlockDeviceMaxSize / kBlockSize));
ASSERT_OK(fs_management::FvmInitPreallocated(block_interface(), kBlockDeviceInitialSize,
kBlockDeviceMaxSize, kSliceSize));
// Same slice size but can reference up to 200 Slices, which is far less than what the
// preallocated can have.
fvm::SparseImage header =
SparseHeaderForSliceSizeAndMaxDiskSize(kSliceSize, 2 * kBlockDeviceInitialSize);
paver::FormatResult result;
ASSERT_OK(FvmPartitionFormat(devfs_root(), device_->block_interface(),
device_->block_controller_interface(), header,
paver::BindOption::TryBind, &result));
ASSERT_EQ(paver::FormatResult::kPreserved, result);
}
TEST_F(FvmTest, TryBindAlreadyFormattedWithBiggerSize) {
constexpr size_t kBlockDeviceInitialSize = 1000 * kSliceSize;
constexpr size_t kBlockDeviceMaxSize = 100000 * kSliceSize;
ASSERT_NO_FAILURES(CreateRamdiskWithBlockCount(kBlockDeviceMaxSize / kBlockSize));
ASSERT_OK(fs_management::FvmInitPreallocated(block_interface(), kBlockDeviceInitialSize,
kBlockDeviceMaxSize / 100, kSliceSize));
// Same slice size but can reference up to 200 Slices, which is far less than what the
// preallocated can have.
fvm::SparseImage header = SparseHeaderForSliceSizeAndMaxDiskSize(kSliceSize, kBlockDeviceMaxSize);
paver::FormatResult result;
ASSERT_OK(FvmPartitionFormat(devfs_root(), device_->block_interface(),
device_->block_controller_interface(), header,
paver::BindOption::TryBind, &result));
ASSERT_EQ(paver::FormatResult::kReformatted, result);
}
constexpr char kRamdisk0BlobPath[] =
"sys/platform/ram-disk/ramctl/ramdisk-0/block/fvm/blobfs-p-1/block";
constexpr char kRamdisk0DataPath[] =
"sys/platform/ram-disk/ramctl/ramdisk-0/block/fvm/data-p-2/block";
constexpr char kRamdisk1BlobPath[] =
"sys/platform/ram-disk/ramctl/ramdisk-1/block/fvm/blobfs-p-1/block";
constexpr char kRamdisk1DataPath[] =
"sys/platform/ram-disk/ramctl/ramdisk-1/block/fvm/data-p-2/block";
TEST_F(FvmTest, AllocateEmptyPartitions) {
ASSERT_NO_FAILURES(CreateRamdisk());
zx::result fvm = FvmPartitionFormat(
devfs_root(), device_->block_interface(), device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat);
ASSERT_OK(fvm);
auto [volume, volume_server] =
fidl::Endpoints<fuchsia_hardware_block_volume::VolumeManager>::Create();
ASSERT_OK(fidl::WireCall(fvm.value())->ConnectToDeviceFidl(volume_server.TakeChannel()).status());
ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), volume));
zx_handle_t devfs_root_handle;
fdio_fd_clone(devfs_root().get(), &devfs_root_handle);
zx::result blob_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(blob_endpoints);
auto& [blob, blob_server] = blob_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0BlobPath,
blob_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(blob)->GetInfo().status());
zx::result data_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(data_endpoints);
auto& [data, data_server] = data_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0DataPath,
data_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(data)->GetInfo().status());
}
TEST_F(FvmTest, WipeWithMultipleFvm) {
ASSERT_NO_FAILURES(CreateRamdisk());
zx::result fvm1 = FvmPartitionFormat(
devfs_root(), device_->block_interface(), device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat);
ASSERT_OK(fvm1);
auto [volume1, volume_server1] =
fidl::Endpoints<fuchsia_hardware_block_volume::VolumeManager>::Create();
ASSERT_OK(
fidl::WireCall(fvm1.value())->ConnectToDeviceFidl(volume_server1.TakeChannel()).status());
ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), volume1));
zx_handle_t devfs_root_handle;
fdio_fd_clone(devfs_root().get(), &devfs_root_handle);
{
zx::result blob_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(blob_endpoints);
auto& [blob, blob_server] = blob_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0BlobPath,
blob_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(blob)->GetInfo().status());
zx::result data_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(data_endpoints);
auto& [data, data_server] = data_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0DataPath,
data_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(data)->GetInfo().status());
}
// Save the old device.
std::unique_ptr<BlockDevice> first_device = std::move(device_);
ASSERT_NO_FAILURES(CreateRamdisk());
zx::result fvm2 = FvmPartitionFormat(
devfs_root(), device_->block_interface(), device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat);
ASSERT_OK(fvm2);
auto [volume2, volume_server2] =
fidl::Endpoints<fuchsia_hardware_block_volume::VolumeManager>::Create();
ASSERT_OK(
fidl::WireCall(fvm2.value())->ConnectToDeviceFidl(volume_server2.TakeChannel()).status());
ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), volume2));
{
zx::result blob_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(blob_endpoints);
auto& [blob, blob_server] = blob_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk1BlobPath,
blob_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(blob)->GetInfo().status());
zx::result data_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(data_endpoints);
auto& [data, data_server] = data_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk1DataPath,
data_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(data)->GetInfo().status());
}
std::array<uint8_t, fvm::kGuidSize> blobfs_guid = GUID_BLOB_VALUE;
ASSERT_OK(paver::WipeAllFvmPartitionsWithGuid(fvm2.value(), blobfs_guid.data()));
// Check we can still open the first ramdisk's blobfs:
{
zx::result blob_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(blob_endpoints);
auto& [blob, blob_server] = blob_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0BlobPath,
blob_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(blob)->GetInfo().status());
}
// But not the second's.
{
zx::result blob_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(blob_endpoints);
auto& [blob, blob_server] = blob_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk1BlobPath,
blob_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(blob)->GetInfo().status());
}
}
TEST_F(FvmTest, Unbind) {
ASSERT_NO_FAILURES(CreateRamdisk());
zx::result fvm = FvmPartitionFormat(
devfs_root(), device_->block_interface(), device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat);
ASSERT_OK(fvm);
auto [volume, volume_server] =
fidl::Endpoints<fuchsia_hardware_block_volume::VolumeManager>::Create();
ASSERT_OK(fidl::WireCall(fvm.value())->ConnectToDeviceFidl(volume_server.TakeChannel()).status());
ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), volume));
zx_handle_t devfs_root_handle;
fdio_fd_clone(devfs_root().get(), &devfs_root_handle);
zx::result blob_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(blob_endpoints);
auto& [blob, blob_server] = blob_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0BlobPath,
blob_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(blob)->GetInfo().status());
zx::result data_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(data_endpoints);
auto& [data, data_server] = data_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0DataPath,
data_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(data)->GetInfo().status());
ASSERT_OK(paver::FvmUnbind(devfs_root(), "/dev/sys/platform/ram-disk/ramctl/ramdisk-0/block"));
}
TEST_F(FvmTest, UnbindInvalidPath) {
ASSERT_NO_FAILURES(CreateRamdisk());
zx::result fvm = FvmPartitionFormat(
devfs_root(), device_->block_interface(), device_->block_controller_interface(),
SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat);
ASSERT_OK(fvm);
auto [volume, volume_server] =
fidl::Endpoints<fuchsia_hardware_block_volume::VolumeManager>::Create();
ASSERT_OK(fidl::WireCall(fvm.value())->ConnectToDeviceFidl(volume_server.TakeChannel()).status());
ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), volume));
zx_handle_t devfs_root_handle;
fdio_fd_clone(devfs_root().get(), &devfs_root_handle);
zx::result blob_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(blob_endpoints);
auto& [blob, blob_server] = blob_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0BlobPath,
blob_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(blob)->GetInfo().status());
zx::result data_endpoints = fidl::CreateEndpoints<fuchsia_hardware_block::Block>();
ASSERT_OK(data_endpoints);
auto& [data, data_server] = data_endpoints.value();
ASSERT_OK(fdio_service_connect_at(devfs_root_handle, kRamdisk0DataPath,
data_server.TakeChannel().release()));
ASSERT_OK(fidl::WireCall(data)->GetInfo().status());
// Path too short
ASSERT_EQ(paver::FvmUnbind(devfs_root(), "/dev"), ZX_ERR_INVALID_ARGS);
// Path too long
char path[PATH_MAX + 2];
memset(path, 'a', sizeof(path));
path[sizeof(path) - 1] = '\0';
ASSERT_EQ(paver::FvmUnbind(devfs_root(), path), ZX_ERR_INVALID_ARGS);
ASSERT_OK(paver::FvmUnbind(devfs_root(), "/dev/sys/platform/ram-disk/ramctl/ramdisk-0/block"));
}
} // namespace