blob: bc3c2bf22783fc1750a2714715ff2a622bc02595 [file] [log] [blame]
// Copyright 2024 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 <fidl/fuchsia.io.test/cpp/fidl.h>
#include <fidl/fuchsia.io/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/dispatcher.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/result.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <atomic>
#include <cstdlib>
#include <memory>
#include <fbl/ref_ptr.h>
#include "src/lib/fxl/strings/string_printf.h"
#include "src/storage/lib/vfs/cpp/vfs_types.h"
#include "src/storage/memfs/memfs.h"
#include "src/storage/memfs/vnode_dir.h"
#include "src/storage/memfs/vnode_file.h"
namespace fio = fuchsia_io;
namespace fio_test = fuchsia_io_test;
void AddEntry(const fio_test::DirectoryEntry& entry, memfs::VnodeDir& dir) {
switch (entry.Which()) {
case fio_test::DirectoryEntry::Tag::kDirectory: {
zx::result node = dir.Create(entry.directory()->name(), fs::CreationType::kDirectory);
ZX_ASSERT_MSG(node.is_ok(), "Failed to create directory: %s", node.status_string());
auto sub_dir = fbl::RefPtr<memfs::VnodeDir>::Downcast(*std::move(node));
for (const auto& entry : entry.directory()->entries()) {
AddEntry(*entry, *sub_dir);
}
break;
}
case fio_test::DirectoryEntry::Tag::kFile: {
zx::result node = dir.Create(entry.file()->name(), fs::CreationType::kFile);
ZX_ASSERT_MSG(node.is_ok(), "Failed to create file: %s", node.status_string());
auto file = fbl::RefPtr<memfs::VnodeFile>::Downcast(*std::move(node));
const auto& contents = entry.file()->contents();
if (!contents.empty()) {
zx::result<zx::stream> stream = file->CreateStream(ZX_STREAM_MODE_WRITE);
ZX_ASSERT(stream.is_ok());
size_t actual;
zx_iovec_t iovec = {
.buffer = const_cast<uint8_t*>(contents.data()),
.capacity = contents.size(),
};
ZX_ASSERT(stream->writev(0, &iovec, 1, &actual) == ZX_OK);
ZX_ASSERT(actual == contents.size());
}
break;
}
case fio_test::DirectoryEntry::Tag::kRemoteDirectory:
ZX_PANIC("Remote directories are not supported");
break;
case fio_test::DirectoryEntry::Tag::kExecutableFile:
ZX_PANIC("Executable files are not supported");
break;
}
}
class TestHarness : public fidl::Server<fio_test::Io1Harness> {
public:
explicit TestHarness(std::unique_ptr<memfs::Memfs> memfs, fbl::RefPtr<memfs::VnodeDir> root)
: memfs_(std::move(memfs)), root_(std::move(root)) {}
void GetConfig(GetConfigCompleter::Sync& completer) final {
fio_test::Io1Config config;
// Supported options
config.supports_open2(true);
config.supports_get_backing_memory(true);
config.supports_get_token(true);
config.supports_append(true);
config.supports_modify_directory(true);
config.supported_attributes(
fio::NodeAttributesQuery::kCreationTime | fio::NodeAttributesQuery::kModificationTime |
fio::NodeAttributesQuery::kContentSize | fio::NodeAttributesQuery::kStorageSize |
fio::NodeAttributesQuery::kId | fio::NodeAttributesQuery::kLinkCount |
fio::NodeAttributesQuery::kMode | fio::NodeAttributesQuery::kUid |
fio::NodeAttributesQuery::kGid);
completer.Reply(config);
}
void GetDirectory(GetDirectoryRequest& request, GetDirectoryCompleter::Sync& completer) final {
uint64_t test_id = test_counter_.fetch_add(1);
std::string directory_name = fxl::StringPrintf("test.%ld", test_id);
zx::result test_root = root_->Create(directory_name, fs::CreationType::kDirectory);
ZX_ASSERT_MSG(test_root.is_ok(), "Failed to create test root: %s", test_root.status_string());
auto root_dir = fbl::RefPtr<memfs::VnodeDir>::Downcast(*std::move(test_root));
for (auto& entry : request.root().entries()) {
AddEntry(*entry, *root_dir);
}
zx::result options = fs::VnodeConnectionOptions::FromOpen1Flags(request.flags());
ZX_ASSERT_MSG(options.is_ok(), "Failed to validate flags: %s", options.status_string());
zx_status_t status =
memfs_->Serve(root_dir, request.directory_request().TakeChannel(), *options);
ZX_ASSERT_MSG(status == ZX_OK, "Failed to serve directory: %s", zx_status_get_string(status));
}
private:
std::unique_ptr<memfs::Memfs> memfs_;
fbl::RefPtr<memfs::VnodeDir> root_;
std::atomic_uint64_t test_counter_ = 0;
};
int main(int argc, const char** argv) {
fuchsia_logging::SetTags({"io_conformance_harness_memfs"});
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
async_dispatcher_t* dispatcher = loop.dispatcher();
component::OutgoingDirectory outgoing = component::OutgoingDirectory(dispatcher);
zx::result result = outgoing.ServeFromStartupInfo();
if (result.is_error()) {
FX_LOGS(ERROR) << "Failed to serve outgoing directory: " << result.status_string();
return EXIT_FAILURE;
}
auto memfs = memfs::Memfs::Create(dispatcher, "memfs");
if (memfs.is_error()) {
FX_LOGS(ERROR) << "Failed to create memfs: " << memfs.status_string();
return EXIT_FAILURE;
}
result = outgoing.AddProtocol<fio_test::Io1Harness>(
std::make_unique<TestHarness>(std::move(memfs->first), std::move(memfs->second)));
if (result.is_error()) {
FX_LOGS(ERROR) << "Failed to server test harness: " << result.status_string();
return EXIT_FAILURE;
}
loop.Run();
return EXIT_SUCCESS;
}