blob: 7770a90f2fd847c879934cda140dcb86908fbd78 [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 <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
#include <sys/stat.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include <fbl/ref_ptr.h>
#include "fuchsia/io/cpp/fidl.h"
#include "fuchsia/io/test/cpp/fidl.h"
#include "src/storage/lib/block_client/cpp/fake_block_device.h"
#include "src/storage/lib/vfs/cpp/pseudo_dir.h"
#include "src/storage/lib/vfs/cpp/vfs_types.h"
#include "src/storage/minfs/bcache.h"
#include "src/storage/minfs/directory.h"
#include "src/storage/minfs/format.h"
#include "src/storage/minfs/minfs.h"
#include "src/storage/minfs/minfs_private.h"
#include "src/storage/minfs/runner.h"
#include "src/storage/minfs/vnode.h"
namespace minfs {
constexpr uint64_t kBlockCount = 1 << 11;
class MinfsHarness : public fuchsia::io::test::Io1Harness {
public:
explicit MinfsHarness() : vfs_loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {
vfs_loop_.StartThread("vfs_thread");
auto device = std::make_unique<block_client::FakeBlockDevice>(kBlockCount, kMinfsBlockSize);
auto bcache = Bcache::Create(std::move(device), kBlockCount);
ZX_ASSERT(bcache.is_ok());
ZX_ASSERT(Mkfs(bcache.value().get()).is_ok());
auto runner = Runner::Create(vfs_loop_.dispatcher(), *std::move(bcache), {});
ZX_ASSERT(runner.is_ok());
runner_ = *std::move(runner);
// One connection must be maintained to avoid filesystem termination.
auto root_server = root_client_.NewRequest();
zx::result status =
runner_->ServeRoot(fidl::ServerEnd<fuchsia_io::Directory>(root_server.TakeChannel()));
ZX_ASSERT(status.is_ok());
}
~MinfsHarness() override {
// The runner shutdown takes care of shutting everything down in the right order, including the
// async loop.
runner_->Shutdown([](zx_status_t status) { ZX_ASSERT(status == ZX_OK); });
vfs_loop_.JoinThreads();
}
void GetConfig(GetConfigCallback callback) final {
fuchsia::io::test::Io1Config config;
// Supported options
config.supports_create = true;
config.supports_rename = true;
config.supports_link = true;
config.supports_get_token = true;
config.supports_unlink = true;
config.supports_directory_watchers = true;
config.supports_append = true;
config.supported_attributes = fuchsia::io::NodeAttributesQuery::CHANGE_TIME |
fuchsia::io::NodeAttributesQuery::MODIFICATION_TIME;
callback(config);
}
void GetDirectory(fuchsia::io::test::Directory root, fuchsia::io::OpenFlags flags,
fidl::InterfaceRequest<fuchsia::io::Directory> directory_request) final {
// Create a unique directory within the root of minfs for each request and popuplate it with the
// requested contents.
auto directory = CreateUniqueDirectory();
PopulateDirectory(root.entries, *directory);
zx::result options = fs::VnodeConnectionOptions::FromOpen1Flags(
fuchsia_io::OpenFlags{static_cast<uint32_t>(flags)});
ZX_ASSERT_MSG(options.is_ok(), "Failed to validate flags: %s", options.status_string());
zx_status_t status =
runner_->Serve(std::move(directory), directory_request.TakeChannel(), *options);
ZX_ASSERT_MSG(status == ZX_OK, "Failed to serve test directory: %s",
zx_status_get_string(status));
}
void PopulateDirectory(
const std::vector<std::unique_ptr<fuchsia::io::test::DirectoryEntry>>& entries,
Directory& dir) {
for (const auto& entry : entries) {
AddEntry(*entry, dir);
}
}
void AddEntry(const fuchsia::io::test::DirectoryEntry& entry, Directory& parent) {
switch (entry.Which()) {
case fuchsia::io::test::DirectoryEntry::Tag::kDirectory: {
zx::result vnode = parent.Create(entry.directory().name, fs::CreationType::kDirectory);
ZX_ASSERT_MSG(vnode.is_ok(), "Failed to create a directory: %s", vnode.status_string());
auto directory = fbl::RefPtr<Directory>::Downcast(*std::move(vnode));
ZX_ASSERT_MSG(directory != nullptr, "A vnode of the wrong type was created");
PopulateDirectory(entry.directory().entries, *directory);
// The directory was opened when it was created.
directory->Close();
break;
}
case fuchsia::io::test::DirectoryEntry::Tag::kFile: {
zx::result file = parent.Create(entry.file().name, fs::CreationType::kFile);
ZX_ASSERT_MSG(file.is_ok(), "Failed to create a file: %s", file.status_string());
if (!entry.file().contents.empty()) {
size_t actual = 0;
zx_status_t status =
file->Write(entry.file().contents.data(), entry.file().contents.size(),
/*offset=*/0, &actual);
ZX_ASSERT_MSG(status == ZX_OK, "Failed to write to file: %s",
zx_status_get_string(status));
}
// The file was opened when it was created.
file->Close();
break;
}
case fuchsia::io::test::DirectoryEntry::Tag::kRemoteDirectory:
ZX_PANIC("Remote directories are not supported");
case fuchsia::io::test::DirectoryEntry::Tag::kExecutableFile:
ZX_PANIC("Executable files are not supported");
case fuchsia::io::test::DirectoryEntry::Tag::Invalid:
ZX_PANIC("Unknown/Invalid DirectoryEntry type!");
}
}
fbl::RefPtr<Directory> GetRootNode() {
auto vn_or = runner_->minfs().VnodeGet(kMinfsRootIno);
ZX_ASSERT(vn_or.is_ok());
auto root = fbl::RefPtr<Directory>::Downcast(std::move(vn_or.value()));
ZX_ASSERT_MSG(root != nullptr, "The root node wasn't a directory");
return root;
}
fbl::RefPtr<Directory> CreateUniqueDirectory() {
++directory_count_;
std::string directory_name = std::to_string(directory_count_);
fbl::RefPtr<Directory> root = GetRootNode();
zx::result vnode = root->Create(directory_name, fs::CreationType::kDirectory);
ZX_ASSERT_MSG(vnode.is_ok(), "Failed to create a unique directory: %s", vnode.status_string());
auto directory = fbl::RefPtr<Directory>::Downcast(*std::move(vnode));
ZX_ASSERT_MSG(directory != nullptr, "A vnode of the wrong type was created");
return directory;
}
private:
async::Loop vfs_loop_;
std::unique_ptr<Runner> runner_;
// Used to create a new unique directory within minfs for every call to |GetDirectory|.
uint32_t directory_count_ = 0;
fuchsia::io::DirectoryPtr root_client_;
};
} // namespace minfs
int main(int argc, const char** argv) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
fuchsia_logging::SetTags({"io_conformance_harness_minfs"});
minfs::MinfsHarness harness;
fidl::BindingSet<fuchsia::io::test::Io1Harness> bindings;
// Expose the Io1Harness protocol as an outgoing service.
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
context->outgoing()->AddPublicService(bindings.GetHandler(&harness));
zx_status_t status = loop.Run();
return status;
}