blob: 652b41cfcf45e0be32834145bd4998afdf6557e8 [file] [log] [blame]
// Copyright 2022 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 <fcntl.h>
#include <fuchsia/io/cpp/fidl.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/zx/status.h>
#include <utility>
#include <fbl/unique_fd.h>
#include "gtest/gtest.h"
#include "src/lib/storage/block_client/cpp/remote_block_device.h"
#include "src/storage/fs_test/fs_test_fixture.h"
#include "storage/buffer/owned_vmoid.h"
#include "zircon/errors.h"
namespace fs_test {
namespace {
using DeviceTest = fs_test::FilesystemTest;
void CreateFxFile(const std::string& kFilename) {
fbl::unique_fd fd(open(kFilename.c_str(), O_CREAT | O_RDWR, 0666));
ASSERT_TRUE(fd);
ASSERT_EQ(ftruncate(fd.get(), 1024 * 1024), 0);
}
TEST_P(DeviceTest, TestValidDiskFormat) {
ASSERT_TRUE(fs().Unmount().is_ok());
fbl::unique_fd device_fd(open(fs().DevicePath()->c_str(), O_RDWR));
ASSERT_EQ(fs_management::DetectDiskFormat(device_fd.get()), fs_management::kDiskFormatFxfs);
}
TEST_P(DeviceTest, TestWriteThenRead) {
const std::string kFilename = GetPath("block_device");
{ CreateFxFile(kFilename); }
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Node>();
ASSERT_EQ(endpoints.status_value(), ZX_OK);
auto [client, server] = *std::move(endpoints);
// Re-open file as block device
fdio_cpp::FdioCaller caller(fs().GetRootFd());
ASSERT_EQ(fidl::WireCall(caller.directory())
->Open(fuchsia_io::wire::OpenFlags::kRightReadable |
fuchsia_io::wire::OpenFlags::kRightWritable,
fuchsia_io::wire::kModeTypeBlockDevice, "block_device", std::move(server))
.status(),
ZX_OK);
std::unique_ptr<block_client::RemoteBlockDevice> device;
ASSERT_EQ(block_client::RemoteBlockDevice::Create(client.TakeChannel(), &device), ZX_OK);
zx::vmo vmo;
storage::OwnedVmoid vmoid;
const size_t kVmoBlocks = 5;
fuchsia_hardware_block_BlockInfo info = {};
ASSERT_EQ(device->BlockGetInfo(&info), ZX_OK);
ASSERT_EQ(zx::vmo::create(kVmoBlocks * info.block_size, 0, &vmo), ZX_OK);
ASSERT_NO_FATAL_FAILURE(device->BlockAttachVmo(vmo, &vmoid.GetReference(device.get())));
const size_t kVmoWriteBlocks = 2;
const size_t kVmoBlockOffset = 1;
ASSERT_LE(kVmoBlockOffset + kVmoWriteBlocks, kVmoBlocks);
char write_buf[kVmoWriteBlocks * info.block_size];
memset(write_buf, 0xa3, sizeof(write_buf));
ASSERT_EQ(vmo.write(write_buf, kVmoBlockOffset * info.block_size, sizeof(write_buf)), ZX_OK);
block_fifo_request_t write_request;
write_request.opcode = BLOCKIO_WRITE;
write_request.vmoid = vmoid.get();
write_request.length = kVmoWriteBlocks;
write_request.vmo_offset = kVmoBlockOffset;
write_request.dev_offset = 0;
EXPECT_EQ(device->FifoTransaction(&write_request, 1), ZX_OK);
// "Clear" vmo, so any data in the vmo after is solely dependent on the following BLOCKIO_READ
char zero_buf[kVmoBlocks * info.block_size];
memset(zero_buf, 0, sizeof(zero_buf));
ASSERT_EQ(vmo.write(zero_buf, 0, sizeof(zero_buf)), ZX_OK);
char read_buf[kVmoWriteBlocks * info.block_size];
memset(read_buf, 0, sizeof(read_buf));
block_fifo_request_t read_request;
read_request.opcode = BLOCKIO_READ;
read_request.vmoid = vmoid.get();
read_request.length = kVmoWriteBlocks;
read_request.vmo_offset = kVmoBlockOffset;
read_request.dev_offset = 0;
ASSERT_EQ(device->FifoTransaction(&read_request, 1), ZX_OK);
ASSERT_EQ(vmo.read(read_buf, kVmoBlockOffset * info.block_size, sizeof(read_buf)), ZX_OK);
EXPECT_EQ(memcmp(write_buf, read_buf, sizeof(write_buf)), 0);
}
// Tests multiple reads and writes in a group
TEST_P(DeviceTest, TestGroupWritesThenReads) {
const std::string kFilename = GetPath("block_device");
{ CreateFxFile(kFilename); }
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Node>();
ASSERT_EQ(endpoints.status_value(), ZX_OK);
auto [client, server] = *std::move(endpoints);
fdio_cpp::FdioCaller caller(fs().GetRootFd());
ASSERT_EQ(fidl::WireCall(caller.directory())
->Open(fuchsia_io::wire::OpenFlags::kRightReadable |
fuchsia_io::wire::OpenFlags::kRightWritable,
fuchsia_io::wire::kModeTypeBlockDevice, "block_device", std::move(server))
.status(),
ZX_OK);
std::unique_ptr<block_client::RemoteBlockDevice> device;
ASSERT_EQ(block_client::RemoteBlockDevice::Create(client.TakeChannel(), &device), ZX_OK);
const size_t kVmoBlocks = 6;
zx::vmo vmo;
storage::OwnedVmoid vmoid;
fuchsia_hardware_block_BlockInfo info = {};
ASSERT_EQ(device->BlockGetInfo(&info), ZX_OK);
ASSERT_EQ(zx::vmo::create(kVmoBlocks * info.block_size, 0, &vmo), ZX_OK);
ASSERT_NO_FATAL_FAILURE(device->BlockAttachVmo(vmo, &vmoid.GetReference(device.get())));
// The first group of writes will send 2 write requests, with a buffer size of kVmoWriteBlocks *
// block_size This test will write and read from vmo and device with an offset to test that read
// and writes work with offsets
const size_t kVmoWriteBlocks = 2;
const size_t kOffsetBlocks = 1;
ASSERT_LE(kOffsetBlocks + 2 * kVmoWriteBlocks, kVmoBlocks);
// Write write_buf1 and write_buf2 to vmo with offset = kOffsetBlocks
char write_buf1[kVmoWriteBlocks * info.block_size];
memset(write_buf1, 0xa3, sizeof(write_buf1));
ASSERT_EQ(vmo.write(write_buf1, kOffsetBlocks * info.block_size, sizeof(write_buf1)), ZX_OK);
char write_buf2[kVmoWriteBlocks * info.block_size];
memset(write_buf2, 0xf7, sizeof(write_buf2));
ASSERT_EQ(vmo.write(write_buf2, (kOffsetBlocks + kVmoWriteBlocks) * info.block_size,
sizeof(write_buf2)),
ZX_OK);
block_fifo_request_t write_requests[2];
write_requests[0].opcode = BLOCKIO_WRITE;
write_requests[0].vmoid = vmoid.get();
write_requests[0].length = kVmoWriteBlocks;
write_requests[0].vmo_offset = kOffsetBlocks;
write_requests[0].dev_offset = 0;
write_requests[1].opcode = BLOCKIO_WRITE;
write_requests[1].vmoid = vmoid.get();
write_requests[1].length = kVmoWriteBlocks;
write_requests[1].vmo_offset = kOffsetBlocks + kVmoWriteBlocks;
write_requests[1].dev_offset = kVmoWriteBlocks;
EXPECT_EQ(device->FifoTransaction(write_requests, std::size(write_requests)), ZX_OK);
char read_buf[kVmoBlocks * info.block_size];
memset(read_buf, 0, sizeof(read_buf));
ASSERT_EQ(vmo.write(read_buf, 0, sizeof(read_buf)), ZX_OK);
block_fifo_request_t read_requests[2];
read_requests[0].opcode = BLOCKIO_READ;
read_requests[0].vmoid = vmoid.get();
read_requests[0].length = kVmoWriteBlocks;
read_requests[0].vmo_offset = kOffsetBlocks;
read_requests[0].dev_offset = 0;
read_requests[1].opcode = BLOCKIO_READ;
read_requests[1].vmoid = vmoid.get();
read_requests[1].length = kVmoWriteBlocks;
read_requests[1].vmo_offset = kOffsetBlocks + kVmoWriteBlocks;
read_requests[1].dev_offset = kVmoWriteBlocks;
ASSERT_EQ(device->FifoTransaction(read_requests, std::size(read_requests)), ZX_OK);
ASSERT_EQ(vmo.read(read_buf, 0, sizeof(read_buf)), ZX_OK);
EXPECT_EQ(memcmp(write_buf1, read_buf + (kOffsetBlocks * info.block_size), sizeof(write_buf1)),
0);
EXPECT_EQ(memcmp(write_buf2, read_buf + ((kOffsetBlocks + kVmoWriteBlocks) * info.block_size),
sizeof(write_buf2)),
0);
}
TEST_P(DeviceTest, TestWriteThenFlushThenRead) {
const std::string kFilename = GetPath("block_device");
{ CreateFxFile(kFilename); }
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Node>();
ASSERT_EQ(endpoints.status_value(), ZX_OK);
auto [client, server] = *std::move(endpoints);
fdio_cpp::FdioCaller caller(fs().GetRootFd());
ASSERT_EQ(fidl::WireCall(caller.directory())
->Open(fuchsia_io::wire::OpenFlags::kRightReadable |
fuchsia_io::wire::OpenFlags::kRightWritable,
fuchsia_io::wire::kModeTypeBlockDevice, "block_device", std::move(server))
.status(),
ZX_OK);
std::unique_ptr<block_client::RemoteBlockDevice> device;
ASSERT_EQ(block_client::RemoteBlockDevice::Create(client.TakeChannel(), &device), ZX_OK);
const size_t kVmoBlocks = 2;
zx::vmo vmo;
storage::OwnedVmoid vmoid;
fuchsia_hardware_block_BlockInfo info = {};
ASSERT_EQ(device->BlockGetInfo(&info), ZX_OK);
ASSERT_EQ(zx::vmo::create(kVmoBlocks * info.block_size, 0, &vmo), ZX_OK);
ASSERT_NO_FATAL_FAILURE(device->BlockAttachVmo(vmo, &vmoid.GetReference(device.get())));
char write_buf[kVmoBlocks * info.block_size];
memset(write_buf, 0xa3, sizeof(write_buf));
ASSERT_EQ(vmo.write(write_buf, 0, sizeof(write_buf)), ZX_OK);
block_fifo_request_t requests[2];
requests[0].opcode = BLOCKIO_WRITE;
requests[0].vmoid = vmoid.get();
requests[0].length = kVmoBlocks;
requests[0].vmo_offset = 0;
requests[0].dev_offset = 0;
requests[1].opcode = BLOCKIO_FLUSH;
requests[1].vmoid = vmoid.get();
requests[1].length = 0;
requests[1].vmo_offset = 0;
requests[1].dev_offset = 0;
EXPECT_EQ(device->FifoTransaction(requests, std::size(requests)), ZX_OK);
char read_buf[kVmoBlocks * info.block_size];
memset(read_buf, 0, sizeof(read_buf));
ASSERT_EQ(vmo.write(read_buf, 0, sizeof(read_buf)), ZX_OK);
block_fifo_request_t read_request;
read_request.opcode = BLOCKIO_READ;
read_request.vmoid = vmoid.get();
read_request.length = kVmoBlocks;
read_request.vmo_offset = 0;
read_request.dev_offset = 0;
ASSERT_EQ(device->FifoTransaction(&read_request, 1), ZX_OK);
ASSERT_EQ(vmo.read(read_buf, 0, sizeof(read_buf)), ZX_OK);
EXPECT_EQ(memcmp(write_buf, read_buf, sizeof(write_buf)), 0);
}
TEST_P(DeviceTest, TestInvalidGroupRequests) {
const std::string kFilename = GetPath("block_device");
{ CreateFxFile(kFilename); }
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Node>();
ASSERT_EQ(endpoints.status_value(), ZX_OK);
auto [client, server] = *std::move(endpoints);
fdio_cpp::FdioCaller caller(fs().GetRootFd());
ASSERT_EQ(fidl::WireCall(caller.directory())
->Open(fuchsia_io::wire::OpenFlags::kRightReadable |
fuchsia_io::wire::OpenFlags::kRightWritable,
fuchsia_io::wire::kModeTypeBlockDevice, "block_device", std::move(server))
.status(),
ZX_OK);
std::unique_ptr<block_client::RemoteBlockDevice> device;
ASSERT_EQ(block_client::RemoteBlockDevice::Create(client.TakeChannel(), &device), ZX_OK);
const size_t kVmoBlocks = 5;
zx::vmo vmo;
storage::OwnedVmoid vmoid;
fuchsia_hardware_block_BlockInfo info = {};
ASSERT_EQ(device->BlockGetInfo(&info), ZX_OK);
ASSERT_EQ(zx::vmo::create(kVmoBlocks * info.block_size, 0, &vmo), ZX_OK);
ASSERT_NO_FATAL_FAILURE(device->BlockAttachVmo(vmo, &vmoid.GetReference(device.get())));
block_fifo_request_t requests[3];
requests[0].opcode = BLOCKIO_FLUSH;
requests[0].vmoid = vmoid.get();
requests[0].length = 0;
requests[0].vmo_offset = 0;
requests[0].dev_offset = 0;
// Not a valid request
requests[1].opcode = BLOCKIO_CLOSE_VMO;
requests[1].vmoid = 100;
requests[1].length = 0;
requests[1].vmo_offset = 0;
requests[1].dev_offset = 0;
requests[2].opcode = BLOCKIO_FLUSH;
requests[2].vmoid = vmoid.get();
requests[2].length = 0;
requests[2].vmo_offset = 0;
requests[2].dev_offset = 0;
EXPECT_NE(device->FifoTransaction(requests, std::size(requests)), ZX_OK);
}
INSTANTIATE_TEST_SUITE_P(/*no prefix*/, DeviceTest, testing::ValuesIn(AllTestFilesystems()),
testing::PrintToStringParamName());
} // namespace
} // namespace fs_test