mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user