summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorCraig Chi <craigchi@google.com>2020-08-17 10:05:10 -0700
committerAndrei Vagin <avagin@gmail.com>2020-09-16 12:19:30 -0700
commit717b661c457cc3a125fcdfd133b633ca48545541 (patch)
treed4cb58a8b8c90460883424066d77befb048e766a
parentd928d3c00a66a29933eee9671e3558cd8163337f (diff)
Add function to create a fake inode in FUSE integration test
Adds a function for the testing thread to set up a fake inode with a specific path under mount point. After this function is called, each subsequent FUSE_LOOKUP request with the same path will be served with the fixed stub response. Fixes #3539
-rw-r--r--test/fuse/linux/fuse_base.cc91
-rw-r--r--test/fuse/linux/fuse_base.h25
2 files changed, 116 insertions, 0 deletions
diff --git a/test/fuse/linux/fuse_base.cc b/test/fuse/linux/fuse_base.cc
index c354e1dcb..a9fe1044e 100644
--- a/test/fuse/linux/fuse_base.cc
+++ b/test/fuse/linux/fuse_base.cc
@@ -117,6 +117,23 @@ uint32_t FuseTest::GetServerTotalReceivedBytes() {
static_cast<uint32_t>(FuseTestCmd::kGetTotalReceivedBytes));
}
+// 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) {
+ 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)));
+
+ // 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() {
EXPECT_THAT(dev_fd_ = open("/dev/fuse", O_RDWR), SyscallSucceeds());
@@ -252,6 +269,9 @@ void FuseTest::ServerHandleCommand() {
case FuseTestCmd::kSetResponse:
ServerReceiveResponse();
break;
+ case FuseTestCmd::kSetInodeLookup:
+ ServerReceiveInodeLookup();
+ break;
case FuseTestCmd::kGetRequest:
ServerSendReceivedRequest();
break;
@@ -272,6 +292,64 @@ void FuseTest::ServerHandleCommand() {
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;
+ 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], 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 = {
+ .nodeid = nodeid_,
+ .generation = 0,
+ .entry_valid = 0,
+ .attr_valid = 0,
+ .entry_valid_nsec = 0,
+ .attr_valid_nsec = 0,
+ .attr =
+ (struct fuse_attr){
+ .ino = nodeid_,
+ .size = 512,
+ .blocks = 4,
+ .atime = 0,
+ .mtime = 0,
+ .ctime = 0,
+ .atimensec = 0,
+ .mtimensec = 0,
+ .ctimensec = 0,
+ .mode = mode,
+ .nlink = 2,
+ .uid = 1234,
+ .gid = 4321,
+ .rdev = 12,
+ .blksize = 4096,
+ },
+ };
+ // Since this is only used in test, nodeid_ is simply increased by 1 to
+ // comply with the unqiueness of different path.
+ ++nodeid_;
+
+ 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()) {
@@ -297,6 +375,19 @@ void FuseTest::ServerProcessFuseRequest() {
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);
// Check if there is a corresponding response.
diff --git a/test/fuse/linux/fuse_base.h b/test/fuse/linux/fuse_base.h
index 3f2522977..a21b4bb8d 100644
--- a/test/fuse/linux/fuse_base.h
+++ b/test/fuse/linux/fuse_base.h
@@ -17,9 +17,11 @@
#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"
@@ -35,6 +37,7 @@ constexpr char kMountOpts[] = "rootmode=755,user_id=0,group_id=0";
// server. See test/fuse/README.md for further detail.
enum class FuseTestCmd {
kSetResponse = 0,
+ kSetInodeLookup,
kGetRequest,
kGetNumUnconsumedRequests,
kGetNumUnsentResponses,
@@ -114,6 +117,9 @@ class FuseMemBuffer {
// 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;
@@ -122,6 +128,16 @@ class FuseTest : public ::testing::Test {
// 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);
+
// 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.
@@ -182,6 +198,11 @@ class FuseTest : public ::testing::Test {
// 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.
@@ -203,8 +224,12 @@ class FuseTest : public ::testing::Test {
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