summaryrefslogtreecommitdiffhomepage
path: root/test/fuse
diff options
context:
space:
mode:
Diffstat (limited to 'test/fuse')
-rw-r--r--test/fuse/BUILD9
-rw-r--r--test/fuse/README.md103
-rw-r--r--test/fuse/linux/BUILD32
-rw-r--r--test/fuse/linux/fuse_base.cc208
-rw-r--r--test/fuse/linux/fuse_base.h99
-rw-r--r--test/fuse/linux/stat_test.cc169
6 files changed, 0 insertions, 620 deletions
diff --git a/test/fuse/BUILD b/test/fuse/BUILD
deleted file mode 100644
index 56157c96b..000000000
--- a/test/fuse/BUILD
+++ /dev/null
@@ -1,9 +0,0 @@
-load("//test/runner:defs.bzl", "syscall_test")
-
-package(licenses = ["notice"])
-
-syscall_test(
- fuse = "True",
- test = "//test/fuse/linux:stat_test",
- vfs2 = "True",
-)
diff --git a/test/fuse/README.md b/test/fuse/README.md
deleted file mode 100644
index 734c3a4e3..000000000
--- a/test/fuse/README.md
+++ /dev/null
@@ -1,103 +0,0 @@
-# gVisor FUSE Test Suite
-
-This is an integration test suite for fuse(4) filesystem. It runs under both
-gVisor and Linux, and ensures compatibility between the two. This test suite is
-based on system calls test.
-
-This document describes the framework of fuse integration test and the
-guidelines that should be followed when adding new fuse tests.
-
-## Integration Test Framework
-
-Please refer to the figure below. `>` is entering the function, `<` is leaving
-the function, and `=` indicates sequentially entering and leaving.
-
-```
- | Client (Test Main Process) | Server (FUSE Daemon)
- | |
- | >TEST_F() |
- | >SetUp() |
- | =MountFuse() |
- | >SetUpFuseServer() |
- | [create communication pipes] |
- | =fork() | =fork()
- | >WaitCompleted() |
- | [wait for MarkDone()] |
- | | =ConsumeFuseInit()
- | | =MarkDone()
- | <WaitCompleted() |
- | <SetUpFuseServer() |
- | <SetUp() |
- | >SetExpected() |
- | [construct expected reaction] |
- | | >FuseLoop()
- | | >ReceiveExpected()
- | | [wait data from pipe]
- | [write data to pipe] |
- | [wait for MarkDone()] |
- | | [save data to memory]
- | | =MarkDone()
- | <SetExpected() |
- | | <ReceiveExpected()
- | | >read()
- | | [wait for fs operation]
- | >[Do fs operation] |
- | [wait for fs response] |
- | | <read()
- | | =CompareRequest()
- | | =write() [write fs response]
- | <[Do fs operation] |
- | =[Test fs operation result] |
- | =[wait for MarkDone()] |
- | | =MarkDone()
- | >TearDown() |
- | =UnmountFuse() |
- | <TearDown() |
- | <TEST_F() |
-```
-
-## Running the tests
-
-Based on syscall tests, fuse tests can run in different environments. To enable
-fuse testing environment, the test targets should be appended with `_fuse`.
-
-For example, to run fuse test in `stat_test.cc`:
-
-```bash
-$ bazel test //test/fuse:stat_test_runsc_ptrace_vfs2_fuse
-```
-
-Test all targets tagged with fuse:
-
-```bash
-$ bazel test --test_tag_filters=fuse //test/fuse/...
-```
-
-## Writing a new FUSE test
-
-1. Add test targets in `BUILD` and `linux/BUILD`.
-2. Inherit your test from `FuseTest` base class. It allows you to:
- - Run a fake FUSE server in background during each test setup.
- - Create pipes for communication and provide utility functions.
- - Stop FUSE server after test completes.
-3. Customize your comparison function for request assessment in FUSE server.
-4. Add the mapping of the size of structs if you are working on new FUSE
- opcode.
- - Please update `FuseTest::GetPayloadSize()` for each new FUSE opcode.
-5. Build the expected request-response pair of your FUSE operation.
-6. Call `SetExpected()` function to inject the expected reaction.
-7. Check the response and/or errors.
-8. Finally call `WaitCompleted()` to ensure the FUSE server acts correctly.
-
-A few customized matchers used in syscalls test are encouraged to test the
-outcome of filesystem operations. Such as:
-
-```cc
-SyscallSucceeds()
-SyscallSucceedsWithValue(...)
-SyscallFails()
-SyscallFailsWithErrno(...)
-```
-
-Please refer to [test/syscalls/README.md](../syscalls/README.md) for further
-details.
diff --git a/test/fuse/linux/BUILD b/test/fuse/linux/BUILD
deleted file mode 100644
index 4871bb531..000000000
--- a/test/fuse/linux/BUILD
+++ /dev/null
@@ -1,32 +0,0 @@
-load("//tools:defs.bzl", "cc_binary", "cc_library", "gtest")
-
-package(
- default_visibility = ["//:sandbox"],
- licenses = ["notice"],
-)
-
-cc_binary(
- name = "stat_test",
- testonly = 1,
- srcs = ["stat_test.cc"],
- deps = [
- gtest,
- ":fuse_base",
- "//test/util:test_main",
- "//test/util:test_util",
- ],
-)
-
-cc_library(
- name = "fuse_base",
- testonly = 1,
- srcs = ["fuse_base.cc"],
- hdrs = ["fuse_base.h"],
- deps = [
- gtest,
- "//test/util:posix_error",
- "//test/util:temp_path",
- "//test/util:test_util",
- "@com_google_absl//absl/strings:str_format",
- ],
-)
diff --git a/test/fuse/linux/fuse_base.cc b/test/fuse/linux/fuse_base.cc
deleted file mode 100644
index 9c3124472..000000000
--- a/test/fuse/linux/fuse_base.cc
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "test/fuse/linux/fuse_base.h"
-
-#include <fcntl.h>
-#include <linux/fuse.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <unistd.h>
-
-#include <iostream>
-
-#include "gtest/gtest.h"
-#include "absl/strings/str_format.h"
-#include "test/util/posix_error.h"
-#include "test/util/temp_path.h"
-#include "test/util/test_util.h"
-
-namespace gvisor {
-namespace testing {
-
-void FuseTest::SetUp() {
- MountFuse();
- SetUpFuseServer();
-}
-
-void FuseTest::TearDown() { UnmountFuse(); }
-
-// Since CompareRequest is running in background thread, gTest assertions and
-// expectations won't directly reflect the test result. However, the FUSE
-// background server still connects to the same standard I/O as testing main
-// thread. So EXPECT_XX can still be used to show different results. To
-// ensure failed testing result is observable, return false and the result
-// will be sent to test main thread via pipe.
-bool FuseTest::CompareRequest(void* expected_mem, size_t expected_len,
- void* real_mem, size_t real_len) {
- if (expected_len != real_len) return false;
- return memcmp(expected_mem, real_mem, expected_len) == 0;
-}
-
-// SetExpected is called by the testing main thread to set expected request-
-// response pair of a single FUSE operation.
-void FuseTest::SetExpected(struct iovec* iov_in, int iov_in_cnt,
- struct iovec* iov_out, int iov_out_cnt) {
- EXPECT_THAT(RetryEINTR(writev)(set_expected_[1], iov_in, iov_in_cnt),
- SyscallSucceedsWithValue(::testing::Gt(0)));
- WaitCompleted();
-
- EXPECT_THAT(RetryEINTR(writev)(set_expected_[1], iov_out, iov_out_cnt),
- SyscallSucceedsWithValue(::testing::Gt(0)));
- WaitCompleted();
-}
-
-// WaitCompleted waits for the FUSE server to finish its job and check if it
-// completes without errors.
-void FuseTest::WaitCompleted() {
- char success;
- EXPECT_THAT(RetryEINTR(read)(done_[0], &success, sizeof(success)),
- SyscallSucceedsWithValue(1));
-}
-
-void FuseTest::MountFuse() {
- EXPECT_THAT(dev_fd_ = open("/dev/fuse", O_RDWR), SyscallSucceeds());
-
- std::string mount_opts = absl::StrFormat("fd=%d,%s", dev_fd_, kMountOpts);
- mount_point_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- EXPECT_THAT(mount("fuse", mount_point_.path().c_str(), "fuse",
- MS_NODEV | MS_NOSUID, mount_opts.c_str()),
- SyscallSucceedsWithValue(0));
-}
-
-void FuseTest::UnmountFuse() {
- EXPECT_THAT(umount(mount_point_.path().c_str()), SyscallSucceeds());
- // TODO(gvisor.dev/issue/3330): ensure the process is terminated successfully.
-}
-
-// ConsumeFuseInit consumes the first FUSE request and returns the
-// corresponding PosixError.
-PosixError FuseTest::ConsumeFuseInit() {
- RETURN_ERROR_IF_SYSCALL_FAIL(
- RetryEINTR(read)(dev_fd_, buf_.data(), buf_.size()));
-
- struct iovec iov_out[2];
- struct fuse_out_header out_header = {
- .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_init_out),
- .error = 0,
- .unique = 2,
- };
- // Returns a fake fuse_init_out with 7.0 version to avoid ECONNREFUSED
- // error in the initialization of FUSE connection.
- struct fuse_init_out out_payload = {
- .major = 7,
- };
- iov_out[0].iov_len = sizeof(out_header);
- iov_out[0].iov_base = &out_header;
- iov_out[1].iov_len = sizeof(out_payload);
- iov_out[1].iov_base = &out_payload;
-
- RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(writev)(dev_fd_, iov_out, 2));
- return NoError();
-}
-
-// ReceiveExpected reads 1 pair of expected fuse request-response `iovec`s
-// from pipe and save them into member variables of this testing instance.
-void FuseTest::ReceiveExpected() {
- // Set expected fuse_in request.
- EXPECT_THAT(len_in_ = RetryEINTR(read)(set_expected_[0], mem_in_.data(),
- mem_in_.size()),
- SyscallSucceedsWithValue(::testing::Gt(0)));
- MarkDone(len_in_ > 0);
-
- // Set expected fuse_out response.
- EXPECT_THAT(len_out_ = RetryEINTR(read)(set_expected_[0], mem_out_.data(),
- mem_out_.size()),
- SyscallSucceedsWithValue(::testing::Gt(0)));
- MarkDone(len_out_ > 0);
-}
-
-// MarkDone writes 1 byte of success indicator through pipe.
-void FuseTest::MarkDone(bool success) {
- char data = success ? 1 : 0;
- EXPECT_THAT(RetryEINTR(write)(done_[1], &data, sizeof(data)),
- SyscallSucceedsWithValue(1));
-}
-
-// FuseLoop is the implementation of the fake FUSE server. Read from /dev/fuse,
-// compare the request by CompareRequest (use derived function if specified),
-// and write the expected response to /dev/fuse.
-void FuseTest::FuseLoop() {
- bool success = true;
- ssize_t len = 0;
- while (true) {
- ReceiveExpected();
-
- EXPECT_THAT(len = RetryEINTR(read)(dev_fd_, buf_.data(), buf_.size()),
- SyscallSucceedsWithValue(len_in_));
- if (len != len_in_) success = false;
-
- if (!CompareRequest(buf_.data(), len_in_, mem_in_.data(), len_in_)) {
- std::cerr << "the FUSE request is not expected" << std::endl;
- success = false;
- }
-
- EXPECT_THAT(len = RetryEINTR(write)(dev_fd_, mem_out_.data(), len_out_),
- SyscallSucceedsWithValue(len_out_));
- if (len != len_out_) success = false;
- MarkDone(success);
- }
-}
-
-// SetUpFuseServer creates 2 pipes. First is for testing client to send the
-// expected request-response pair, and the other acts as a checkpoint for the
-// FUSE server to notify the client that it can proceed.
-void FuseTest::SetUpFuseServer() {
- ASSERT_THAT(pipe(set_expected_), SyscallSucceedsWithValue(0));
- ASSERT_THAT(pipe(done_), SyscallSucceedsWithValue(0));
-
- switch (fork()) {
- case -1:
- GTEST_FAIL();
- return;
- case 0:
- break;
- default:
- ASSERT_THAT(close(set_expected_[0]), SyscallSucceedsWithValue(0));
- ASSERT_THAT(close(done_[1]), SyscallSucceedsWithValue(0));
- WaitCompleted();
- return;
- }
-
- ASSERT_THAT(close(set_expected_[1]), SyscallSucceedsWithValue(0));
- ASSERT_THAT(close(done_[0]), SyscallSucceedsWithValue(0));
-
- MarkDone(ConsumeFuseInit().ok());
-
- FuseLoop();
- _exit(0);
-}
-
-// GetPayloadSize is a helper function to get the number of bytes of a
-// specific FUSE operation struct.
-size_t FuseTest::GetPayloadSize(uint32_t opcode, bool in) {
- switch (opcode) {
- case FUSE_INIT:
- return in ? sizeof(struct fuse_init_in) : sizeof(struct fuse_init_out);
- default:
- break;
- }
- return 0;
-}
-
-} // namespace testing
-} // namespace gvisor
diff --git a/test/fuse/linux/fuse_base.h b/test/fuse/linux/fuse_base.h
deleted file mode 100644
index 3a2f255a9..000000000
--- a/test/fuse/linux/fuse_base.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef GVISOR_TEST_FUSE_FUSE_BASE_H_
-#define GVISOR_TEST_FUSE_FUSE_BASE_H_
-
-#include <linux/fuse.h>
-#include <sys/uio.h>
-
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "test/util/posix_error.h"
-#include "test/util/temp_path.h"
-
-namespace gvisor {
-namespace testing {
-
-constexpr char kMountOpts[] = "rootmode=755,user_id=0,group_id=0";
-
-class FuseTest : public ::testing::Test {
- public:
- FuseTest() {
- buf_.resize(FUSE_MIN_READ_BUFFER);
- mem_in_.resize(FUSE_MIN_READ_BUFFER);
- mem_out_.resize(FUSE_MIN_READ_BUFFER);
- }
- void SetUp() override;
- void TearDown() override;
-
- // CompareRequest is used by the FUSE server and should be implemented to
- // compare different FUSE operations. It compares the actual FUSE input
- // request with the expected one set by `SetExpected()`.
- virtual bool CompareRequest(void* expected_mem, size_t expected_len,
- void* real_mem, size_t real_len);
-
- // SetExpected is called by the testing main thread. Writes a request-
- // response pair into FUSE server's member variables via pipe.
- void SetExpected(struct iovec* iov_in, int iov_in_cnt, struct iovec* iov_out,
- int iov_out_cnt);
-
- // WaitCompleted waits for FUSE server to complete its processing. It
- // complains if the FUSE server responds failure during tests.
- void WaitCompleted();
-
- protected:
- TempPath mount_point_;
-
- private:
- void MountFuse();
- void UnmountFuse();
-
- // ConsumeFuseInit is only used during FUSE server setup.
- PosixError ConsumeFuseInit();
-
- // ReceiveExpected is the FUSE server side's corresponding code of
- // `SetExpected()`. Save the request-response pair into its memory.
- void ReceiveExpected();
-
- // MarkDone is used by the FUSE server to tell testing main if it's OK to
- // proceed next command.
- void MarkDone(bool success);
-
- // FuseLoop is where the FUSE server stay until it is terminated.
- void FuseLoop();
-
- // SetUpFuseServer creates 2 pipes for communication and forks FUSE server.
- void SetUpFuseServer();
-
- // GetPayloadSize is a helper function to get the number of bytes of a
- // specific FUSE operation struct.
- size_t GetPayloadSize(uint32_t opcode, bool in);
-
- int dev_fd_;
- int set_expected_[2];
- int done_[2];
-
- std::vector<char> buf_;
- std::vector<char> mem_in_;
- std::vector<char> mem_out_;
- ssize_t len_in_;
- ssize_t len_out_;
-};
-
-} // namespace testing
-} // namespace gvisor
-
-#endif // GVISOR_TEST_FUSE_FUSE_BASE_H_
diff --git a/test/fuse/linux/stat_test.cc b/test/fuse/linux/stat_test.cc
deleted file mode 100644
index 172e09867..000000000
--- a/test/fuse/linux/stat_test.cc
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/fuse.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <vector>
-
-#include "gtest/gtest.h"
-#include "test/fuse/linux/fuse_base.h"
-#include "test/util/test_util.h"
-
-namespace gvisor {
-namespace testing {
-
-namespace {
-
-class StatTest : public FuseTest {
- public:
- bool CompareRequest(void* expected_mem, size_t expected_len, void* real_mem,
- size_t real_len) override {
- if (expected_len != real_len) return false;
- struct fuse_in_header* real_header =
- reinterpret_cast<fuse_in_header*>(real_mem);
-
- if (real_header->opcode != FUSE_GETATTR) {
- std::cerr << "expect header opcode " << FUSE_GETATTR << " but got "
- << real_header->opcode << std::endl;
- return false;
- }
- return true;
- }
-
- bool StatsAreEqual(struct stat expected, struct stat actual) {
- // device number will be dynamically allocated by kernel, we cannot know
- // in advance
- actual.st_dev = expected.st_dev;
- return memcmp(&expected, &actual, sizeof(struct stat)) == 0;
- }
-};
-
-TEST_F(StatTest, StatNormal) {
- struct iovec iov_in[2];
- struct iovec iov_out[2];
-
- struct fuse_in_header in_header = {
- .len = sizeof(struct fuse_in_header) + sizeof(struct fuse_getattr_in),
- .opcode = FUSE_GETATTR,
- .unique = 4,
- .nodeid = 1,
- .uid = 0,
- .gid = 0,
- .pid = 4,
- .padding = 0,
- };
- struct fuse_getattr_in in_payload = {0};
- iov_in[0].iov_len = sizeof(in_header);
- iov_in[0].iov_base = &in_header;
- iov_in[1].iov_len = sizeof(in_payload);
- iov_in[1].iov_base = &in_payload;
-
- mode_t expected_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
- struct timespec atime = {.tv_sec = 1595436289, .tv_nsec = 134150844};
- struct timespec mtime = {.tv_sec = 1595436290, .tv_nsec = 134150845};
- struct timespec ctime = {.tv_sec = 1595436291, .tv_nsec = 134150846};
- struct fuse_out_header out_header = {
- .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
- .error = 0,
- .unique = 4,
- };
- struct fuse_attr attr = {
- .ino = 1,
- .size = 512,
- .blocks = 4,
- .atime = static_cast<uint64_t>(atime.tv_sec),
- .mtime = static_cast<uint64_t>(mtime.tv_sec),
- .ctime = static_cast<uint64_t>(ctime.tv_sec),
- .atimensec = static_cast<uint32_t>(atime.tv_nsec),
- .mtimensec = static_cast<uint32_t>(mtime.tv_nsec),
- .ctimensec = static_cast<uint32_t>(ctime.tv_nsec),
- .mode = expected_mode,
- .nlink = 2,
- .uid = 1234,
- .gid = 4321,
- .rdev = 12,
- .blksize = 4096,
- };
- struct fuse_attr_out out_payload = {
- .attr = attr,
- };
- iov_out[0].iov_len = sizeof(out_header);
- iov_out[0].iov_base = &out_header;
- iov_out[1].iov_len = sizeof(out_payload);
- iov_out[1].iov_base = &out_payload;
-
- SetExpected(iov_in, 2, iov_out, 2);
-
- struct stat stat_buf;
- EXPECT_THAT(stat(mount_point_.path().c_str(), &stat_buf), SyscallSucceeds());
-
- struct stat expected_stat = {
- .st_ino = attr.ino,
- .st_nlink = attr.nlink,
- .st_mode = expected_mode,
- .st_uid = attr.uid,
- .st_gid = attr.gid,
- .st_rdev = attr.rdev,
- .st_size = static_cast<off_t>(attr.size),
- .st_blksize = attr.blksize,
- .st_blocks = static_cast<blkcnt_t>(attr.blocks),
- .st_atim = atime,
- .st_mtim = mtime,
- .st_ctim = ctime,
- };
- EXPECT_TRUE(StatsAreEqual(stat_buf, expected_stat));
- WaitCompleted();
-}
-
-TEST_F(StatTest, StatNotFound) {
- struct iovec iov_in[2];
- struct iovec iov_out[2];
-
- struct fuse_in_header in_header = {
- .len = sizeof(struct fuse_in_header) + sizeof(struct fuse_getattr_in),
- .opcode = FUSE_GETATTR,
- .unique = 4,
- };
- struct fuse_getattr_in in_payload = {0};
- iov_in[0].iov_len = sizeof(in_header);
- iov_in[0].iov_base = &in_header;
- iov_in[1].iov_len = sizeof(in_payload);
- iov_in[1].iov_base = &in_payload;
-
- struct fuse_out_header out_header = {
- .len = sizeof(struct fuse_out_header),
- .error = -ENOENT,
- .unique = 4,
- };
- iov_out[0].iov_len = sizeof(out_header);
- iov_out[0].iov_base = &out_header;
-
- SetExpected(iov_in, 2, iov_out, 1);
-
- struct stat stat_buf;
- EXPECT_THAT(stat(mount_point_.path().c_str(), &stat_buf),
- SyscallFailsWithErrno(ENOENT));
- WaitCompleted();
-}
-
-} // namespace
-
-} // namespace testing
-} // namespace gvisor