| // 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::CREATION_TIME | |
| fuchsia::io::NodeAttributesQuery::MODIFICATION_TIME | |
| fuchsia::io::NodeAttributesQuery::ID | |
| fuchsia::io::NodeAttributesQuery::CONTENT_SIZE | |
| fuchsia::io::NodeAttributesQuery::STORAGE_SIZE | |
| fuchsia::io::NodeAttributesQuery::LINK_COUNT; |
| |
| 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; |
| } |