blob: 4c92a49838e0423351462496f95415aea7fa175d [file] [log] [blame]
// Copyright 2020 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/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 <cstdlib>
#include <memory>
#include <fbl/ref_ptr.h>
#include "src/storage/lib/vfs/cpp/managed_vfs.h"
#include "src/storage/lib/vfs/cpp/pseudo_dir.h"
#include "src/storage/lib/vfs/cpp/remote_dir.h"
#include "src/storage/lib/vfs/cpp/vfs_types.h"
#include "src/storage/lib/vfs/cpp/vmo_file.h"
namespace fio = fuchsia_io;
namespace fio_test = fuchsia_io_test;
class TestHarness : public fidl::Server<fio_test::Io1Harness> {
public:
explicit TestHarness() : vfs_loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {
vfs_loop_.StartThread("vfs_thread");
vfs_ = std::make_unique<fs::ManagedVfs>(vfs_loop_.dispatcher());
}
~TestHarness() override {
// |fs::ManagedVfs| must be shutdown first before stopping its dispatch loop.
// Here we asynchronously post the shutdown request, then synchronously join
// the |vfs_loop_| thread.
vfs_->Shutdown([this](zx_status_t status) mutable {
async::PostTask(vfs_loop_.dispatcher(), [this] {
vfs_.reset();
vfs_loop_.Quit();
});
});
vfs_loop_.JoinThreads();
}
void GetConfig(GetConfigCompleter::Sync& completer) final {
fio_test::Io1Config config;
// Supported options
config.supports_get_backing_memory(true);
config.supports_remote_dir(true);
config.supports_get_token(true);
config.supports_open2(true);
config.supported_attributes(fio::NodeAttributesQuery::kContentSize |
fio::NodeAttributesQuery::kStorageSize);
// TODO(https://fxbug.dev/324112857): Support append mode when adding open2 support.
completer.Reply(config);
}
void GetDirectory(GetDirectoryRequest& request, GetDirectoryCompleter::Sync& completer) final {
fbl::RefPtr<fs::PseudoDir> dir{fbl::MakeRefCounted<fs::PseudoDir>()};
for (auto& entry : request.root().entries()) {
AddEntry(std::move(*entry), *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 =
vfs_->Serve(std::move(dir), request.directory_request().TakeChannel(), *options);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Serving directory failed: " << zx_status_get_string(status);
return;
}
}
void AddEntry(fio_test::DirectoryEntry entry, fs::PseudoDir& dest) {
switch (entry.Which()) {
case fio_test::DirectoryEntry::Tag::kDirectory: {
fio_test::Directory& directory = entry.directory().value();
// TODO(https://fxbug.dev/42109125): Set the correct flags on this directory.
auto dir_entry = fbl::MakeRefCounted<fs::PseudoDir>();
for (auto& child : directory.entries()) {
AddEntry(std::move(*child), *dir_entry);
}
dest.AddEntry(directory.name(), dir_entry);
break;
}
case fio_test::DirectoryEntry::Tag::kRemoteDirectory: {
fio_test::RemoteDirectory& remote_dir = entry.remote_directory().value();
auto remote_entry =
fbl::MakeRefCounted<fs::RemoteDir>(std::move(remote_dir.remote_client()));
dest.AddEntry(remote_dir.name(), std::move(remote_entry));
break;
}
case fio_test::DirectoryEntry::Tag::kFile: {
fio_test::File& file = entry.file().value();
zx::vmo vmo;
zx_status_t status = zx::vmo::create(file.contents().size(), {}, &vmo);
ZX_ASSERT_MSG(status == ZX_OK, "Failed to create VMO: %s", zx_status_get_string(status));
if (!file.contents().empty()) {
status = vmo.write(file.contents().data(), 0, file.contents().size());
ZX_ASSERT_MSG(status == ZX_OK, "Failed to write to VMO: %s",
zx_status_get_string(status));
}
dest.AddEntry(file.name(),
fbl::MakeRefCounted<fs::VmoFile>(std::move(vmo), file.contents().size(),
/*writable=*/true));
break;
}
case fio_test::DirectoryEntry::Tag::kExecutableFile:
ZX_PANIC("Executable files are not supported");
break;
}
}
private:
std::unique_ptr<fs::ManagedVfs> vfs_;
async::Loop vfs_loop_;
};
int main(int argc, const char** argv) {
fuchsia_logging::SetTags({"io_conformance_harness_cppvfs"});
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
component::OutgoingDirectory outgoing = component::OutgoingDirectory(loop.dispatcher());
zx::result result = outgoing.ServeFromStartupInfo();
if (result.is_error()) {
FX_LOGS(ERROR) << "Failed to serve outgoing directory: " << result.status_string();
return EXIT_FAILURE;
}
result = outgoing.AddProtocol<fio_test::Io1Harness>(std::make_unique<TestHarness>());
if (result.is_error()) {
FX_LOGS(ERROR) << "Failed to server test harness: " << result.status_string();
return EXIT_FAILURE;
}
return loop.Run();
}