diff options
Diffstat (limited to 'test/fuse')
-rw-r--r-- | test/fuse/BUILD | 73 | ||||
-rw-r--r-- | test/fuse/README.md | 188 | ||||
-rw-r--r-- | test/fuse/linux/BUILD | 230 | ||||
-rw-r--r-- | test/fuse/linux/create_test.cc | 128 | ||||
-rw-r--r-- | test/fuse/linux/fuse_base.cc | 447 | ||||
-rw-r--r-- | test/fuse/linux/fuse_base.h | 251 | ||||
-rw-r--r-- | test/fuse/linux/fuse_fd_util.cc | 61 | ||||
-rw-r--r-- | test/fuse/linux/fuse_fd_util.h | 48 | ||||
-rw-r--r-- | test/fuse/linux/mkdir_test.cc | 88 | ||||
-rw-r--r-- | test/fuse/linux/mknod_test.cc | 107 | ||||
-rw-r--r-- | test/fuse/linux/open_test.cc | 128 | ||||
-rw-r--r-- | test/fuse/linux/read_test.cc | 390 | ||||
-rw-r--r-- | test/fuse/linux/readdir_test.cc | 193 | ||||
-rw-r--r-- | test/fuse/linux/readlink_test.cc | 85 | ||||
-rw-r--r-- | test/fuse/linux/release_test.cc | 74 | ||||
-rw-r--r-- | test/fuse/linux/rmdir_test.cc | 100 | ||||
-rw-r--r-- | test/fuse/linux/setstat_test.cc | 338 | ||||
-rw-r--r-- | test/fuse/linux/stat_test.cc | 229 | ||||
-rw-r--r-- | test/fuse/linux/symlink_test.cc | 88 | ||||
-rw-r--r-- | test/fuse/linux/unlink_test.cc | 107 | ||||
-rw-r--r-- | test/fuse/linux/write_test.cc | 303 |
21 files changed, 0 insertions, 3656 deletions
diff --git a/test/fuse/BUILD b/test/fuse/BUILD deleted file mode 100644 index 8e31fdd41..000000000 --- a/test/fuse/BUILD +++ /dev/null @@ -1,73 +0,0 @@ -load("//test/runner:defs.bzl", "syscall_test") - -package(licenses = ["notice"]) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:stat_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:open_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:release_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:mknod_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:symlink_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:readlink_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:mkdir_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:read_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:write_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:rmdir_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:readdir_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:create_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:unlink_test", -) - -syscall_test( - fuse = "True", - test = "//test/fuse/linux:setstat_test", -) diff --git a/test/fuse/README.md b/test/fuse/README.md deleted file mode 100644 index 65add57e2..000000000 --- a/test/fuse/README.md +++ /dev/null @@ -1,188 +0,0 @@ -# gVisor FUSE Test Suite - -This is an integration test suite for fuse(4) filesystem. It runs under gVisor -sandbox container with VFS2 and FUSE function enabled. - -This document describes the framework of FUSE integration test, how to use it, -and the guidelines that should be followed when adding new testing features. - -## Integration Test Framework - -By inheriting the `FuseTest` class defined in `linux/fuse_base.h`, every test -fixture can run in an environment with `mount_point_` mounted by a fake FUSE -server. It creates a `socketpair(2)` to send and receive control commands and -data between the client and the server. Because the FUSE server runs in the -background thread, gTest cannot catch its assertion failure immediately. Thus, -`TearDown()` function sends command to the FUSE server to check if all gTest -assertion in the server are successful and all requests and preset responses are -consumed. - -## Communication Diagram - -Diagram below describes how a testing thread communicates with the FUSE server -to achieve integration test. - -For the following diagram, `>` means entering the function, `<` is leaving the -function, and `=` indicates sequentially entering and leaving. Not necessarily -follow exactly the below diagram due to the nature of a multi-threaded system, -however, it is still helpful to know when the client waits for the server to -complete a command and when the server awaits the next instruction. - -``` -| Client (Testing Thread) | Server (FUSE Server Thread) -| | -| >TEST_F() | -| >SetUp() | -| =MountFuse() | -| >SetUpFuseServer() | -| [create communication socket]| -| =fork() | =fork() -| [wait server complete] | -| | =ServerConsumeFuseInit() -| | =ServerCompleteWith() -| <SetUpFuseServer() | -| <SetUp() | -| [testing main] | -| | >ServerFuseLoop() -| | [poll on socket and fd] -| >SetServerResponse() | -| [write data to socket] | -| [wait server complete] | -| | [socket event occurs] -| | >ServerHandleCommand() -| | >ServerReceiveResponse() -| | [read data from socket] -| | [save data to memory] -| | <ServerReceiveResponse() -| | =ServerCompleteWith() -| <SetServerResponse() | -| | <ServerHandleCommand() -| >[Do fs operation] | -| [wait for fs response] | -| | [fd event occurs] -| | >ServerProcessFuseRequest() -| | =[read fs request] -| | =[save fs request to memory] -| | =[write fs response] -| <[Do fs operation] | -| | <ServerProcessFuseRequest() -| | -| =[Test fs operation result] | -| | -| >GetServerActualRequest() | -| [write data to socket] | -| [wait data from server] | -| | [socket event occurs] -| | >ServerHandleCommand() -| | >ServerSendReceivedRequest() -| | [write data to socket] -| [read data from socket] | -| [wait server complete] | -| | <ServerSendReceivedRequest() -| | =ServerCompleteWith() -| <GetServerActualRequest() | -| | <ServerHandleCommand() -| | -| =[Test actual request] | -| | -| >TearDown() | -| ... | -| >GetServerNumUnsentResponses() | -| [write data to socket] | -| [wait server complete] | -| | [socket event arrive] -| | >ServerHandleCommand() -| | >ServerSendData() -| | [write data to socket] -| | <ServerSendData() -| | =ServerCompleteWith() -| [read data from socket] | -| [test if all succeeded] | -| <GetServerNumUnsentResponses() | -| | <ServerHandleCommand() -| =UnmountFuse() | -| <TearDown() | -| <TEST_F() | -``` - -## Running the tests - -Based on syscall tests, FUSE tests generate targets only with vfs2 and fuse -enabled. The corresponding targets end in `_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: - - Fork a fake FUSE server in background during each test setup. - - Create a pair of sockets for communication and provide utility - functions. - - Stop FUSE server and check if error occurs in it after test completes. -3. Build the expected opcode-response pairs of your FUSE operation. -4. Call `SetServerResponse()` to preset the next expected opcode and response. -5. Do real filesystem operations (FUSE is mounted at `mount_point_`). -6. Check FUSE response and/or errors. -7. Retrieve FUSE request by `GetServerActualRequest()`. -8. Check if the request is as expected. - -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. - -## Writing a new FuseTestCmd - -A `FuseTestCmd` is a control protocol used in the communication between the -testing thread and the FUSE server. Such commands are sent from the testing -thread to the FUSE server to set up, control, or inspect the behavior of the -FUSE server in response to a sequence of FUSE requests. - -The lifecycle of a command contains following steps: - -1. The testing thread sends a `FuseTestCmd` via socket and waits for - completion. -2. The FUSE server receives the command and does corresponding action. -3. (Optional) The testing thread reads data from socket. -4. The FUSE server sends a success indicator via socket after processing. -5. The testing thread gets the success signal and continues testing. - -The success indicator, i.e. `WaitServerComplete()`, is crucial at the end of -each `FuseTestCmd` sent from the testing thread. Because we don't want to begin -filesystem operation if the requests have not been completely set up. Also, to -test FUSE interactions in a sequential manner, concurrent requests are not -supported now. - -To add a new `FuseTestCmd`, one must comply with following format: - -1. Add a new `FuseTestCmd` enum class item defined in `linux/fuse_base.h` -2. Add a `SetServerXXX()` or `GetServerXXX()` public function in `FuseTest`. - This is how the testing thread will call to send control message. Define how - many bytes you want to send along with the command and what you will expect - to receive. Finally it should block and wait for a success indicator from - the FUSE server. -3. Add a handler logic in the switch condition of `ServerHandleCommand()`. Use - `ServerSendData()` or declare a new private function such as - `ServerReceiveXXX()` or `ServerSendXXX()`. It is mandatory to set it private - since only the FUSE server (forked from `FuseTest` base class) can call it. - This is the server part of the specific `FuseTestCmd` and the format of the - data should be consistent with what the client expects in the previous step. diff --git a/test/fuse/linux/BUILD b/test/fuse/linux/BUILD deleted file mode 100644 index 7673252ec..000000000 --- a/test/fuse/linux/BUILD +++ /dev/null @@ -1,230 +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_fd_util", - "//test/util:cleanup", - "//test/util:fs_util", - "//test/util:fuse_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "open_test", - testonly = 1, - srcs = ["open_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "release_test", - testonly = 1, - srcs = ["release_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "mknod_test", - testonly = 1, - srcs = ["mknod_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:temp_umask", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "symlink_test", - testonly = 1, - srcs = ["symlink_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "readlink_test", - testonly = 1, - srcs = ["readlink_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "mkdir_test", - testonly = 1, - srcs = ["mkdir_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:temp_umask", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "setstat_test", - testonly = 1, - srcs = ["setstat_test.cc"], - deps = [ - gtest, - ":fuse_fd_util", - "//test/util:cleanup", - "//test/util:fs_util", - "//test/util:fuse_util", - "//test/util:temp_umask", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "rmdir_test", - testonly = 1, - srcs = ["rmdir_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fs_util", - "//test/util:fuse_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "readdir_test", - testonly = 1, - srcs = ["readdir_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fs_util", - "//test/util:fuse_util", - "//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:fuse_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_util", - "@com_google_absl//absl/strings:str_format", - ], -) - -cc_library( - name = "fuse_fd_util", - testonly = 1, - srcs = ["fuse_fd_util.cc"], - hdrs = ["fuse_fd_util.h"], - deps = [ - gtest, - ":fuse_base", - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fuse_util", - "//test/util:posix_error", - ], -) - -cc_binary( - name = "read_test", - testonly = 1, - srcs = ["read_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "write_test", - testonly = 1, - srcs = ["write_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "create_test", - testonly = 1, - srcs = ["create_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fs_util", - "//test/util:fuse_util", - "//test/util:temp_umask", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "unlink_test", - testonly = 1, - srcs = ["unlink_test.cc"], - deps = [ - gtest, - ":fuse_base", - "//test/util:fuse_util", - "//test/util:temp_umask", - "//test/util:test_main", - "//test/util:test_util", - ], -) diff --git a/test/fuse/linux/create_test.cc b/test/fuse/linux/create_test.cc deleted file mode 100644 index 9a0219a58..000000000 --- a/test/fuse/linux/create_test.cc +++ /dev/null @@ -1,128 +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 <string> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fs_util.h" -#include "test/util/fuse_util.h" -#include "test/util/temp_umask.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class CreateTest : public FuseTest { - protected: - const std::string test_file_name_ = "test_file"; - const mode_t mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; -}; - -TEST_F(CreateTest, CreateFile) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_name_); - - // Ensure the file doesn't exist. - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header), - .error = -ENOENT, - }; - auto iov_out = FuseGenerateIovecs(out_header); - SetServerResponse(FUSE_LOOKUP, iov_out); - - // creat(2) is equal to open(2) with open_flags O_CREAT | O_WRONLY | O_TRUNC. - const mode_t new_mask = S_IWGRP | S_IWOTH; - const int open_flags = O_CREAT | O_WRONLY | O_TRUNC; - out_header.error = 0; - out_header.len = sizeof(struct fuse_out_header) + - sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out); - struct fuse_entry_out entry_payload = DefaultEntryOut(mode & ~new_mask, 2); - struct fuse_open_out out_payload = { - .fh = 1, - .open_flags = open_flags, - }; - iov_out = FuseGenerateIovecs(out_header, entry_payload, out_payload); - SetServerResponse(FUSE_CREATE, iov_out); - - // kernfs generates a successive FUSE_OPEN after the file is created. Linux's - // fuse kernel module will not send this FUSE_OPEN after creat(2). - out_header.len = - sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out); - iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_OPEN, iov_out); - - int fd; - TempUmask mask(new_mask); - EXPECT_THAT(fd = creat(test_file_path.c_str(), mode), SyscallSucceeds()); - EXPECT_THAT(fcntl(fd, F_GETFL), - SyscallSucceedsWithValue(open_flags & O_ACCMODE)); - - struct fuse_in_header in_header; - struct fuse_create_in in_payload; - std::vector<char> name(test_file_name_.size() + 1); - auto iov_in = FuseGenerateIovecs(in_header, in_payload, name); - - // Skip the request of FUSE_LOOKUP. - SkipServerActualRequest(); - - // Get the first FUSE_CREATE. - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload) + - test_file_name_.size() + 1); - EXPECT_EQ(in_header.opcode, FUSE_CREATE); - EXPECT_EQ(in_payload.flags, open_flags); - EXPECT_EQ(in_payload.mode, mode & ~new_mask); - EXPECT_EQ(in_payload.umask, new_mask); - EXPECT_EQ(std::string(name.data()), test_file_name_); - - // Get the successive FUSE_OPEN. - struct fuse_open_in in_payload_open; - iov_in = FuseGenerateIovecs(in_header, in_payload_open); - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload_open)); - EXPECT_EQ(in_header.opcode, FUSE_OPEN); - EXPECT_EQ(in_payload_open.flags, open_flags & O_ACCMODE); - - EXPECT_THAT(close(fd), SyscallSucceeds()); - // Skip the FUSE_RELEASE. - SkipServerActualRequest(); -} - -TEST_F(CreateTest, CreateFileAlreadyExists) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_name_); - - const int open_flags = O_CREAT | O_EXCL; - - SetServerInodeLookup(test_file_name_); - - EXPECT_THAT(open(test_file_path.c_str(), mode, open_flags), - SyscallFailsWithErrno(EEXIST)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/fuse_base.cc b/test/fuse/linux/fuse_base.cc deleted file mode 100644 index 5b45804e1..000000000 --- a/test/fuse/linux/fuse_base.cc +++ /dev/null @@ -1,447 +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 <poll.h> -#include <sys/mount.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/strings/str_format.h" -#include "test/util/fuse_util.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() { - EXPECT_EQ(GetServerNumUnconsumedRequests(), 0); - EXPECT_EQ(GetServerNumUnsentResponses(), 0); - UnmountFuse(); -} - -// Sends 3 parts of data to the FUSE server: -// 1. The `kSetResponse` command -// 2. The expected opcode -// 3. The fake FUSE response -// Then waits for the FUSE server to notify its completion. -void FuseTest::SetServerResponse(uint32_t opcode, - std::vector<struct iovec>& iovecs) { - uint32_t cmd = static_cast<uint32_t>(FuseTestCmd::kSetResponse); - EXPECT_THAT(RetryEINTR(write)(sock_[0], &cmd, sizeof(cmd)), - SyscallSucceedsWithValue(sizeof(cmd))); - - EXPECT_THAT(RetryEINTR(write)(sock_[0], &opcode, sizeof(opcode)), - SyscallSucceedsWithValue(sizeof(opcode))); - - EXPECT_THAT(RetryEINTR(writev)(sock_[0], iovecs.data(), iovecs.size()), - SyscallSucceeds()); - - WaitServerComplete(); -} - -// Waits for the FUSE server to finish its blocking job and check if it -// completes without errors. -void FuseTest::WaitServerComplete() { - uint32_t success; - EXPECT_THAT(RetryEINTR(read)(sock_[0], &success, sizeof(success)), - SyscallSucceedsWithValue(sizeof(success))); - ASSERT_EQ(success, 1); -} - -// Sends the `kGetRequest` command to the FUSE server, then reads the next -// request into iovec struct. The order of calling this function should be -// the same as the one of SetServerResponse(). -void FuseTest::GetServerActualRequest(std::vector<struct iovec>& iovecs) { - uint32_t cmd = static_cast<uint32_t>(FuseTestCmd::kGetRequest); - EXPECT_THAT(RetryEINTR(write)(sock_[0], &cmd, sizeof(cmd)), - SyscallSucceedsWithValue(sizeof(cmd))); - - EXPECT_THAT(RetryEINTR(readv)(sock_[0], iovecs.data(), iovecs.size()), - SyscallSucceeds()); - - WaitServerComplete(); -} - -// Sends a FuseTestCmd command to the FUSE server, reads from the socket, and -// returns the corresponding data. -uint32_t FuseTest::GetServerData(uint32_t cmd) { - uint32_t data; - EXPECT_THAT(RetryEINTR(write)(sock_[0], &cmd, sizeof(cmd)), - SyscallSucceedsWithValue(sizeof(cmd))); - - EXPECT_THAT(RetryEINTR(read)(sock_[0], &data, sizeof(data)), - SyscallSucceedsWithValue(sizeof(data))); - - WaitServerComplete(); - return data; -} - -uint32_t FuseTest::GetServerNumUnconsumedRequests() { - return GetServerData( - static_cast<uint32_t>(FuseTestCmd::kGetNumUnconsumedRequests)); -} - -uint32_t FuseTest::GetServerNumUnsentResponses() { - return GetServerData( - static_cast<uint32_t>(FuseTestCmd::kGetNumUnsentResponses)); -} - -uint32_t FuseTest::GetServerTotalReceivedBytes() { - return GetServerData( - static_cast<uint32_t>(FuseTestCmd::kGetTotalReceivedBytes)); -} - -// Sends the `kSkipRequest` command to the FUSE server, which would skip -// current stored request data. -void FuseTest::SkipServerActualRequest() { - uint32_t cmd = static_cast<uint32_t>(FuseTestCmd::kSkipRequest); - EXPECT_THAT(RetryEINTR(write)(sock_[0], &cmd, sizeof(cmd)), - SyscallSucceedsWithValue(sizeof(cmd))); - - WaitServerComplete(); -} - -// Sends the `kSetInodeLookup` command, expected mode, and the path of the -// inode to create under the mount point. -void FuseTest::SetServerInodeLookup(const std::string& path, mode_t mode, - uint64_t size) { - uint32_t cmd = static_cast<uint32_t>(FuseTestCmd::kSetInodeLookup); - EXPECT_THAT(RetryEINTR(write)(sock_[0], &cmd, sizeof(cmd)), - SyscallSucceedsWithValue(sizeof(cmd))); - - EXPECT_THAT(RetryEINTR(write)(sock_[0], &mode, sizeof(mode)), - SyscallSucceedsWithValue(sizeof(mode))); - - EXPECT_THAT(RetryEINTR(write)(sock_[0], &size, sizeof(size)), - SyscallSucceedsWithValue(sizeof(size))); - - // Pad 1 byte for null-terminate c-string. - EXPECT_THAT(RetryEINTR(write)(sock_[0], path.c_str(), path.size() + 1), - SyscallSucceedsWithValue(path.size() + 1)); - - WaitServerComplete(); -} - -void FuseTest::MountFuse(const char* mountOpts) { - EXPECT_THAT(dev_fd_ = open("/dev/fuse", O_RDWR), SyscallSucceeds()); - - std::string mount_opts = absl::StrFormat("fd=%d,%s", dev_fd_, mountOpts); - 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()), - SyscallSucceeds()); -} - -void FuseTest::UnmountFuse() { - EXPECT_THAT(umount(mount_point_.path().c_str()), SyscallSucceeds()); - // TODO(gvisor.dev/issue/3330): ensure the process is terminated successfully. -} - -// Consumes the first FUSE request and returns the corresponding PosixError. -PosixError FuseTest::ServerConsumeFuseInit( - const struct fuse_init_out* out_payload) { - std::vector<char> buf(FUSE_MIN_READ_BUFFER); - RETURN_ERROR_IF_SYSCALL_FAIL( - RetryEINTR(read)(dev_fd_, buf.data(), buf.size())); - - 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. - auto iov_out = FuseGenerateIovecs( - out_header, *const_cast<struct fuse_init_out*>(out_payload)); - - RETURN_ERROR_IF_SYSCALL_FAIL( - RetryEINTR(writev)(dev_fd_, iov_out.data(), iov_out.size())); - return NoError(); -} - -// Reads 1 expected opcode and a fake response from socket and save them into -// the serial buffer of this testing instance. -void FuseTest::ServerReceiveResponse() { - ssize_t len; - uint32_t opcode; - std::vector<char> buf(FUSE_MIN_READ_BUFFER); - EXPECT_THAT(RetryEINTR(read)(sock_[1], &opcode, sizeof(opcode)), - SyscallSucceedsWithValue(sizeof(opcode))); - - EXPECT_THAT(len = RetryEINTR(read)(sock_[1], buf.data(), buf.size()), - SyscallSucceeds()); - - responses_.AddMemBlock(opcode, buf.data(), len); -} - -// Writes 1 byte of success indicator through socket. -void FuseTest::ServerCompleteWith(bool success) { - uint32_t data = success ? 1 : 0; - ServerSendData(data); -} - -// ServerFuseLoop is the implementation of the fake FUSE server. Monitors 2 -// file descriptors: /dev/fuse and sock_[1]. Events from /dev/fuse are FUSE -// requests and events from sock_[1] are FUSE testing commands, leading by -// a FuseTestCmd data to indicate the command. -void FuseTest::ServerFuseLoop() { - const int nfds = 2; - struct pollfd fds[nfds] = { - { - .fd = dev_fd_, - .events = POLL_IN | POLLHUP | POLLERR | POLLNVAL, - }, - { - .fd = sock_[1], - .events = POLL_IN | POLLHUP | POLLERR | POLLNVAL, - }, - }; - - while (true) { - ASSERT_THAT(poll(fds, nfds, -1), SyscallSucceeds()); - - for (int fd_idx = 0; fd_idx < nfds; ++fd_idx) { - if (fds[fd_idx].revents == 0) continue; - - ASSERT_EQ(fds[fd_idx].revents, POLL_IN); - if (fds[fd_idx].fd == sock_[1]) { - ServerHandleCommand(); - } else if (fds[fd_idx].fd == dev_fd_) { - ServerProcessFuseRequest(); - } - } - } -} - -// SetUpFuseServer creates 1 socketpair and fork the process. The parent thread -// becomes testing thread and the child thread becomes the FUSE server running -// in background. These 2 threads are connected via socketpair. sock_[0] is -// opened in testing thread and sock_[1] is opened in the FUSE server. -void FuseTest::SetUpFuseServer(const struct fuse_init_out* payload) { - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_), SyscallSucceeds()); - - switch (fork()) { - case -1: - GTEST_FAIL(); - return; - case 0: - break; - default: - ASSERT_THAT(close(sock_[1]), SyscallSucceeds()); - WaitServerComplete(); - return; - } - - // Begin child thread, i.e. the FUSE server. - ASSERT_THAT(close(sock_[0]), SyscallSucceeds()); - ServerCompleteWith(ServerConsumeFuseInit(payload).ok()); - ServerFuseLoop(); - _exit(0); -} - -void FuseTest::ServerSendData(uint32_t data) { - EXPECT_THAT(RetryEINTR(write)(sock_[1], &data, sizeof(data)), - SyscallSucceedsWithValue(sizeof(data))); -} - -// Reads FuseTestCmd sent from testing thread and routes to correct handler. -// Since each command should be a blocking operation, a `ServerCompleteWith()` -// is required after the switch keyword. -void FuseTest::ServerHandleCommand() { - uint32_t cmd; - EXPECT_THAT(RetryEINTR(read)(sock_[1], &cmd, sizeof(cmd)), - SyscallSucceedsWithValue(sizeof(cmd))); - - switch (static_cast<FuseTestCmd>(cmd)) { - case FuseTestCmd::kSetResponse: - ServerReceiveResponse(); - break; - case FuseTestCmd::kSetInodeLookup: - ServerReceiveInodeLookup(); - break; - case FuseTestCmd::kGetRequest: - ServerSendReceivedRequest(); - break; - case FuseTestCmd::kGetTotalReceivedBytes: - ServerSendData(static_cast<uint32_t>(requests_.UsedBytes())); - break; - case FuseTestCmd::kGetNumUnconsumedRequests: - ServerSendData(static_cast<uint32_t>(requests_.RemainingBlocks())); - break; - case FuseTestCmd::kGetNumUnsentResponses: - ServerSendData(static_cast<uint32_t>(responses_.RemainingBlocks())); - break; - case FuseTestCmd::kSkipRequest: - ServerSkipReceivedRequest(); - break; - default: - FAIL() << "Unknown FuseTestCmd " << cmd; - break; - } - - ServerCompleteWith(!HasFailure()); -} - -// Reads the expected file mode and the path of one file. Crafts a basic -// `fuse_entry_out` memory block and inserts into a map for future use. -// The FUSE server will always return this response if a FUSE_LOOKUP -// request with this specific path comes in. -void FuseTest::ServerReceiveInodeLookup() { - mode_t mode; - uint64_t size; - std::vector<char> buf(FUSE_MIN_READ_BUFFER); - - EXPECT_THAT(RetryEINTR(read)(sock_[1], &mode, sizeof(mode)), - SyscallSucceedsWithValue(sizeof(mode))); - - EXPECT_THAT(RetryEINTR(read)(sock_[1], &size, sizeof(size)), - SyscallSucceedsWithValue(sizeof(size))); - - EXPECT_THAT(RetryEINTR(read)(sock_[1], buf.data(), buf.size()), - SyscallSucceeds()); - - std::string path(buf.data()); - - uint32_t out_len = - sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out); - struct fuse_out_header out_header = { - .len = out_len, - .error = 0, - }; - struct fuse_entry_out out_payload = DefaultEntryOut(mode, nodeid_); - // Since this is only used in test, nodeid_ is simply increased by 1 to - // comply with the unqiueness of different path. - ++nodeid_; - - // Set the size. - out_payload.attr.size = size; - - memcpy(buf.data(), &out_header, sizeof(out_header)); - memcpy(buf.data() + sizeof(out_header), &out_payload, sizeof(out_payload)); - lookups_.AddMemBlock(FUSE_LOOKUP, buf.data(), out_len); - lookup_map_[path] = lookups_.Next(); -} - -// Sends the received request pointed by current cursor and advances cursor. -void FuseTest::ServerSendReceivedRequest() { - if (requests_.End()) { - FAIL() << "No more received request."; - return; - } - auto mem_block = requests_.Next(); - EXPECT_THAT( - RetryEINTR(write)(sock_[1], requests_.DataAtOffset(mem_block.offset), - mem_block.len), - SyscallSucceedsWithValue(mem_block.len)); -} - -// Skip the request pointed by current cursor. -void FuseTest::ServerSkipReceivedRequest() { - if (requests_.End()) { - FAIL() << "No more received request."; - return; - } - requests_.Next(); -} - -// Handles FUSE request. Reads request from /dev/fuse, checks if it has the -// same opcode as expected, and responds with the saved fake FUSE response. -// The FUSE request is copied to the serial buffer and can be retrieved one- -// by-one by calling GetServerActualRequest from testing thread. -void FuseTest::ServerProcessFuseRequest() { - ssize_t len; - std::vector<char> buf(FUSE_MIN_READ_BUFFER); - - // Read FUSE request. - EXPECT_THAT(len = RetryEINTR(read)(dev_fd_, buf.data(), buf.size()), - SyscallSucceeds()); - fuse_in_header* in_header = reinterpret_cast<fuse_in_header*>(buf.data()); - - // Check if this is a preset FUSE_LOOKUP path. - if (in_header->opcode == FUSE_LOOKUP) { - std::string path(buf.data() + sizeof(struct fuse_in_header)); - auto it = lookup_map_.find(path); - if (it != lookup_map_.end()) { - // Matches a preset path. Reply with fake data and skip saving the - // request. - ServerRespondFuseSuccess(lookups_, it->second, in_header->unique); - return; - } - } - - requests_.AddMemBlock(in_header->opcode, buf.data(), len); - - if (in_header->opcode == FUSE_RELEASE || in_header->opcode == FUSE_RELEASEDIR) - return; - // Check if there is a corresponding response. - if (responses_.End()) { - GTEST_NONFATAL_FAILURE_("No more FUSE response is expected"); - ServerRespondFuseError(in_header->unique); - return; - } - auto mem_block = responses_.Next(); - if (in_header->opcode != mem_block.opcode) { - std::string message = absl::StrFormat("Expect opcode %d but got %d", - mem_block.opcode, in_header->opcode); - GTEST_NONFATAL_FAILURE_(message.c_str()); - // We won't get correct response if opcode is not expected. Send error - // response here to avoid wrong parsing by VFS. - ServerRespondFuseError(in_header->unique); - return; - } - - // Write FUSE response. - ServerRespondFuseSuccess(responses_, mem_block, in_header->unique); -} - -void FuseTest::ServerRespondFuseSuccess(FuseMemBuffer& mem_buf, - const FuseMemBlock& block, - uint64_t unique) { - fuse_out_header* out_header = - reinterpret_cast<fuse_out_header*>(mem_buf.DataAtOffset(block.offset)); - - // Patch `unique` in fuse_out_header to avoid EINVAL caused by responding - // with an unknown `unique`. - out_header->unique = unique; - EXPECT_THAT(RetryEINTR(write)(dev_fd_, out_header, block.len), - SyscallSucceedsWithValue(block.len)); -} - -void FuseTest::ServerRespondFuseError(uint64_t unique) { - fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header), - .error = ENOSYS, - .unique = unique, - }; - EXPECT_THAT(RetryEINTR(write)(dev_fd_, &out_header, sizeof(out_header)), - SyscallSucceedsWithValue(sizeof(out_header))); -} - -} // 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 6ad296ca2..000000000 --- a/test/fuse/linux/fuse_base.h +++ /dev/null @@ -1,251 +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 <string.h> -#include <sys/stat.h> -#include <sys/uio.h> - -#include <iostream> -#include <unordered_map> -#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"; - -constexpr struct fuse_init_out kDefaultFUSEInitOutPayload = {.major = 7}; - -// Internal commands used to communicate between testing thread and the FUSE -// server. See test/fuse/README.md for further detail. -enum class FuseTestCmd { - kSetResponse = 0, - kSetInodeLookup, - kGetRequest, - kGetNumUnconsumedRequests, - kGetNumUnsentResponses, - kGetTotalReceivedBytes, - kSkipRequest, -}; - -// Holds the information of a memory block in a serial buffer. -struct FuseMemBlock { - uint32_t opcode; - size_t offset; - size_t len; -}; - -// A wrapper of a simple serial buffer that can be used with read(2) and -// write(2). Contains a cursor to indicate accessing. This class is not thread- -// safe and can only be used in single-thread version. -class FuseMemBuffer { - public: - FuseMemBuffer() : cursor_(0) { - // To read from /dev/fuse, a buffer needs at least FUSE_MIN_READ_BUFFER - // bytes to avoid EINVAL. FuseMemBuffer holds memory that can accommodate - // a sequence of FUSE request/response, so it is initiated with double - // minimal requirement. - mem_.resize(FUSE_MIN_READ_BUFFER * 2); - } - - // Returns whether there is no memory block. - bool Empty() { return blocks_.empty(); } - - // Returns if there is no more remaining memory blocks. - bool End() { return cursor_ == blocks_.size(); } - - // Returns how many bytes that have been received. - size_t UsedBytes() { - return Empty() ? 0 : blocks_.back().offset + blocks_.back().len; - } - - // Returns the available bytes remains in the serial buffer. - size_t AvailBytes() { return mem_.size() - UsedBytes(); } - - // Appends a memory block information that starts at the tail of the serial - // buffer. /dev/fuse requires at least FUSE_MIN_READ_BUFFER bytes to read, or - // it will issue EINVAL. If it is not enough, just double the buffer length. - void AddMemBlock(uint32_t opcode, void* data, size_t len) { - if (AvailBytes() < FUSE_MIN_READ_BUFFER) { - mem_.resize(mem_.size() << 1); - } - size_t offset = UsedBytes(); - memcpy(mem_.data() + offset, data, len); - blocks_.push_back(FuseMemBlock{opcode, offset, len}); - } - - // Returns the memory address at a specific offset. Used with read(2) or - // write(2). - char* DataAtOffset(size_t offset) { return mem_.data() + offset; } - - // Returns current memory block pointed by the cursor and increase by 1. - FuseMemBlock Next() { - if (End()) { - std::cerr << "Buffer is already exhausted." << std::endl; - return FuseMemBlock{}; - } - return blocks_[cursor_++]; - } - - // Returns the number of the blocks that has not been requested. - size_t RemainingBlocks() { return blocks_.size() - cursor_; } - - private: - size_t cursor_; - std::vector<FuseMemBlock> blocks_; - std::vector<char> mem_; -}; - -// FuseTest base class is useful in FUSE integration test. Inherit this class -// to automatically set up a fake FUSE server and use the member functions -// to manipulate with it. Refer to test/fuse/README.md for detailed explanation. -class FuseTest : public ::testing::Test { - public: - // nodeid_ is the ID of a fake inode. We starts from 2 since 1 is occupied by - // the mount point. - FuseTest() : nodeid_(2) {} - void SetUp() override; - void TearDown() override; - - // Called by the testing thread to set up a fake response for an expected - // opcode via socket. This can be used multiple times to define a sequence of - // expected FUSE reactions. - void SetServerResponse(uint32_t opcode, std::vector<struct iovec>& iovecs); - - // Called by the testing thread to install a fake path under the mount point. - // e.g. a file under /mnt/dir/file and moint point is /mnt, then it will look - // up "dir/file" in this case. - // - // It sets a fixed response to the FUSE_LOOKUP requests issued with this - // path, pretending there is an inode and avoid ENOENT when testing. If mode - // is not given, it creates a regular file with mode 0600. - void SetServerInodeLookup(const std::string& path, - mode_t mode = S_IFREG | S_IRUSR | S_IWUSR, - uint64_t size = 512); - - // Called by the testing thread to ask the FUSE server for its next received - // FUSE request. Be sure to use the corresponding struct of iovec to receive - // data from server. - void GetServerActualRequest(std::vector<struct iovec>& iovecs); - - // Called by the testing thread to query the number of unconsumed requests in - // the requests_ serial buffer of the FUSE server. TearDown() ensures all - // FUSE requests received by the FUSE server were consumed by the testing - // thread. - uint32_t GetServerNumUnconsumedRequests(); - - // Called by the testing thread to query the number of unsent responses in - // the responses_ serial buffer of the FUSE server. TearDown() ensures all - // preset FUSE responses were sent out by the FUSE server. - uint32_t GetServerNumUnsentResponses(); - - // Called by the testing thread to ask the FUSE server for its total received - // bytes from /dev/fuse. - uint32_t GetServerTotalReceivedBytes(); - - // Called by the testing thread to ask the FUSE server to skip stored - // request data. - void SkipServerActualRequest(); - - protected: - TempPath mount_point_; - - // Opens /dev/fuse and inherit the file descriptor for the FUSE server. - void MountFuse(const char* mountOpts = kMountOpts); - - // Creates a socketpair for communication and forks FUSE server. - void SetUpFuseServer( - const struct fuse_init_out* payload = &kDefaultFUSEInitOutPayload); - - // Unmounts the mountpoint of the FUSE server. - void UnmountFuse(); - - private: - // Sends a FuseTestCmd and gets a uint32_t data from the FUSE server. - inline uint32_t GetServerData(uint32_t cmd); - - // Waits for FUSE server to complete its processing. Complains if the FUSE - // server responds any failure during tests. - void WaitServerComplete(); - - // The FUSE server stays here and waits next command or FUSE request until it - // is terminated. - void ServerFuseLoop(); - - // Used by the FUSE server to tell testing thread if it is OK to proceed next - // command. Will be issued after processing each FuseTestCmd. - void ServerCompleteWith(bool success); - - // Consumes the first FUSE request when mounting FUSE. Replies with a - // response with empty payload. - PosixError ServerConsumeFuseInit(const struct fuse_init_out* payload); - - // A command switch that dispatch different FuseTestCmd to its handler. - void ServerHandleCommand(); - - // The FUSE server side's corresponding code of `SetServerResponse()`. - // Handles `kSetResponse` command. Saves the fake response into its output - // memory queue. - void ServerReceiveResponse(); - - // The FUSE server side's corresponding code of `SetServerInodeLookup()`. - // Handles `kSetInodeLookup` command. Receives an expected file mode and - // file path under the mount point. - void ServerReceiveInodeLookup(); - - // The FUSE server side's corresponding code of `GetServerActualRequest()`. - // Handles `kGetRequest` command. Sends the next received request pointed by - // the cursor. - void ServerSendReceivedRequest(); - - // Sends a uint32_t data via socket. - inline void ServerSendData(uint32_t data); - - // The FUSE server side's corresponding code of `SkipServerActualRequest()`. - // Handles `kSkipRequest` command. Skip the request pointed by current cursor. - void ServerSkipReceivedRequest(); - - // Handles FUSE request sent to /dev/fuse by its saved responses. - void ServerProcessFuseRequest(); - - // Responds to FUSE request with a saved data. - void ServerRespondFuseSuccess(FuseMemBuffer& mem_buf, - const FuseMemBlock& block, uint64_t unique); - - // Responds an error header to /dev/fuse when bad thing happens. - void ServerRespondFuseError(uint64_t unique); - - int dev_fd_; - int sock_[2]; - - uint64_t nodeid_; - std::unordered_map<std::string, FuseMemBlock> lookup_map_; - - FuseMemBuffer requests_; - FuseMemBuffer responses_; - FuseMemBuffer lookups_; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_FUSE_FUSE_BASE_H_ diff --git a/test/fuse/linux/fuse_fd_util.cc b/test/fuse/linux/fuse_fd_util.cc deleted file mode 100644 index 30d1157bb..000000000 --- a/test/fuse/linux/fuse_fd_util.cc +++ /dev/null @@ -1,61 +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_fd_util.h" - -#include <fcntl.h> -#include <linux/fuse.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/uio.h> - -#include <string> -#include <vector> - -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fuse_util.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<FileDescriptor> FuseFdTest::OpenPath(const std::string &path, - uint32_t flags, uint64_t fh) { - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), - }; - struct fuse_open_out out_payload = { - .fh = fh, - .open_flags = flags, - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_OPEN, iov_out); - - auto res = Open(path.c_str(), flags); - if (res.ok()) { - SkipServerActualRequest(); - } - return res; -} - -Cleanup FuseFdTest::CloseFD(FileDescriptor &fd) { - return Cleanup([&] { - close(fd.release()); - SkipServerActualRequest(); - }); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/fuse_fd_util.h b/test/fuse/linux/fuse_fd_util.h deleted file mode 100644 index 066185c94..000000000 --- a/test/fuse/linux/fuse_fd_util.h +++ /dev/null @@ -1,48 +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_FD_UTIL_H_ -#define GVISOR_TEST_FUSE_FUSE_FD_UTIL_H_ - -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <string> - -#include "test/fuse/linux/fuse_base.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -class FuseFdTest : public FuseTest { - public: - // Sets the FUSE server to respond to a FUSE_OPEN with corresponding flags and - // fh. Then does a real file system open on the absolute path to get an fd. - PosixErrorOr<FileDescriptor> OpenPath(const std::string &path, - uint32_t flags = O_RDONLY, - uint64_t fh = 1); - - // Returns a cleanup object that closes the fd when it is destroyed. After - // the close is done, tells the FUSE server to skip this FUSE_RELEASE. - Cleanup CloseFD(FileDescriptor &fd); -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_FUSE_FUSE_FD_UTIL_H_ diff --git a/test/fuse/linux/mkdir_test.cc b/test/fuse/linux/mkdir_test.cc deleted file mode 100644 index 9647cb93f..000000000 --- a/test/fuse/linux/mkdir_test.cc +++ /dev/null @@ -1,88 +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 <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/temp_umask.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class MkdirTest : public FuseTest { - protected: - const std::string test_dir_ = "test_dir"; - const mode_t perms_ = S_IRWXU | S_IRWXG | S_IRWXO; -}; - -TEST_F(MkdirTest, CreateDir) { - const std::string test_dir_path_ = - JoinPath(mount_point_.path().c_str(), test_dir_); - const mode_t new_umask = 0077; - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), - }; - struct fuse_entry_out out_payload = DefaultEntryOut(S_IFDIR | perms_, 5); - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_MKDIR, iov_out); - TempUmask mask(new_umask); - ASSERT_THAT(mkdir(test_dir_path_.c_str(), 0777), SyscallSucceeds()); - - struct fuse_in_header in_header; - struct fuse_mkdir_in in_payload; - std::vector<char> actual_dir(test_dir_.length() + 1); - auto iov_in = FuseGenerateIovecs(in_header, in_payload, actual_dir); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, - sizeof(in_header) + sizeof(in_payload) + test_dir_.length() + 1); - EXPECT_EQ(in_header.opcode, FUSE_MKDIR); - EXPECT_EQ(in_payload.mode & 0777, perms_ & ~new_umask); - EXPECT_EQ(in_payload.umask, new_umask); - EXPECT_EQ(std::string(actual_dir.data()), test_dir_); -} - -TEST_F(MkdirTest, FileTypeError) { - const std::string test_dir_path_ = - JoinPath(mount_point_.path().c_str(), test_dir_); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), - }; - struct fuse_entry_out out_payload = DefaultEntryOut(S_IFREG | perms_, 5); - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_MKDIR, iov_out); - ASSERT_THAT(mkdir(test_dir_path_.c_str(), 0777), SyscallFailsWithErrno(EIO)); - SkipServerActualRequest(); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/mknod_test.cc b/test/fuse/linux/mknod_test.cc deleted file mode 100644 index 74c74d76b..000000000 --- a/test/fuse/linux/mknod_test.cc +++ /dev/null @@ -1,107 +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 <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/temp_umask.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class MknodTest : public FuseTest { - protected: - const std::string test_file_ = "test_file"; - const mode_t perms_ = S_IRWXU | S_IRWXG | S_IRWXO; -}; - -TEST_F(MknodTest, RegularFile) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - const mode_t new_umask = 0077; - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), - }; - struct fuse_entry_out out_payload = DefaultEntryOut(S_IFREG | perms_, 5); - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_MKNOD, iov_out); - TempUmask mask(new_umask); - ASSERT_THAT(mknod(test_file_path.c_str(), perms_, 0), SyscallSucceeds()); - - struct fuse_in_header in_header; - struct fuse_mknod_in in_payload; - std::vector<char> actual_file(test_file_.length() + 1); - auto iov_in = FuseGenerateIovecs(in_header, in_payload, actual_file); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, - sizeof(in_header) + sizeof(in_payload) + test_file_.length() + 1); - EXPECT_EQ(in_header.opcode, FUSE_MKNOD); - EXPECT_EQ(in_payload.mode & 0777, perms_ & ~new_umask); - EXPECT_EQ(in_payload.umask, new_umask); - EXPECT_EQ(in_payload.rdev, 0); - EXPECT_EQ(std::string(actual_file.data()), test_file_); -} - -TEST_F(MknodTest, FileTypeError) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), - }; - // server return directory instead of regular file should cause an error. - struct fuse_entry_out out_payload = DefaultEntryOut(S_IFDIR | perms_, 5); - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_MKNOD, iov_out); - ASSERT_THAT(mknod(test_file_path.c_str(), perms_, 0), - SyscallFailsWithErrno(EIO)); - SkipServerActualRequest(); -} - -TEST_F(MknodTest, NodeIDError) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), - }; - struct fuse_entry_out out_payload = - DefaultEntryOut(S_IFREG | perms_, FUSE_ROOT_ID); - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_MKNOD, iov_out); - ASSERT_THAT(mknod(test_file_path.c_str(), perms_, 0), - SyscallFailsWithErrno(EIO)); - SkipServerActualRequest(); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/open_test.cc b/test/fuse/linux/open_test.cc deleted file mode 100644 index 4b0c4a805..000000000 --- a/test/fuse/linux/open_test.cc +++ /dev/null @@ -1,128 +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 <string> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class OpenTest : public FuseTest { - // OpenTest doesn't care the release request when close a fd, - // so doesn't check leftover requests when tearing down. - void TearDown() { UnmountFuse(); } - - protected: - const std::string test_file_ = "test_file"; - const mode_t regular_file_ = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; - - struct fuse_open_out out_payload_ = { - .fh = 1, - .open_flags = O_RDWR, - }; -}; - -TEST_F(OpenTest, RegularFile) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - SetServerInodeLookup(test_file_, regular_file_); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload_); - SetServerResponse(FUSE_OPEN, iov_out); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_path.c_str(), O_RDWR)); - - struct fuse_in_header in_header; - struct fuse_open_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_OPEN); - EXPECT_EQ(in_payload.flags, O_RDWR); - EXPECT_THAT(fcntl(fd.get(), F_GETFL), SyscallSucceedsWithValue(O_RDWR)); -} - -TEST_F(OpenTest, SetNoOpen) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - SetServerInodeLookup(test_file_, regular_file_); - - // ENOSYS indicates open is not implemented. - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), - .error = -ENOSYS, - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload_); - SetServerResponse(FUSE_OPEN, iov_out); - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_path.c_str(), O_RDWR)); - SkipServerActualRequest(); - - // check open doesn't send new request. - uint32_t recieved_before = GetServerTotalReceivedBytes(); - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_path.c_str(), O_RDWR)); - EXPECT_EQ(GetServerTotalReceivedBytes(), recieved_before); -} - -TEST_F(OpenTest, OpenFail) { - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), - .error = -ENOENT, - }; - - auto iov_out = FuseGenerateIovecs(out_header, out_payload_); - SetServerResponse(FUSE_OPENDIR, iov_out); - ASSERT_THAT(open(mount_point_.path().c_str(), O_RDWR), - SyscallFailsWithErrno(ENOENT)); - - struct fuse_in_header in_header; - struct fuse_open_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_OPENDIR); - EXPECT_EQ(in_payload.flags, O_RDWR); -} - -TEST_F(OpenTest, DirectoryFlagOnRegularFile) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - - SetServerInodeLookup(test_file_, regular_file_); - ASSERT_THAT(open(test_file_path.c_str(), O_RDWR | O_DIRECTORY), - SyscallFailsWithErrno(ENOTDIR)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/read_test.cc b/test/fuse/linux/read_test.cc deleted file mode 100644 index 88fc299d8..000000000 --- a/test/fuse/linux/read_test.cc +++ /dev/null @@ -1,390 +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 <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class ReadTest : public FuseTest { - void SetUp() override { - FuseTest::SetUp(); - test_file_path_ = JoinPath(mount_point_.path().c_str(), test_file_); - } - - // TearDown overrides the parent's function - // to skip checking the unconsumed release request at the end. - void TearDown() override { UnmountFuse(); } - - protected: - const std::string test_file_ = "test_file"; - const mode_t test_file_mode_ = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; - const uint64_t test_fh_ = 1; - const uint32_t open_flag_ = O_RDWR; - - std::string test_file_path_; - - PosixErrorOr<FileDescriptor> OpenTestFile(const std::string &path, - uint64_t size = 512) { - SetServerInodeLookup(test_file_, test_file_mode_, size); - - struct fuse_out_header out_header_open = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), - }; - struct fuse_open_out out_payload_open = { - .fh = test_fh_, - .open_flags = open_flag_, - }; - auto iov_out_open = FuseGenerateIovecs(out_header_open, out_payload_open); - SetServerResponse(FUSE_OPEN, iov_out_open); - - auto res = Open(path.c_str(), open_flag_); - if (res.ok()) { - SkipServerActualRequest(); - } - return res; - } -}; - -class ReadTestSmallMaxRead : public ReadTest { - void SetUp() override { - MountFuse(mountOpts); - SetUpFuseServer(); - test_file_path_ = JoinPath(mount_point_.path().c_str(), test_file_); - } - - protected: - constexpr static char mountOpts[] = - "rootmode=755,user_id=0,group_id=0,max_read=4096"; - // 4096 is hard-coded as the max_read in mount options. - const int size_fragment = 4096; -}; - -TEST_F(ReadTest, ReadWhole) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Prepare for the read. - const int n_read = 5; - std::vector<char> data(n_read); - RandomizeBuffer(data.data(), data.size()); - struct fuse_out_header out_header_read = { - .len = - static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()), - }; - auto iov_out_read = FuseGenerateIovecs(out_header_read, data); - SetServerResponse(FUSE_READ, iov_out_read); - - // Read the whole "file". - std::vector<char> buf(n_read); - EXPECT_THAT(read(fd.get(), buf.data(), n_read), - SyscallSucceedsWithValue(n_read)); - - // Check the read request. - struct fuse_in_header in_header_read; - struct fuse_read_in in_payload_read; - auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_header_read.len, - sizeof(in_header_read) + sizeof(in_payload_read)); - EXPECT_EQ(in_header_read.opcode, FUSE_READ); - EXPECT_EQ(in_payload_read.offset, 0); - EXPECT_EQ(buf, data); -} - -TEST_F(ReadTest, ReadPartial) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Prepare for the read. - const int n_data = 10; - std::vector<char> data(n_data); - RandomizeBuffer(data.data(), data.size()); - // Note: due to read ahead, current read implementation will treat any - // response that is longer than requested as correct (i.e. not reach the EOF). - // Therefore, the test below should make sure the size to read does not exceed - // n_data. - struct fuse_out_header out_header_read = { - .len = - static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()), - }; - auto iov_out_read = FuseGenerateIovecs(out_header_read, data); - struct fuse_in_header in_header_read; - struct fuse_read_in in_payload_read; - auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read); - - std::vector<char> buf(n_data); - - // Read 1 bytes. - SetServerResponse(FUSE_READ, iov_out_read); - EXPECT_THAT(read(fd.get(), buf.data(), 1), SyscallSucceedsWithValue(1)); - - // Check the 1-byte read request. - GetServerActualRequest(iov_in); - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_header_read.len, - sizeof(in_header_read) + sizeof(in_payload_read)); - EXPECT_EQ(in_header_read.opcode, FUSE_READ); - EXPECT_EQ(in_payload_read.offset, 0); - - // Read 3 bytes. - SetServerResponse(FUSE_READ, iov_out_read); - EXPECT_THAT(read(fd.get(), buf.data(), 3), SyscallSucceedsWithValue(3)); - - // Check the 3-byte read request. - GetServerActualRequest(iov_in); - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_payload_read.offset, 1); - - // Read 5 bytes. - SetServerResponse(FUSE_READ, iov_out_read); - EXPECT_THAT(read(fd.get(), buf.data(), 5), SyscallSucceedsWithValue(5)); - - // Check the 5-byte read request. - GetServerActualRequest(iov_in); - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_payload_read.offset, 4); -} - -TEST_F(ReadTest, PRead) { - const int file_size = 512; - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, file_size)); - - // Prepare for the read. - const int n_read = 5; - std::vector<char> data(n_read); - RandomizeBuffer(data.data(), data.size()); - struct fuse_out_header out_header_read = { - .len = - static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()), - }; - auto iov_out_read = FuseGenerateIovecs(out_header_read, data); - SetServerResponse(FUSE_READ, iov_out_read); - - // Read some bytes. - std::vector<char> buf(n_read); - const int offset_read = file_size >> 1; - EXPECT_THAT(pread(fd.get(), buf.data(), n_read, offset_read), - SyscallSucceedsWithValue(n_read)); - - // Check the read request. - struct fuse_in_header in_header_read; - struct fuse_read_in in_payload_read; - auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_header_read.len, - sizeof(in_header_read) + sizeof(in_payload_read)); - EXPECT_EQ(in_header_read.opcode, FUSE_READ); - EXPECT_EQ(in_payload_read.offset, offset_read); - EXPECT_EQ(buf, data); -} - -TEST_F(ReadTest, ReadZero) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Issue the read. - std::vector<char> buf; - EXPECT_THAT(read(fd.get(), buf.data(), 0), SyscallSucceedsWithValue(0)); -} - -TEST_F(ReadTest, ReadShort) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Prepare for the short read. - const int n_read = 5; - std::vector<char> data(n_read >> 1); - RandomizeBuffer(data.data(), data.size()); - struct fuse_out_header out_header_read = { - .len = - static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()), - }; - auto iov_out_read = FuseGenerateIovecs(out_header_read, data); - SetServerResponse(FUSE_READ, iov_out_read); - - // Read the whole "file". - std::vector<char> buf(n_read); - EXPECT_THAT(read(fd.get(), buf.data(), n_read), - SyscallSucceedsWithValue(data.size())); - - // Check the read request. - struct fuse_in_header in_header_read; - struct fuse_read_in in_payload_read; - auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_header_read.len, - sizeof(in_header_read) + sizeof(in_payload_read)); - EXPECT_EQ(in_header_read.opcode, FUSE_READ); - EXPECT_EQ(in_payload_read.offset, 0); - std::vector<char> short_buf(buf.begin(), buf.begin() + data.size()); - EXPECT_EQ(short_buf, data); -} - -TEST_F(ReadTest, ReadShortEOF) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Prepare for the short read. - struct fuse_out_header out_header_read = { - .len = static_cast<uint32_t>(sizeof(struct fuse_out_header)), - }; - auto iov_out_read = FuseGenerateIovecs(out_header_read); - SetServerResponse(FUSE_READ, iov_out_read); - - // Read the whole "file". - const int n_read = 10; - std::vector<char> buf(n_read); - EXPECT_THAT(read(fd.get(), buf.data(), n_read), SyscallSucceedsWithValue(0)); - - // Check the read request. - struct fuse_in_header in_header_read; - struct fuse_read_in in_payload_read; - auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_header_read.len, - sizeof(in_header_read) + sizeof(in_payload_read)); - EXPECT_EQ(in_header_read.opcode, FUSE_READ); - EXPECT_EQ(in_payload_read.offset, 0); -} - -TEST_F(ReadTestSmallMaxRead, ReadSmallMaxRead) { - const int n_fragment = 10; - const int n_read = size_fragment * n_fragment; - - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, n_read)); - - // Prepare for the read. - std::vector<char> data(size_fragment); - RandomizeBuffer(data.data(), data.size()); - struct fuse_out_header out_header_read = { - .len = - static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()), - }; - auto iov_out_read = FuseGenerateIovecs(out_header_read, data); - - for (int i = 0; i < n_fragment; ++i) { - SetServerResponse(FUSE_READ, iov_out_read); - } - - // Read the whole "file". - std::vector<char> buf(n_read); - EXPECT_THAT(read(fd.get(), buf.data(), n_read), - SyscallSucceedsWithValue(n_read)); - - ASSERT_EQ(GetServerNumUnsentResponses(), 0); - ASSERT_EQ(GetServerNumUnconsumedRequests(), n_fragment); - - // Check each read segment. - struct fuse_in_header in_header_read; - struct fuse_read_in in_payload_read; - auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read); - - for (int i = 0; i < n_fragment; ++i) { - GetServerActualRequest(iov_in); - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_header_read.len, - sizeof(in_header_read) + sizeof(in_payload_read)); - EXPECT_EQ(in_header_read.opcode, FUSE_READ); - EXPECT_EQ(in_payload_read.offset, i * size_fragment); - EXPECT_EQ(in_payload_read.size, size_fragment); - - auto it = buf.begin() + i * size_fragment; - EXPECT_EQ(std::vector<char>(it, it + size_fragment), data); - } -} - -TEST_F(ReadTestSmallMaxRead, ReadSmallMaxReadShort) { - const int n_fragment = 10; - const int n_read = size_fragment * n_fragment; - - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, n_read)); - - // Prepare for the read. - std::vector<char> data(size_fragment); - RandomizeBuffer(data.data(), data.size()); - struct fuse_out_header out_header_read = { - .len = - static_cast<uint32_t>(sizeof(struct fuse_out_header) + data.size()), - }; - auto iov_out_read = FuseGenerateIovecs(out_header_read, data); - - for (int i = 0; i < n_fragment - 1; ++i) { - SetServerResponse(FUSE_READ, iov_out_read); - } - - // The last fragment is a short read. - std::vector<char> half_data(data.begin(), data.begin() + (data.size() >> 1)); - struct fuse_out_header out_header_read_short = { - .len = static_cast<uint32_t>(sizeof(struct fuse_out_header) + - half_data.size()), - }; - auto iov_out_read_short = - FuseGenerateIovecs(out_header_read_short, half_data); - SetServerResponse(FUSE_READ, iov_out_read_short); - - // Read the whole "file". - std::vector<char> buf(n_read); - EXPECT_THAT(read(fd.get(), buf.data(), n_read), - SyscallSucceedsWithValue(n_read - (data.size() >> 1))); - - ASSERT_EQ(GetServerNumUnsentResponses(), 0); - ASSERT_EQ(GetServerNumUnconsumedRequests(), n_fragment); - - // Check each read segment. - struct fuse_in_header in_header_read; - struct fuse_read_in in_payload_read; - auto iov_in = FuseGenerateIovecs(in_header_read, in_payload_read); - - for (int i = 0; i < n_fragment; ++i) { - GetServerActualRequest(iov_in); - EXPECT_EQ(in_payload_read.fh, test_fh_); - EXPECT_EQ(in_header_read.len, - sizeof(in_header_read) + sizeof(in_payload_read)); - EXPECT_EQ(in_header_read.opcode, FUSE_READ); - EXPECT_EQ(in_payload_read.offset, i * size_fragment); - EXPECT_EQ(in_payload_read.size, size_fragment); - - auto it = buf.begin() + i * size_fragment; - if (i != n_fragment - 1) { - EXPECT_EQ(std::vector<char>(it, it + data.size()), data); - } else { - EXPECT_EQ(std::vector<char>(it, it + half_data.size()), half_data); - } - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/readdir_test.cc b/test/fuse/linux/readdir_test.cc deleted file mode 100644 index 2afb4b062..000000000 --- a/test/fuse/linux/readdir_test.cc +++ /dev/null @@ -1,193 +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 <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <linux/fuse.h> -#include <linux/unistd.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include <string> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) -#define FUSE_DIRENT_ALIGN(x) \ - (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) -#define FUSE_DIRENT_SIZE(d) FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) - -namespace gvisor { -namespace testing { - -namespace { - -class ReaddirTest : public FuseTest { - public: - void fill_fuse_dirent(char *buf, const char *name, uint64_t ino) { - size_t namelen = strlen(name); - size_t entlen = FUSE_NAME_OFFSET + namelen; - size_t entlen_padded = FUSE_DIRENT_ALIGN(entlen); - struct fuse_dirent *dirent; - - dirent = reinterpret_cast<struct fuse_dirent *>(buf); - dirent->ino = ino; - dirent->namelen = namelen; - memcpy(dirent->name, name, namelen); - memset(dirent->name + namelen, 0, entlen_padded - entlen); - } - - protected: - const std::string test_dir_name_ = "test_dir"; -}; - -TEST_F(ReaddirTest, SingleEntry) { - const std::string test_dir_path = - JoinPath(mount_point_.path().c_str(), test_dir_name_); - - const uint64_t ino_dir = 1024; - // We need to make sure the test dir is a directory that can be found. - mode_t expected_mode = - S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - struct fuse_attr dir_attr = { - .ino = ino_dir, - .size = 512, - .blocks = 4, - .mode = expected_mode, - .blksize = 4096, - }; - - // We need to make sure the test dir is a directory that can be found. - struct fuse_out_header lookup_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), - }; - struct fuse_entry_out lookup_payload = { - .nodeid = 1, - .entry_valid = true, - .attr_valid = true, - .attr = dir_attr, - }; - - struct fuse_out_header open_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), - }; - struct fuse_open_out open_payload = { - .fh = 1, - }; - auto iov_out = FuseGenerateIovecs(lookup_header, lookup_payload); - SetServerResponse(FUSE_LOOKUP, iov_out); - - iov_out = FuseGenerateIovecs(open_header, open_payload); - SetServerResponse(FUSE_OPENDIR, iov_out); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_dir_path.c_str(), O_RDONLY)); - - // The open command makes two syscalls. Lookup the dir file and open. - // We don't need to inspect those headers in this test. - SkipServerActualRequest(); // LOOKUP. - SkipServerActualRequest(); // OPENDIR. - - // Readdir test code. - std::string dot = "."; - std::string dot_dot = ".."; - std::string test_file = "testFile"; - - // Figure out how many dirents to send over and allocate them appropriately. - // Each dirent has a dynamic name and a static metadata part. The dirent size - // is aligned to being a multiple of 8. - size_t dot_file_dirent_size = - FUSE_DIRENT_ALIGN(dot.length() + FUSE_NAME_OFFSET); - size_t dot_dot_file_dirent_size = - FUSE_DIRENT_ALIGN(dot_dot.length() + FUSE_NAME_OFFSET); - size_t test_file_dirent_size = - FUSE_DIRENT_ALIGN(test_file.length() + FUSE_NAME_OFFSET); - - // Create an appropriately sized payload. - size_t readdir_payload_size = - test_file_dirent_size + dot_file_dirent_size + dot_dot_file_dirent_size; - std::vector<char> readdir_payload_vec(readdir_payload_size); - char *readdir_payload = readdir_payload_vec.data(); - - // Use fake ino for other directories. - fill_fuse_dirent(readdir_payload, dot.c_str(), ino_dir - 2); - fill_fuse_dirent(readdir_payload + dot_file_dirent_size, dot_dot.c_str(), - ino_dir - 1); - fill_fuse_dirent( - readdir_payload + dot_file_dirent_size + dot_dot_file_dirent_size, - test_file.c_str(), ino_dir); - - struct fuse_out_header readdir_header = { - .len = uint32_t(sizeof(struct fuse_out_header) + readdir_payload_size), - }; - struct fuse_out_header readdir_header_break = { - .len = uint32_t(sizeof(struct fuse_out_header)), - }; - - iov_out = FuseGenerateIovecs(readdir_header, readdir_payload_vec); - SetServerResponse(FUSE_READDIR, iov_out); - - iov_out = FuseGenerateIovecs(readdir_header_break); - SetServerResponse(FUSE_READDIR, iov_out); - - std::vector<char> buf(4090, 0); - int nread, off = 0, i = 0; - EXPECT_THAT( - nread = syscall(__NR_getdents64, fd.get(), buf.data(), buf.size()), - SyscallSucceeds()); - for (; off < nread;) { - struct dirent64 *ent = (struct dirent64 *)(buf.data() + off); - off += ent->d_reclen; - switch (i++) { - case 0: - EXPECT_EQ(std::string(ent->d_name), dot); - break; - case 1: - EXPECT_EQ(std::string(ent->d_name), dot_dot); - break; - case 2: - EXPECT_EQ(std::string(ent->d_name), test_file); - break; - } - } - - EXPECT_THAT( - nread = syscall(__NR_getdents64, fd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(0)); - - SkipServerActualRequest(); // READDIR. - SkipServerActualRequest(); // READDIR with no data. - - // Clean up. - fd.reset(-1); - - struct fuse_in_header in_header; - struct fuse_release_in in_payload; - - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_RELEASEDIR); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/readlink_test.cc b/test/fuse/linux/readlink_test.cc deleted file mode 100644 index 2cba8fc23..000000000 --- a/test/fuse/linux/readlink_test.cc +++ /dev/null @@ -1,85 +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 <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class ReadlinkTest : public FuseTest { - protected: - const std::string test_file_ = "test_file_"; - const mode_t perms_ = S_IRWXU | S_IRWXG | S_IRWXO; -}; - -TEST_F(ReadlinkTest, ReadSymLink) { - const std::string symlink_path = - JoinPath(mount_point_.path().c_str(), test_file_); - SetServerInodeLookup(test_file_, S_IFLNK | perms_); - - struct fuse_out_header out_header = { - .len = static_cast<uint32_t>(sizeof(struct fuse_out_header)) + - static_cast<uint32_t>(test_file_.length()) + 1, - }; - std::string link = test_file_; - auto iov_out = FuseGenerateIovecs(out_header, link); - SetServerResponse(FUSE_READLINK, iov_out); - const std::string actual_link = - ASSERT_NO_ERRNO_AND_VALUE(ReadLink(symlink_path)); - - struct fuse_in_header in_header; - auto iov_in = FuseGenerateIovecs(in_header); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, sizeof(in_header)); - EXPECT_EQ(in_header.opcode, FUSE_READLINK); - EXPECT_EQ(0, memcmp(actual_link.c_str(), link.data(), link.size())); - - // next readlink should have link cached, so shouldn't have new request to - // server. - uint32_t recieved_before = GetServerTotalReceivedBytes(); - ASSERT_NO_ERRNO(ReadLink(symlink_path)); - EXPECT_EQ(GetServerTotalReceivedBytes(), recieved_before); -} - -TEST_F(ReadlinkTest, NotSymlink) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - SetServerInodeLookup(test_file_, S_IFREG | perms_); - - std::vector<char> buf(PATH_MAX + 1); - ASSERT_THAT(readlink(test_file_path.c_str(), buf.data(), PATH_MAX), - SyscallFailsWithErrno(EINVAL)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/release_test.cc b/test/fuse/linux/release_test.cc deleted file mode 100644 index b5adb0870..000000000 --- a/test/fuse/linux/release_test.cc +++ /dev/null @@ -1,74 +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/mount.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class ReleaseTest : public FuseTest { - protected: - const std::string test_file_ = "test_file"; -}; - -TEST_F(ReleaseTest, RegularFile) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - SetServerInodeLookup(test_file_, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), - }; - struct fuse_open_out out_payload = { - .fh = 1, - .open_flags = O_RDWR, - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_OPEN, iov_out); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_path, O_RDWR)); - SkipServerActualRequest(); - ASSERT_THAT(close(fd.release()), SyscallSucceeds()); - - struct fuse_in_header in_header; - struct fuse_release_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_RELEASE); - EXPECT_EQ(in_payload.flags, O_RDWR); - EXPECT_EQ(in_payload.fh, 1); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/rmdir_test.cc b/test/fuse/linux/rmdir_test.cc deleted file mode 100644 index e3200e446..000000000 --- a/test/fuse/linux/rmdir_test.cc +++ /dev/null @@ -1,100 +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 <sys/uio.h> -#include <unistd.h> - -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fs_util.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class RmDirTest : public FuseTest { - protected: - const std::string test_dir_name_ = "test_dir"; - const std::string test_subdir_ = "test_subdir"; - const mode_t test_dir_mode_ = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO; -}; - -TEST_F(RmDirTest, NormalRmDir) { - const std::string test_dir_path_ = - JoinPath(mount_point_.path().c_str(), test_dir_name_); - - SetServerInodeLookup(test_dir_name_, test_dir_mode_); - - // RmDir code. - struct fuse_out_header rmdir_header = { - .len = sizeof(struct fuse_out_header), - }; - - auto iov_out = FuseGenerateIovecs(rmdir_header); - SetServerResponse(FUSE_RMDIR, iov_out); - - ASSERT_THAT(rmdir(test_dir_path_.c_str()), SyscallSucceeds()); - - struct fuse_in_header in_header; - std::vector<char> actual_dirname(test_dir_name_.length() + 1); - auto iov_in = FuseGenerateIovecs(in_header, actual_dirname); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, sizeof(in_header) + test_dir_name_.length() + 1); - EXPECT_EQ(in_header.opcode, FUSE_RMDIR); - EXPECT_EQ(std::string(actual_dirname.data()), test_dir_name_); -} - -TEST_F(RmDirTest, NormalRmDirSubdir) { - SetServerInodeLookup(test_subdir_, S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO); - const std::string test_dir_path_ = - JoinPath(mount_point_.path().c_str(), test_subdir_, test_dir_name_); - SetServerInodeLookup(test_dir_name_, test_dir_mode_); - - // RmDir code. - struct fuse_out_header rmdir_header = { - .len = sizeof(struct fuse_out_header), - }; - - auto iov_out = FuseGenerateIovecs(rmdir_header); - SetServerResponse(FUSE_RMDIR, iov_out); - - ASSERT_THAT(rmdir(test_dir_path_.c_str()), SyscallSucceeds()); - - struct fuse_in_header in_header; - std::vector<char> actual_dirname(test_dir_name_.length() + 1); - auto iov_in = FuseGenerateIovecs(in_header, actual_dirname); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, sizeof(in_header) + test_dir_name_.length() + 1); - EXPECT_EQ(in_header.opcode, FUSE_RMDIR); - EXPECT_EQ(std::string(actual_dirname.data()), test_dir_name_); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/setstat_test.cc b/test/fuse/linux/setstat_test.cc deleted file mode 100644 index 68301c775..000000000 --- a/test/fuse/linux/setstat_test.cc +++ /dev/null @@ -1,338 +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 <sys/uio.h> -#include <unistd.h> -#include <utime.h> - -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_fd_util.h" -#include "test/util/cleanup.h" -#include "test/util/fs_util.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class SetStatTest : public FuseFdTest { - public: - void SetUp() override { - FuseFdTest::SetUp(); - test_dir_path_ = JoinPath(mount_point_.path(), test_dir_); - test_file_path_ = JoinPath(mount_point_.path(), test_file_); - } - - protected: - const uint64_t fh = 23; - const std::string test_dir_ = "testdir"; - const std::string test_file_ = "testfile"; - const mode_t test_dir_mode_ = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR; - const mode_t test_file_mode_ = S_IFREG | S_IRUSR | S_IWUSR | S_IXUSR; - - std::string test_dir_path_; - std::string test_file_path_; -}; - -TEST_F(SetStatTest, ChmodDir) { - // Set up fixture. - SetServerInodeLookup(test_dir_, test_dir_mode_); - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - .error = 0, - }; - mode_t set_mode = S_IRGRP | S_IWGRP | S_IXGRP; - struct fuse_attr_out out_payload = { - .attr = DefaultFuseAttr(set_mode, 2), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SETATTR, iov_out); - - // Make syscall. - EXPECT_THAT(chmod(test_dir_path_.c_str(), set_mode), SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_setattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_SETATTR); - EXPECT_EQ(in_header.uid, 0); - EXPECT_EQ(in_header.gid, 0); - EXPECT_EQ(in_payload.valid, FATTR_MODE); - EXPECT_EQ(in_payload.mode, S_IFDIR | set_mode); -} - -TEST_F(SetStatTest, ChownDir) { - // Set up fixture. - SetServerInodeLookup(test_dir_, test_dir_mode_); - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - .error = 0, - }; - struct fuse_attr_out out_payload = { - .attr = DefaultFuseAttr(test_dir_mode_, 2), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SETATTR, iov_out); - - // Make syscall. - EXPECT_THAT(chown(test_dir_path_.c_str(), 1025, 1025), SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_setattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_SETATTR); - EXPECT_EQ(in_header.uid, 0); - EXPECT_EQ(in_header.gid, 0); - EXPECT_EQ(in_payload.valid, FATTR_UID | FATTR_GID); - EXPECT_EQ(in_payload.uid, 1025); - EXPECT_EQ(in_payload.gid, 1025); -} - -TEST_F(SetStatTest, TruncateFile) { - // Set up fixture. - SetServerInodeLookup(test_file_, test_file_mode_); - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - .error = 0, - }; - struct fuse_attr_out out_payload = { - .attr = DefaultFuseAttr(S_IFREG | S_IRUSR | S_IWUSR, 2), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SETATTR, iov_out); - - // Make syscall. - EXPECT_THAT(truncate(test_file_path_.c_str(), 321), SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_setattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_SETATTR); - EXPECT_EQ(in_header.uid, 0); - EXPECT_EQ(in_header.gid, 0); - EXPECT_EQ(in_payload.valid, FATTR_SIZE); - EXPECT_EQ(in_payload.size, 321); -} - -TEST_F(SetStatTest, UtimeFile) { - // Set up fixture. - SetServerInodeLookup(test_file_, test_file_mode_); - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - .error = 0, - }; - struct fuse_attr_out out_payload = { - .attr = DefaultFuseAttr(S_IFREG | S_IRUSR | S_IWUSR, 2), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SETATTR, iov_out); - - // Make syscall. - time_t expected_atime = 1597159766, expected_mtime = 1597159765; - struct utimbuf times = { - .actime = expected_atime, - .modtime = expected_mtime, - }; - EXPECT_THAT(utime(test_file_path_.c_str(), ×), SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_setattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_SETATTR); - EXPECT_EQ(in_header.uid, 0); - EXPECT_EQ(in_header.gid, 0); - EXPECT_EQ(in_payload.valid, FATTR_ATIME | FATTR_MTIME); - EXPECT_EQ(in_payload.atime, expected_atime); - EXPECT_EQ(in_payload.mtime, expected_mtime); -} - -TEST_F(SetStatTest, UtimesFile) { - // Set up fixture. - SetServerInodeLookup(test_file_, test_file_mode_); - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - .error = 0, - }; - struct fuse_attr_out out_payload = { - .attr = DefaultFuseAttr(test_file_mode_, 2), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SETATTR, iov_out); - - // Make syscall. - struct timeval expected_times[2] = { - { - .tv_sec = 1597159766, - .tv_usec = 234945, - }, - { - .tv_sec = 1597159765, - .tv_usec = 232341, - }, - }; - EXPECT_THAT(utimes(test_file_path_.c_str(), expected_times), - SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_setattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_SETATTR); - EXPECT_EQ(in_header.uid, 0); - EXPECT_EQ(in_header.gid, 0); - EXPECT_EQ(in_payload.valid, FATTR_ATIME | FATTR_MTIME); - EXPECT_EQ(in_payload.atime, expected_times[0].tv_sec); - EXPECT_EQ(in_payload.atimensec, expected_times[0].tv_usec * 1000); - EXPECT_EQ(in_payload.mtime, expected_times[1].tv_sec); - EXPECT_EQ(in_payload.mtimensec, expected_times[1].tv_usec * 1000); -} - -TEST_F(SetStatTest, FtruncateFile) { - // Set up fixture. - SetServerInodeLookup(test_file_, test_file_mode_); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDWR, fh)); - auto close_fd = CloseFD(fd); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - .error = 0, - }; - struct fuse_attr_out out_payload = { - .attr = DefaultFuseAttr(test_file_mode_, 2), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SETATTR, iov_out); - - // Make syscall. - EXPECT_THAT(ftruncate(fd.get(), 321), SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_setattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_SETATTR); - EXPECT_EQ(in_header.uid, 0); - EXPECT_EQ(in_header.gid, 0); - EXPECT_EQ(in_payload.valid, FATTR_SIZE | FATTR_FH); - EXPECT_EQ(in_payload.fh, fh); - EXPECT_EQ(in_payload.size, 321); -} - -TEST_F(SetStatTest, FchmodFile) { - // Set up fixture. - SetServerInodeLookup(test_file_, test_file_mode_); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDWR, fh)); - auto close_fd = CloseFD(fd); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - .error = 0, - }; - mode_t set_mode = S_IROTH | S_IWOTH | S_IXOTH; - struct fuse_attr_out out_payload = { - .attr = DefaultFuseAttr(set_mode, 2), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SETATTR, iov_out); - - // Make syscall. - EXPECT_THAT(fchmod(fd.get(), set_mode), SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_setattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_SETATTR); - EXPECT_EQ(in_header.uid, 0); - EXPECT_EQ(in_header.gid, 0); - EXPECT_EQ(in_payload.valid, FATTR_MODE | FATTR_FH); - EXPECT_EQ(in_payload.fh, fh); - EXPECT_EQ(in_payload.mode, S_IFREG | set_mode); -} - -TEST_F(SetStatTest, FchownFile) { - // Set up fixture. - SetServerInodeLookup(test_file_, test_file_mode_); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDWR, fh)); - auto close_fd = CloseFD(fd); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - .error = 0, - }; - struct fuse_attr_out out_payload = { - .attr = DefaultFuseAttr(S_IFREG | S_IRUSR | S_IWUSR | S_IXUSR, 2), - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SETATTR, iov_out); - - // Make syscall. - EXPECT_THAT(fchown(fd.get(), 1025, 1025), SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_setattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload)); - EXPECT_EQ(in_header.opcode, FUSE_SETATTR); - EXPECT_EQ(in_header.uid, 0); - EXPECT_EQ(in_header.gid, 0); - EXPECT_EQ(in_payload.valid, FATTR_UID | FATTR_GID | FATTR_FH); - EXPECT_EQ(in_payload.fh, fh); - EXPECT_EQ(in_payload.uid, 1025); - EXPECT_EQ(in_payload.gid, 1025); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/stat_test.cc b/test/fuse/linux/stat_test.cc deleted file mode 100644 index 73321592b..000000000 --- a/test/fuse/linux/stat_test.cc +++ /dev/null @@ -1,229 +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 <sys/uio.h> -#include <unistd.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_fd_util.h" -#include "test/util/cleanup.h" -#include "test/util/fs_util.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class StatTest : public FuseFdTest { - public: - void SetUp() override { - FuseFdTest::SetUp(); - test_file_path_ = JoinPath(mount_point_.path(), test_file_); - } - - protected: - 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; - } - - const std::string test_file_ = "testfile"; - const mode_t expected_mode = S_IFREG | S_IRUSR | S_IWUSR; - const uint64_t fh = 23; - - std::string test_file_path_; -}; - -TEST_F(StatTest, StatNormal) { - // Set up fixture. - struct fuse_attr attr = DefaultFuseAttr(expected_mode, 1); - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - }; - struct fuse_attr_out out_payload = { - .attr = attr, - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_GETATTR, iov_out); - - // Make syscall. - struct stat stat_buf; - EXPECT_THAT(stat(mount_point_.path().c_str(), &stat_buf), SyscallSucceeds()); - - // Check filesystem operation result. - struct stat expected_stat = { - .st_ino = attr.ino, -#ifdef __aarch64__ - .st_mode = expected_mode, - .st_nlink = attr.nlink, -#else - .st_nlink = attr.nlink, - .st_mode = expected_mode, -#endif - .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 = (struct timespec){.tv_sec = static_cast<int>(attr.atime), - .tv_nsec = attr.atimensec}, - .st_mtim = (struct timespec){.tv_sec = static_cast<int>(attr.mtime), - .tv_nsec = attr.mtimensec}, - .st_ctim = (struct timespec){.tv_sec = static_cast<int>(attr.ctime), - .tv_nsec = attr.ctimensec}, - }; - EXPECT_TRUE(StatsAreEqual(stat_buf, expected_stat)); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_getattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.opcode, FUSE_GETATTR); - EXPECT_EQ(in_payload.getattr_flags, 0); - EXPECT_EQ(in_payload.fh, 0); -} - -TEST_F(StatTest, StatNotFound) { - // Set up fixture. - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header), - .error = -ENOENT, - }; - auto iov_out = FuseGenerateIovecs(out_header); - SetServerResponse(FUSE_GETATTR, iov_out); - - // Make syscall. - struct stat stat_buf; - EXPECT_THAT(stat(mount_point_.path().c_str(), &stat_buf), - SyscallFailsWithErrno(ENOENT)); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_getattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.opcode, FUSE_GETATTR); - EXPECT_EQ(in_payload.getattr_flags, 0); - EXPECT_EQ(in_payload.fh, 0); -} - -TEST_F(StatTest, FstatNormal) { - // Set up fixture. - SetServerInodeLookup(test_file_); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDONLY, fh)); - auto close_fd = CloseFD(fd); - - struct fuse_attr attr = DefaultFuseAttr(expected_mode, 2); - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - }; - struct fuse_attr_out out_payload = { - .attr = attr, - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_GETATTR, iov_out); - - // Make syscall. - struct stat stat_buf; - EXPECT_THAT(fstat(fd.get(), &stat_buf), SyscallSucceeds()); - - // Check filesystem operation result. - struct stat expected_stat = { - .st_ino = attr.ino, -#ifdef __aarch64__ - .st_mode = expected_mode, - .st_nlink = attr.nlink, -#else - .st_nlink = attr.nlink, - .st_mode = expected_mode, -#endif - .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 = (struct timespec){.tv_sec = static_cast<int>(attr.atime), - .tv_nsec = attr.atimensec}, - .st_mtim = (struct timespec){.tv_sec = static_cast<int>(attr.mtime), - .tv_nsec = attr.mtimensec}, - .st_ctim = (struct timespec){.tv_sec = static_cast<int>(attr.ctime), - .tv_nsec = attr.ctimensec}, - }; - EXPECT_TRUE(StatsAreEqual(stat_buf, expected_stat)); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_getattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.opcode, FUSE_GETATTR); - EXPECT_EQ(in_payload.getattr_flags, 0); - EXPECT_EQ(in_payload.fh, 0); -} - -TEST_F(StatTest, StatByFileHandle) { - // Set up fixture. - SetServerInodeLookup(test_file_, expected_mode, 0); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDONLY, fh)); - auto close_fd = CloseFD(fd); - - struct fuse_attr attr = DefaultFuseAttr(expected_mode, 2, 0); - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out), - }; - struct fuse_attr_out out_payload = { - .attr = attr, - }; - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_GETATTR, iov_out); - - // Make syscall. - std::vector<char> buf(1); - // Since this is an empty file, it won't issue FUSE_READ. But a FUSE_GETATTR - // will be issued before read completes. - EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), SyscallSucceeds()); - - // Check FUSE request. - struct fuse_in_header in_header; - struct fuse_getattr_in in_payload; - auto iov_in = FuseGenerateIovecs(in_header, in_payload); - - GetServerActualRequest(iov_in); - EXPECT_EQ(in_header.opcode, FUSE_GETATTR); - EXPECT_EQ(in_payload.getattr_flags, FUSE_GETATTR_FH); - EXPECT_EQ(in_payload.fh, fh); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/symlink_test.cc b/test/fuse/linux/symlink_test.cc deleted file mode 100644 index 2c3a52987..000000000 --- a/test/fuse/linux/symlink_test.cc +++ /dev/null @@ -1,88 +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 <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class SymlinkTest : public FuseTest { - protected: - const std::string target_file_ = "target_file_"; - const std::string symlink_ = "symlink_"; - const mode_t perms_ = S_IRWXU | S_IRWXG | S_IRWXO; -}; - -TEST_F(SymlinkTest, CreateSymLink) { - const std::string symlink_path = - JoinPath(mount_point_.path().c_str(), symlink_); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), - }; - struct fuse_entry_out out_payload = DefaultEntryOut(S_IFLNK | perms_, 5); - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SYMLINK, iov_out); - ASSERT_THAT(symlink(target_file_.c_str(), symlink_path.c_str()), - SyscallSucceeds()); - - struct fuse_in_header in_header; - std::vector<char> actual_target_file(target_file_.length() + 1); - std::vector<char> actual_symlink(symlink_.length() + 1); - auto iov_in = - FuseGenerateIovecs(in_header, actual_symlink, actual_target_file); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, - sizeof(in_header) + symlink_.length() + target_file_.length() + 2); - EXPECT_EQ(in_header.opcode, FUSE_SYMLINK); - EXPECT_EQ(std::string(actual_target_file.data()), target_file_); - EXPECT_EQ(std::string(actual_symlink.data()), symlink_); -} - -TEST_F(SymlinkTest, FileTypeError) { - const std::string symlink_path = - JoinPath(mount_point_.path().c_str(), symlink_); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out), - }; - struct fuse_entry_out out_payload = DefaultEntryOut(S_IFREG | perms_, 5); - auto iov_out = FuseGenerateIovecs(out_header, out_payload); - SetServerResponse(FUSE_SYMLINK, iov_out); - ASSERT_THAT(symlink(target_file_.c_str(), symlink_path.c_str()), - SyscallFailsWithErrno(EIO)); - SkipServerActualRequest(); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/unlink_test.cc b/test/fuse/linux/unlink_test.cc deleted file mode 100644 index 13efbf7c7..000000000 --- a/test/fuse/linux/unlink_test.cc +++ /dev/null @@ -1,107 +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/mount.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class UnlinkTest : public FuseTest { - protected: - const std::string test_file_ = "test_file"; - const std::string test_subdir_ = "test_subdir"; -}; - -TEST_F(UnlinkTest, RegularFile) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - SetServerInodeLookup(test_file_, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header), - }; - auto iov_out = FuseGenerateIovecs(out_header); - SetServerResponse(FUSE_UNLINK, iov_out); - - ASSERT_THAT(unlink(test_file_path.c_str()), SyscallSucceeds()); - struct fuse_in_header in_header; - std::vector<char> unlinked_file(test_file_.length() + 1); - auto iov_in = FuseGenerateIovecs(in_header, unlinked_file); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, sizeof(in_header) + test_file_.length() + 1); - EXPECT_EQ(in_header.opcode, FUSE_UNLINK); - EXPECT_EQ(std::string(unlinked_file.data()), test_file_); -} - -TEST_F(UnlinkTest, RegularFileSubDir) { - SetServerInodeLookup(test_subdir_, S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO); - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_subdir_, test_file_); - SetServerInodeLookup(test_file_, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header), - }; - auto iov_out = FuseGenerateIovecs(out_header); - SetServerResponse(FUSE_UNLINK, iov_out); - - ASSERT_THAT(unlink(test_file_path.c_str()), SyscallSucceeds()); - struct fuse_in_header in_header; - std::vector<char> unlinked_file(test_file_.length() + 1); - auto iov_in = FuseGenerateIovecs(in_header, unlinked_file); - GetServerActualRequest(iov_in); - - EXPECT_EQ(in_header.len, sizeof(in_header) + test_file_.length() + 1); - EXPECT_EQ(in_header.opcode, FUSE_UNLINK); - EXPECT_EQ(std::string(unlinked_file.data()), test_file_); -} - -TEST_F(UnlinkTest, NoFile) { - const std::string test_file_path = - JoinPath(mount_point_.path().c_str(), test_file_); - SetServerInodeLookup(test_file_, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO); - - struct fuse_out_header out_header = { - .len = sizeof(struct fuse_out_header), - .error = -ENOENT, - }; - auto iov_out = FuseGenerateIovecs(out_header); - SetServerResponse(FUSE_UNLINK, iov_out); - - ASSERT_THAT(unlink(test_file_path.c_str()), SyscallFailsWithErrno(ENOENT)); - SkipServerActualRequest(); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/fuse/linux/write_test.cc b/test/fuse/linux/write_test.cc deleted file mode 100644 index 1a62beb96..000000000 --- a/test/fuse/linux/write_test.cc +++ /dev/null @@ -1,303 +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 <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/fuse/linux/fuse_base.h" -#include "test/util/fuse_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class WriteTest : public FuseTest { - void SetUp() override { - FuseTest::SetUp(); - test_file_path_ = JoinPath(mount_point_.path().c_str(), test_file_); - } - - // TearDown overrides the parent's function - // to skip checking the unconsumed release request at the end. - void TearDown() override { UnmountFuse(); } - - protected: - const std::string test_file_ = "test_file"; - const mode_t test_file_mode_ = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO; - const uint64_t test_fh_ = 1; - const uint32_t open_flag_ = O_RDWR; - - std::string test_file_path_; - - PosixErrorOr<FileDescriptor> OpenTestFile(const std::string &path, - uint64_t size = 512) { - SetServerInodeLookup(test_file_, test_file_mode_, size); - - struct fuse_out_header out_header_open = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out), - }; - struct fuse_open_out out_payload_open = { - .fh = test_fh_, - .open_flags = open_flag_, - }; - auto iov_out_open = FuseGenerateIovecs(out_header_open, out_payload_open); - SetServerResponse(FUSE_OPEN, iov_out_open); - - auto res = Open(path.c_str(), open_flag_); - if (res.ok()) { - SkipServerActualRequest(); - } - return res; - } -}; - -class WriteTestSmallMaxWrite : public WriteTest { - void SetUp() override { - MountFuse(); - SetUpFuseServer(&fuse_init_payload); - test_file_path_ = JoinPath(mount_point_.path().c_str(), test_file_); - } - - protected: - const static uint32_t max_write_ = 4096; - constexpr static struct fuse_init_out fuse_init_payload = { - .major = 7, - .max_write = max_write_, - }; - - const uint32_t size_fragment = max_write_; -}; - -TEST_F(WriteTest, WriteNormal) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Prepare for the write. - const int n_write = 10; - struct fuse_out_header out_header_write = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), - }; - struct fuse_write_out out_payload_write = { - .size = n_write, - }; - auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); - SetServerResponse(FUSE_WRITE, iov_out_write); - - // Issue the write. - std::vector<char> buf(n_write); - RandomizeBuffer(buf.data(), buf.size()); - EXPECT_THAT(write(fd.get(), buf.data(), n_write), - SyscallSucceedsWithValue(n_write)); - - // Check the write request. - struct fuse_in_header in_header_write; - struct fuse_write_in in_payload_write; - std::vector<char> payload_buf(n_write); - auto iov_in_write = - FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); - GetServerActualRequest(iov_in_write); - - EXPECT_EQ(in_payload_write.fh, test_fh_); - EXPECT_EQ(in_header_write.len, - sizeof(in_header_write) + sizeof(in_payload_write)); - EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); - EXPECT_EQ(in_payload_write.offset, 0); - EXPECT_EQ(in_payload_write.size, n_write); - EXPECT_EQ(buf, payload_buf); -} - -TEST_F(WriteTest, WriteShort) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Prepare for the write. - const int n_write = 10, n_written = 5; - struct fuse_out_header out_header_write = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), - }; - struct fuse_write_out out_payload_write = { - .size = n_written, - }; - auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); - SetServerResponse(FUSE_WRITE, iov_out_write); - - // Issue the write. - std::vector<char> buf(n_write); - RandomizeBuffer(buf.data(), buf.size()); - EXPECT_THAT(write(fd.get(), buf.data(), n_write), - SyscallSucceedsWithValue(n_written)); - - // Check the write request. - struct fuse_in_header in_header_write; - struct fuse_write_in in_payload_write; - std::vector<char> payload_buf(n_write); - auto iov_in_write = - FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); - GetServerActualRequest(iov_in_write); - - EXPECT_EQ(in_payload_write.fh, test_fh_); - EXPECT_EQ(in_header_write.len, - sizeof(in_header_write) + sizeof(in_payload_write)); - EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); - EXPECT_EQ(in_payload_write.offset, 0); - EXPECT_EQ(in_payload_write.size, n_write); - EXPECT_EQ(buf, payload_buf); -} - -TEST_F(WriteTest, WriteShortZero) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Prepare for the write. - const int n_write = 10; - struct fuse_out_header out_header_write = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), - }; - struct fuse_write_out out_payload_write = { - .size = 0, - }; - auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); - SetServerResponse(FUSE_WRITE, iov_out_write); - - // Issue the write. - std::vector<char> buf(n_write); - RandomizeBuffer(buf.data(), buf.size()); - EXPECT_THAT(write(fd.get(), buf.data(), n_write), SyscallFailsWithErrno(EIO)); - - // Check the write request. - struct fuse_in_header in_header_write; - struct fuse_write_in in_payload_write; - std::vector<char> payload_buf(n_write); - auto iov_in_write = - FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); - GetServerActualRequest(iov_in_write); - - EXPECT_EQ(in_payload_write.fh, test_fh_); - EXPECT_EQ(in_header_write.len, - sizeof(in_header_write) + sizeof(in_payload_write)); - EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); - EXPECT_EQ(in_payload_write.offset, 0); - EXPECT_EQ(in_payload_write.size, n_write); - EXPECT_EQ(buf, payload_buf); -} - -TEST_F(WriteTest, WriteZero) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_)); - - // Issue the write. - std::vector<char> buf(0); - EXPECT_THAT(write(fd.get(), buf.data(), 0), SyscallSucceedsWithValue(0)); -} - -TEST_F(WriteTest, PWrite) { - const int file_size = 512; - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, file_size)); - - // Prepare for the write. - const int n_write = 10; - struct fuse_out_header out_header_write = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), - }; - struct fuse_write_out out_payload_write = { - .size = n_write, - }; - auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); - SetServerResponse(FUSE_WRITE, iov_out_write); - - // Issue the write. - std::vector<char> buf(n_write); - RandomizeBuffer(buf.data(), buf.size()); - const int offset_write = file_size >> 1; - EXPECT_THAT(pwrite(fd.get(), buf.data(), n_write, offset_write), - SyscallSucceedsWithValue(n_write)); - - // Check the write request. - struct fuse_in_header in_header_write; - struct fuse_write_in in_payload_write; - std::vector<char> payload_buf(n_write); - auto iov_in_write = - FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); - GetServerActualRequest(iov_in_write); - - EXPECT_EQ(in_payload_write.fh, test_fh_); - EXPECT_EQ(in_header_write.len, - sizeof(in_header_write) + sizeof(in_payload_write)); - EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); - EXPECT_EQ(in_payload_write.offset, offset_write); - EXPECT_EQ(in_payload_write.size, n_write); - EXPECT_EQ(buf, payload_buf); -} - -TEST_F(WriteTestSmallMaxWrite, WriteSmallMaxWrie) { - const int n_fragment = 10; - const int n_write = size_fragment * n_fragment; - - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenTestFile(test_file_path_, n_write)); - - // Prepare for the write. - struct fuse_out_header out_header_write = { - .len = sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out), - }; - struct fuse_write_out out_payload_write = { - .size = size_fragment, - }; - auto iov_out_write = FuseGenerateIovecs(out_header_write, out_payload_write); - - for (int i = 0; i < n_fragment; ++i) { - SetServerResponse(FUSE_WRITE, iov_out_write); - } - - // Issue the write. - std::vector<char> buf(n_write); - RandomizeBuffer(buf.data(), buf.size()); - EXPECT_THAT(write(fd.get(), buf.data(), n_write), - SyscallSucceedsWithValue(n_write)); - - ASSERT_EQ(GetServerNumUnsentResponses(), 0); - ASSERT_EQ(GetServerNumUnconsumedRequests(), n_fragment); - - // Check the write request. - struct fuse_in_header in_header_write; - struct fuse_write_in in_payload_write; - std::vector<char> payload_buf(size_fragment); - auto iov_in_write = - FuseGenerateIovecs(in_header_write, in_payload_write, payload_buf); - - for (int i = 0; i < n_fragment; ++i) { - GetServerActualRequest(iov_in_write); - - EXPECT_EQ(in_payload_write.fh, test_fh_); - EXPECT_EQ(in_header_write.len, - sizeof(in_header_write) + sizeof(in_payload_write)); - EXPECT_EQ(in_header_write.opcode, FUSE_WRITE); - EXPECT_EQ(in_payload_write.offset, i * size_fragment); - EXPECT_EQ(in_payload_write.size, size_fragment); - - auto it = buf.begin() + i * size_fragment; - EXPECT_EQ(std::vector<char>(it, it + size_fragment), payload_buf); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor |