blob: fb1173e1a4b27de06978bf9863c6bff38dd4ef51 [file] [log] [blame]
// Copyright 2018 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 <fidl/fuchsia.io/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/fd.h>
#include <lib/sync/completion.h>
#include <unistd.h>
#include <future>
#include <utility>
#include <fbl/unique_fd.h>
#include <zxtest/zxtest.h>
#include "src/storage/memfs/memfs.h"
#include "src/storage/memfs/vnode_dir.h"
namespace fio = fuchsia_io;
namespace {
void TryFilesystemOperations(fidl::UnownedClientEnd<fio::File> client_end) {
constexpr std::string_view payload("foobar");
const fidl::WireResult write_result =
fidl::WireCall(client_end)
->WriteAt(
fidl::VectorView<uint8_t>::FromExternal(
reinterpret_cast<uint8_t*>(const_cast<char*>(payload.data())), payload.size()),
0);
ASSERT_OK(write_result);
const fit::result write_response = write_result.value();
ASSERT_TRUE(write_response.is_ok(), "%s", zx_status_get_string(write_response.error_value()));
ASSERT_EQ(write_response.value()->actual_count, payload.size());
const fidl::WireResult read_result = fidl::WireCall(client_end)->ReadAt(256, 0);
ASSERT_OK(read_result);
const fit::result read_response = read_result.value();
ASSERT_TRUE(read_response.is_ok(), "%s", zx_status_get_string(read_response.error_value()));
const fidl::VectorView data = read_result->value()->data;
ASSERT_EQ(std::string_view(reinterpret_cast<const char*>(data.data()), data.count()), payload);
}
void TryFilesystemOperations(const zx::unowned_channel& channel) {
TryFilesystemOperations(fidl::UnownedClientEnd<fio::File>(channel));
}
void TryFilesystemOperations(const zx::channel& channel) {
TryFilesystemOperations(channel.borrow());
}
void TryFilesystemOperations(const fdio_cpp::FdioCaller& caller) {
TryFilesystemOperations(caller.channel());
}
void TryFilesystemOperations(const fdio_cpp::UnownedFdioCaller& caller) {
TryFilesystemOperations(caller.channel());
}
class FdioCallerTest : public zxtest::Test {
protected:
void SetUp() override {
zx::result result = memfs::Memfs::Create(loop_.dispatcher(), "<tmp>");
ASSERT_OK(result);
auto& [memfs, root] = result.value();
auto [client, server] = fidl::Endpoints<fio::Directory>::Create();
ASSERT_OK(memfs->ServeDirectory(std::move(root), std::move(server)));
memfs_ = std::move(memfs);
zx::channel ch0, ch1;
ASSERT_OK(zx::channel::create(0, &ch0, &ch1));
ASSERT_OK(fidl::WireCall(client)->Open(
fio::OpenFlags::kCreate | fio::OpenFlags::kRightReadable | fio::OpenFlags::kRightWritable,
{}, "my-file", fidl::ServerEnd<fio::Node>{std::move(ch0)}));
ASSERT_OK(loop_.StartThread());
ASSERT_OK(fdio_fd_create(ch1.release(), fd_.reset_and_get_address()));
}
void TearDown() override {
std::promise<zx_status_t> promise;
memfs_->Shutdown([&promise](zx_status_t status) { promise.set_value(status); });
ASSERT_OK(promise.get_future().get());
}
fbl::unique_fd& fd() { return fd_; }
private:
async::Loop loop_ = async::Loop(&kAsyncLoopConfigNoAttachToCurrentThread);
std::unique_ptr<memfs::Memfs> memfs_;
fbl::unique_fd fd_;
};
TEST_F(FdioCallerTest, FdioCallerFile) {
// Try some filesystem operations.
fdio_cpp::FdioCaller caller(std::move(fd()));
ASSERT_TRUE(caller);
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller));
// Re-acquire the underlying fd.
fbl::unique_fd fd = caller.release();
ASSERT_EQ(close(fd.release()), 0);
}
TEST_F(FdioCallerTest, FdioCallerMoveAssignment) {
fdio_cpp::FdioCaller caller(std::move(fd()));
fdio_cpp::FdioCaller move_assignment_caller = std::move(caller);
ASSERT_TRUE(move_assignment_caller);
ASSERT_FALSE(caller);
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(move_assignment_caller));
}
TEST_F(FdioCallerTest, FdioCallerMoveConstructor) {
fdio_cpp::FdioCaller caller(std::move(fd()));
fdio_cpp::FdioCaller move_ctor_caller(std::move(caller));
ASSERT_TRUE(move_ctor_caller);
ASSERT_FALSE(caller);
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(move_ctor_caller));
}
TEST_F(FdioCallerTest, FdioCallerBorrow) {
fdio_cpp::FdioCaller caller(std::move(fd()));
zx::unowned_channel channel = caller.channel();
ASSERT_TRUE(channel->is_valid());
ASSERT_TRUE(caller);
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(channel));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.node().channel()));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.file().channel()));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.directory().channel()));
}
TEST_F(FdioCallerTest, FdioCallerClone) {
fdio_cpp::FdioCaller caller(std::move(fd()));
auto channel = caller.clone_channel();
ASSERT_OK(channel.status_value());
ASSERT_TRUE(channel->is_valid());
ASSERT_TRUE(caller);
ASSERT_NE(caller.channel()->get(), channel->get());
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(channel->borrow()));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.clone_node()->channel()));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.clone_file()->channel()));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.clone_directory()->channel()));
}
TEST_F(FdioCallerTest, FdioCallerTake) {
fdio_cpp::FdioCaller caller(std::move(fd()));
auto channel = caller.take_channel();
ASSERT_OK(channel.status_value());
ASSERT_TRUE(channel->is_valid());
ASSERT_FALSE(caller);
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(channel->borrow()));
}
TEST_F(FdioCallerTest, FdioCallerTakeAs) {
fdio_cpp::FdioCaller caller(std::move(fd()));
auto channel = caller.take_as<fio::File>();
ASSERT_OK(channel.status_value());
ASSERT_TRUE(channel->is_valid());
ASSERT_FALSE(caller);
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(channel->borrow()));
}
TEST_F(FdioCallerTest, UnownedFdioCaller) {
fdio_cpp::UnownedFdioCaller caller(fd());
ASSERT_TRUE(caller);
ASSERT_TRUE(fd());
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller));
}
TEST_F(FdioCallerTest, UnownedFdioCallerBorrow) {
fdio_cpp::UnownedFdioCaller caller(fd());
zx::unowned_channel channel = caller.channel();
ASSERT_TRUE(channel->is_valid());
ASSERT_TRUE(caller);
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(channel));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.node().channel()));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.file().channel()));
ASSERT_NO_FATAL_FAILURE(TryFilesystemOperations(caller.directory().channel()));
}
} // namespace