| // 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 <fuchsia/fs/llcpp/fidl.h> |
| #include <fuchsia/io/llcpp/fidl.h> |
| #include <lib/fdio/cpp/caller.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/zx/vmo.h> |
| |
| #include <array> |
| #include <atomic> |
| #include <thread> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/storage/blobfs/test/blob_utils.h" |
| #include "src/storage/blobfs/test/integration/blobfs_fixtures.h" |
| #include "src/storage/fvm/format.h" |
| #include "src/storage/lib/utils/topological_path.h" |
| |
| namespace blobfs { |
| namespace { |
| |
| namespace fio = fuchsia_io; |
| namespace fuchsia_fs = fuchsia_fs; |
| |
| using fuchsia_fs::wire::FilesystemInfoQuery; |
| |
| class QueryServiceTest : public BlobfsWithFvmTest { |
| protected: |
| fidl::WireSyncClient<fuchsia_fs::Query> ConnectToQueryService() { |
| auto endpoints = fidl::CreateEndpoints<fuchsia_fs::Query>(); |
| EXPECT_EQ(endpoints.status_value(), ZX_OK); |
| auto [query_client_end, query_server_end] = *std::move(endpoints); |
| |
| std::string query_service_path = |
| std::string("svc/") + fidl::DiscoverableProtocolName<fuchsia_fs::Query>; |
| EXPECT_EQ( |
| fdio_service_connect_at(fs().GetOutgoingDirectory()->get(), query_service_path.c_str(), |
| query_server_end.TakeChannel().release()), |
| ZX_OK); |
| return fidl::WireSyncClient<fuchsia_fs::Query>(std::move(query_client_end)); |
| } |
| |
| void QueryInfo(size_t expected_nodes, size_t expected_bytes) { |
| fidl::WireSyncClient<fuchsia_fs::Query> query_service = ConnectToQueryService(); |
| auto call_result = query_service.GetInfo(FilesystemInfoQuery::kMask); |
| ASSERT_EQ(call_result.status(), ZX_OK); |
| const auto& query_result = call_result.value().result; |
| ASSERT_TRUE(query_result.is_response()); |
| |
| const fuchsia_fs::wire::FilesystemInfo& info = query_result.response().info; |
| |
| // Check that total_bytes are a multiple of slice size. |
| const uint64_t slice_size = fs().options().fvm_slice_size; |
| EXPECT_GE(info.total_bytes(), slice_size); |
| EXPECT_EQ(info.total_bytes() % slice_size, 0ul); |
| |
| // Check that used_bytes are within a reasonable range. |
| EXPECT_GE(info.used_bytes(), expected_bytes); |
| EXPECT_LE(info.used_bytes(), info.total_bytes()); |
| |
| EXPECT_GE(info.total_nodes(), kBlobfsDefaultInodeCount); |
| EXPECT_EQ((info.total_nodes() * sizeof(Inode)) % slice_size, 0ul); |
| EXPECT_EQ(info.used_nodes(), expected_nodes); |
| |
| // Should be able to query for the koid of the |fs_id| event. |
| EXPECT_TRUE(info.fs_id().is_valid()); |
| zx_info_handle_basic_t event_info; |
| EXPECT_EQ(info.fs_id().get_info(ZX_INFO_HANDLE_BASIC, &event_info, sizeof(event_info), nullptr, |
| nullptr), |
| ZX_OK); |
| EXPECT_GE(event_info.koid, 0ul); |
| |
| EXPECT_EQ(info.block_size(), kBlobfsBlockSize); |
| EXPECT_EQ(info.max_node_name_size(), digest::kSha256HexLength); |
| EXPECT_EQ(info.fs_type(), fuchsia_fs::wire::FsType::BLOBFS); |
| |
| const std::string expected_fs_name = "blobfs"; |
| ASSERT_EQ(expected_fs_name.size(), info.name().size()); |
| ASSERT_EQ(std::string(info.name().data(), info.name().size()), expected_fs_name) |
| << "Unexpected filesystem mounted"; |
| |
| const std::string expected_device_path = |
| storage::GetTopologicalPath(fs().DevicePath().value()).value(); |
| ASSERT_EQ(expected_device_path.size(), info.device_path().size()); |
| ASSERT_EQ(std::string(info.device_path().data(), info.device_path().size()), |
| expected_device_path) |
| << "Incorrect device path"; |
| } |
| }; |
| |
| TEST_F(QueryServiceTest, QueryInfo) { |
| size_t total_bytes = 0; |
| ASSERT_NO_FATAL_FAILURE(QueryInfo(0, 0)); |
| for (size_t i = 10; i < 16; i++) { |
| std::unique_ptr<BlobInfo> info = GenerateRandomBlob(fs().mount_path(), 1 << i); |
| std::unique_ptr<MerkleTreeInfo> merkle_tree = |
| CreateMerkleTree(info->data.get(), info->size_data, /*use_compact_format=*/true); |
| |
| fbl::unique_fd fd; |
| ASSERT_NO_FATAL_FAILURE(MakeBlob(*info, &fd)); |
| total_bytes += fbl::round_up(merkle_tree->merkle_tree_size + info->size_data, kBlobfsBlockSize); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(QueryInfo(6, total_bytes)); |
| } |
| |
| TEST_F(QueryServiceTest, SelectiveQueryInfoEmpty) { |
| fidl::WireSyncClient<fuchsia_fs::Query> query_service = ConnectToQueryService(); |
| auto call_result = query_service.GetInfo(FilesystemInfoQuery()); |
| ASSERT_EQ(call_result.status(), ZX_OK); |
| const auto& query_result = call_result.value().result; |
| ASSERT_TRUE(query_result.is_response()); |
| ASSERT_TRUE(query_result.response().info.IsEmpty()); |
| } |
| |
| TEST_F(QueryServiceTest, SelectiveQueryInfoSingleField) { |
| fidl::WireSyncClient<fuchsia_fs::Query> query_service = ConnectToQueryService(); |
| auto call_result = query_service.GetInfo(FilesystemInfoQuery::TOTAL_BYTES); |
| ASSERT_EQ(call_result.status(), ZX_OK); |
| const auto& query_result = call_result.value().result; |
| ASSERT_TRUE(query_result.is_response()); |
| const fuchsia_fs::wire::FilesystemInfo& info = query_result.response().info; |
| |
| ASSERT_FALSE(info.IsEmpty()); |
| ASSERT_TRUE(info.has_total_bytes()); |
| ASSERT_FALSE(info.has_used_bytes()); |
| ASSERT_FALSE(info.has_total_nodes()); |
| ASSERT_FALSE(info.has_used_nodes()); |
| ASSERT_FALSE(info.has_fs_id()); |
| ASSERT_FALSE(info.has_block_size()); |
| ASSERT_FALSE(info.has_max_node_name_size()); |
| ASSERT_FALSE(info.has_fs_type()); |
| ASSERT_FALSE(info.has_name()); |
| ASSERT_FALSE(info.has_device_path()); |
| } |
| |
| TEST_F(QueryServiceTest, IsNodeInFilesystemPositiveCase) { |
| // Get a token corresponding to the root directory. |
| fdio_cpp::UnownedFdioCaller caller(root_fd()); |
| auto token_result = |
| fidl::WireCall(fidl::UnownedClientEnd<fio::Directory>(caller.channel())).GetToken(); |
| ASSERT_EQ(token_result.status(), ZX_OK); |
| ASSERT_EQ(token_result->s, ZX_OK); |
| zx::handle token_raw = std::move(token_result->token); |
| ASSERT_TRUE(token_raw.is_valid()); |
| zx::event token(std::move(token_raw)); |
| |
| // This token is in the filesystem. |
| fidl::WireSyncClient<fuchsia_fs::Query> query_service = ConnectToQueryService(); |
| auto result = query_service.IsNodeInFilesystem(std::move(token)); |
| ASSERT_EQ(result.status(), ZX_OK); |
| ASSERT_TRUE(result->is_in_filesystem); |
| } |
| |
| TEST_F(QueryServiceTest, IsNodeInFilesystemNegativeCase) { |
| // Create some arbitrary event, to fake a token. |
| zx::event token; |
| zx::event::create(0, &token); |
| |
| // This token should not be in the filesystem. |
| fidl::WireSyncClient<fuchsia_fs::Query> query_service = ConnectToQueryService(); |
| auto result = query_service.IsNodeInFilesystem(std::move(token)); |
| ASSERT_EQ(result.status(), ZX_OK); |
| ASSERT_FALSE(result->is_in_filesystem); |
| } |
| |
| } // namespace |
| } // namespace blobfs |