blob: b29db85d8ed685717c946c0906b0fe1f181fbbae [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 <fuchsia/io/llcpp/fidl.h>
#include <lib/fdio/directory.h>
#include <fs-management/admin.h>
#include <fs-management/format.h>
#include <ramdevice-client/ramdisk.h>
#include <zxtest/zxtest.h>
namespace fio = ::llcpp::fuchsia::io;
enum State {
kEmpty,
kFormatted,
kStarted,
};
class OutgoingDirectoryTest : public zxtest::Test {
public:
explicit OutgoingDirectoryTest(disk_format_t format) : format_(format) {}
void SetUp() final {
ASSERT_OK(ramdisk_create(512, 1 << 16, &ramdisk_));
const char* ramdisk_path = ramdisk_get_path(ramdisk_);
ASSERT_OK(mkfs(ramdisk_path, format_, launch_stdio_sync, &default_mkfs_options));
state_ = kFormatted;
}
void TearDown() final {
if (state_ == kStarted) {
StopFilesystem();
}
ASSERT_EQ(ramdisk_destroy(ramdisk_), 0);
}
protected:
void GetExportRoot(zx::unowned_channel* root) {
ASSERT_EQ(state_, kStarted);
*root = zx::unowned(export_root_);
}
void GetDataRoot(zx::channel* root) {
ASSERT_EQ(state_, kStarted);
ASSERT_OK(fs_root_handle(export_root_.get(), root->reset_and_get_address()));
}
void CheckDataRoot() {
const char* format_str = disk_format_string(format_);
zx::channel data_root;
GetDataRoot(&data_root);
fio::DirectoryAdmin::SyncClient data_client(std::move(data_root));
auto resp = data_client.QueryFilesystem();
ASSERT_TRUE(resp.ok());
ASSERT_OK(resp.value().s);
ASSERT_EQ(strncmp(format_str, reinterpret_cast<char*>(resp.value().info->name.data()),
strlen(format_str)),
0);
}
void StartFilesystem(const init_options_t* options) {
ASSERT_EQ(state_, kFormatted);
zx::channel device, device_server;
const char* ramdisk_path = ramdisk_get_path(ramdisk_);
ASSERT_OK(zx::channel::create(0, &device, &device_server));
ASSERT_OK(fdio_service_connect(ramdisk_path, device_server.release()));
ASSERT_OK(fs_init(device.release(), format_, options, export_root_.reset_and_get_address()));
state_ = kStarted;
}
void StopFilesystem() {
ASSERT_EQ(state_, kStarted);
zx::channel data_root;
GetDataRoot(&data_root);
fio::DirectoryAdmin::SyncClient data_client(std::move(data_root));
auto resp = data_client.Unmount();
ASSERT_TRUE(resp.ok());
ASSERT_OK(resp.value().s);
state_ = kFormatted;
}
void WriteTestFile() {
ASSERT_EQ(state_, kStarted);
zx::channel data_root;
GetDataRoot(&data_root);
fio::Directory::SyncClient data_client(std::move(data_root));
zx::channel test_file, test_file_server;
ASSERT_OK(zx::channel::create(0, &test_file, &test_file_server));
uint32_t file_flags =
fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_WRITABLE | fio::OPEN_FLAG_CREATE;
ASSERT_OK(data_client.Open(file_flags, 0, "test_file", std::move(test_file_server)).status());
fio::File::SyncClient file_client(std::move(test_file));
std::vector<uint8_t> content{1, 2, 3, 4};
auto resp = file_client.Write(fidl::unowned_vec(content));
ASSERT_OK(resp.status());
ASSERT_OK(resp.value().s);
ASSERT_EQ(resp.value().actual, content.size());
auto resp2 = file_client.Close();
ASSERT_OK(resp2.status());
ASSERT_OK(resp2.value().s);
}
private:
State state_ = kEmpty;
ramdisk_client_t* ramdisk_ = nullptr;
zx::channel export_root_;
disk_format_t format_;
};
static constexpr init_options_t readonly_options = {
.readonly = true,
.verbose_mount = false,
.collect_metrics = false,
.wait_until_ready = true,
.write_compression_algorithm = nullptr,
.write_compression_level = -1,
.callback = launch_stdio_async,
};
class OutgoingDirectoryBlobfs : public OutgoingDirectoryTest {
public:
OutgoingDirectoryBlobfs() : OutgoingDirectoryTest(DISK_FORMAT_BLOBFS) {}
};
class OutgoingDirectoryMinfs : public OutgoingDirectoryTest {
public:
OutgoingDirectoryMinfs() : OutgoingDirectoryTest(DISK_FORMAT_MINFS) {}
};
TEST_F(OutgoingDirectoryBlobfs, OutgoingDirectoryReadWriteDataRootIsValidBlobfs) {
StartFilesystem(&default_init_options);
CheckDataRoot();
}
TEST_F(OutgoingDirectoryBlobfs, OutgoingDirectoryReadOnlyDataRootIsValidBlobfs) {
StartFilesystem(&readonly_options);
CheckDataRoot();
}
// TODO(http://fxbug.dev/60818): Re-enable the test
TEST_F(OutgoingDirectoryBlobfs, DISABLED_RegisterOutgoingDirectoryWithFSHostRegistry) {
StartFilesystem(&default_init_options);
zx::unowned_channel export_root;
GetExportRoot(&export_root);
ASSERT_OK(fs_register(export_root->get()));
}
TEST_F(OutgoingDirectoryMinfs, OutgoingDirectoryReadWriteDataRootIsValidMinfs) {
StartFilesystem(&default_init_options);
CheckDataRoot();
}
TEST_F(OutgoingDirectoryMinfs, OutgoingDirectoryReadOnlyDataRootIsValidMinfs) {
StartFilesystem(&readonly_options);
CheckDataRoot();
}
TEST_F(OutgoingDirectoryMinfs, CanWriteToReadWriteMinfsDataRoot) {
StartFilesystem(&default_init_options);
WriteTestFile();
}
TEST_F(OutgoingDirectoryMinfs, CannotWriteToReadOnlyMinfsDataRoot) {
// write an initial test file onto a writable filesystem
StartFilesystem(&default_init_options);
WriteTestFile();
StopFilesystem();
// start the filesystem in read-only mode
StartFilesystem(&readonly_options);
zx::channel data_root;
GetDataRoot(&data_root);
fio::Directory::SyncClient data_client(std::move(data_root));
zx::channel fail_test_file, fail_test_file_server;
ASSERT_OK(zx::channel::create(0, &fail_test_file, &fail_test_file_server));
uint32_t fail_file_flags = fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_WRITABLE;
// open "succeeds" but...
ASSERT_OK(
data_client.Open(fail_file_flags, 0, "test_file", std::move(fail_test_file_server)).status());
// ...we can't actually use the channel
fio::File::SyncClient fail_file_client(std::move(fail_test_file));
auto resp = fail_file_client.Read(4);
ASSERT_EQ(resp.status(), ZX_ERR_PEER_CLOSED);
// the channel will be valid if we open the file read-only though
zx::channel test_file, test_file_server;
ASSERT_OK(zx::channel::create(0, &test_file, &test_file_server));
uint32_t file_flags = fio::OPEN_RIGHT_READABLE;
ASSERT_OK(data_client.Open(file_flags, 0, "test_file", std::move(test_file_server)).status());
fio::File::SyncClient file_client(std::move(test_file));
auto resp2 = file_client.Read(4);
ASSERT_OK(resp2.status());
ASSERT_OK(resp2.value().s);
ASSERT_EQ(resp2.value().data.data()[0], 1);
auto resp3 = file_client.Close();
ASSERT_OK(resp3.status());
ASSERT_OK(resp3.value().s);
}
TEST_F(OutgoingDirectoryMinfs, CannotWriteToOutgoingDirectory) {
StartFilesystem(&default_init_options);
zx::unowned_channel export_root;
GetExportRoot(&export_root);
auto test_file_name = std::string("test_file");
zx::channel test_file, test_file_server;
ASSERT_OK(zx::channel::create(0, &test_file, &test_file_server));
uint32_t file_flags = fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_WRITABLE | fio::OPEN_FLAG_CREATE;
ASSERT_OK(fio::Directory::Call::Open(std::move(export_root), file_flags, 0,
fidl::unowned_str(test_file_name),
std::move(test_file_server))
.status());
fio::File::SyncClient file_client(std::move(test_file));
std::vector<uint8_t> content{1, 2, 3, 4};
auto resp = file_client.Write(fidl::unowned_vec(content));
ASSERT_EQ(resp.status(), ZX_ERR_PEER_CLOSED);
}