| // 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); |
| } |