fuse(4): add tests for FUSE_INTERRUPT
This required changing the way that all operations are mocked. Previously MockFS::process had one input argument and one output argument. Now, it returns a vector of zero or more responses. This allows tests to simulate conditions where the filesystem daemon has a queue depth > 1. PR: 236530 Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
3592c9fe12
commit
94ef9d62cc
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=345140
@ -4,12 +4,16 @@ PACKAGE= tests
|
||||
|
||||
TESTSDIR= ${TESTSBASE}/sys/fs/fuse
|
||||
|
||||
# We could simply link all of these files into a single executable. But since
|
||||
# Kyua treats googletest programs as plain tests, it's better to separate them
|
||||
# out, so we get more granular reporting.
|
||||
ATF_TESTS_CXX+= access
|
||||
ATF_TESTS_CXX+= create
|
||||
ATF_TESTS_CXX+= flush
|
||||
ATF_TESTS_CXX+= fsync
|
||||
ATF_TESTS_CXX+= fsyncdir
|
||||
ATF_TESTS_CXX+= getattr
|
||||
ATF_TESTS_CXX+= interrupt
|
||||
ATF_TESTS_CXX+= link
|
||||
ATF_TESTS_CXX+= locks
|
||||
ATF_TESTS_CXX+= lookup
|
||||
@ -60,6 +64,11 @@ SRCS.getattr+= getmntopts.c
|
||||
SRCS.getattr+= mockfs.cc
|
||||
SRCS.getattr+= utils.cc
|
||||
|
||||
SRCS.interrupt+= interrupt.cc
|
||||
SRCS.interrupt+= getmntopts.c
|
||||
SRCS.interrupt+= mockfs.cc
|
||||
SRCS.interrupt+= utils.cc
|
||||
|
||||
SRCS.link+= getmntopts.c
|
||||
SRCS.link+= link.cc
|
||||
SRCS.link+= mockfs.cc
|
||||
|
@ -62,14 +62,14 @@ TEST_F(Create, DISABLED_attr_cache)
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, create);
|
||||
out->body.create.entry.attr.mode = S_IFREG | mode;
|
||||
out->body.create.entry.nodeid = ino;
|
||||
out->body.create.entry.entry_valid = UINT64_MAX;
|
||||
out->body.create.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -145,14 +145,14 @@ TEST_F(Create, DISABLED_Enosys)
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, create);
|
||||
out->body.create.entry.attr.mode = S_IFREG | mode;
|
||||
out->body.create.entry.nodeid = ino;
|
||||
out->body.create.entry.entry_valid = UINT64_MAX;
|
||||
out->body.create.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -160,11 +160,11 @@ TEST_F(Create, DISABLED_Enosys)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(out->header);
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
}));
|
||||
})));
|
||||
|
||||
/* Until the attr cache is working, we may send an additional GETATTR */
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
@ -173,12 +173,12 @@ TEST_F(Create, DISABLED_Enosys)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
|
||||
EXPECT_LE(0, fd) << strerror(errno);
|
||||
@ -214,14 +214,14 @@ TEST_F(Create, DISABLED_entry_cache_negative)
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, create);
|
||||
out->body.create.entry.attr.mode = S_IFREG | mode;
|
||||
out->body.create.entry.nodeid = ino;
|
||||
out->body.create.entry.entry_valid = UINT64_MAX;
|
||||
out->body.create.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
/* Until the attr cache is working, we may send an additional GETATTR */
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
@ -230,12 +230,12 @@ TEST_F(Create, DISABLED_entry_cache_negative)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -269,13 +269,13 @@ TEST_F(Create, DISABLED_entry_cache_negative_purge)
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, create);
|
||||
out->body.create.entry.attr.mode = S_IFREG | mode;
|
||||
out->body.create.entry.nodeid = ino;
|
||||
out->body.create.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
/* Until the attr cache is working, we may send an additional GETATTR */
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
@ -284,12 +284,12 @@ TEST_F(Create, DISABLED_entry_cache_negative_purge)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -344,14 +344,14 @@ TEST_F(Create, ok)
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, create);
|
||||
out->body.create.entry.attr.mode = S_IFREG | mode;
|
||||
out->body.create.entry.nodeid = ino;
|
||||
out->body.create.entry.entry_valid = UINT64_MAX;
|
||||
out->body.create.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
/* Until the attr cache is working, we may send an additional GETATTR */
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
@ -360,12 +360,12 @@ TEST_F(Create, ok)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_CREAT | O_EXCL, mode);
|
||||
EXPECT_LE(0, fd) << strerror(errno);
|
||||
|
@ -132,11 +132,11 @@ TEST_F(Fsync, close)
|
||||
return (in->header.opcode == FUSE_SETATTR);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
}));
|
||||
})));
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_FSYNC);
|
||||
@ -244,12 +244,12 @@ TEST_F(Fsync, DISABLED_fsync_metadata_only)
|
||||
return (in->header.opcode == FUSE_SETATTR);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | mode;
|
||||
}));
|
||||
})));
|
||||
|
||||
expect_fsync(ino, 0, 0);
|
||||
|
||||
|
@ -47,25 +47,25 @@ TEST_F(Getattr, DISABLED_attr_cache)
|
||||
const uint64_t ino = 42;
|
||||
struct stat sb;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
out->body.entry.nodeid = ino;
|
||||
}));
|
||||
})));
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([](auto in) {
|
||||
return (in->header.opcode == FUSE_GETATTR &&
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr_valid = UINT64_MAX;
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
}));
|
||||
})));
|
||||
EXPECT_EQ(0, stat(FULLPATH, &sb));
|
||||
/* The second stat(2) should use cached attributes */
|
||||
EXPECT_EQ(0, stat(FULLPATH, &sb));
|
||||
@ -97,14 +97,14 @@ TEST_F(Getattr, attr_cache_timeout)
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).Times(2)
|
||||
.WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr_valid_nsec = timeout_ns;
|
||||
out->body.attr.attr_valid = UINT64_MAX;
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
}));
|
||||
})));
|
||||
EXPECT_EQ(0, stat(FULLPATH, &sb));
|
||||
usleep(2 * timeout_ns / 1000);
|
||||
/* Timeout has expire. stat(2) should requery the daemon */
|
||||
@ -144,7 +144,7 @@ TEST_F(Getattr, ok)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
@ -161,7 +161,7 @@ TEST_F(Getattr, ok)
|
||||
out->body.attr.attr.uid = 10;
|
||||
out->body.attr.attr.gid = 11;
|
||||
out->body.attr.attr.rdev = 12;
|
||||
}));
|
||||
})));
|
||||
|
||||
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
|
||||
EXPECT_EQ(1, sb.st_size);
|
||||
|
312
tests/sys/fs/fuse/interrupt.cc
Normal file
312
tests/sys/fs/fuse/interrupt.cc
Normal file
@ -0,0 +1,312 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 The FreeBSD Foundation
|
||||
*
|
||||
* This software was developed by BFF Storage Systems, LLC under sponsorship
|
||||
* from the FreeBSD Foundation.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
}
|
||||
|
||||
#include "mockfs.hh"
|
||||
#include "utils.hh"
|
||||
|
||||
using namespace testing;
|
||||
|
||||
/* Don't do anything; all we care about is that the syscall gets interrupted */
|
||||
void sigusr2_handler(int __unused sig) {
|
||||
if (verbosity > 1)
|
||||
printf("Signaled!\n");
|
||||
}
|
||||
|
||||
void* killer(void* target) {
|
||||
/*
|
||||
* Sleep for awhile so we can be mostly confident that the main thread
|
||||
* is already blocked in write(2)
|
||||
*/
|
||||
usleep(250'000);
|
||||
if (verbosity > 1)
|
||||
printf("Signalling!\n");
|
||||
pthread_kill(*(pthread_t*)target, SIGUSR2);
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
class Interrupt: public FuseTest {
|
||||
public:
|
||||
pthread_t m_child;
|
||||
|
||||
Interrupt(): m_child(NULL) {};
|
||||
|
||||
void expect_lookup(const char *relpath, uint64_t ino)
|
||||
{
|
||||
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expect a FUSE_WRITE but don't reply. Instead, just record the unique value
|
||||
* to the provided pointer
|
||||
*/
|
||||
void expect_write(uint64_t ino, uint64_t *write_unique)
|
||||
{
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_WRITE &&
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto &out __unused) {
|
||||
*write_unique = in->header.unique;
|
||||
}));
|
||||
}
|
||||
|
||||
void setup_interruptor(pthread_t self)
|
||||
{
|
||||
ASSERT_EQ(0, signal(SIGUSR2, sigusr2_handler)) << strerror(errno);
|
||||
ASSERT_EQ(0, pthread_create(&m_child, NULL, killer, (void*)self))
|
||||
<< strerror(errno);
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
if (m_child != NULL) {
|
||||
pthread_join(m_child, NULL);
|
||||
}
|
||||
|
||||
FuseTest::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* An interrupt operation that gets received after the original command is
|
||||
* complete should generate an EAGAIN response.
|
||||
*/
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
|
||||
TEST_F(Interrupt, DISABLED_already_complete)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
pthread_t self;
|
||||
uint64_t write_unique = 0;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_getattr(ino, 0);
|
||||
expect_write(ino, &write_unique);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([&](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([&](auto in, auto &out) {
|
||||
// First complete the write request
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.unique = write_unique;
|
||||
SET_OUT_HEADER_LEN(out0, write);
|
||||
out0->body.write.size = bufsize;
|
||||
out.push_back(out0);
|
||||
|
||||
// Then, respond EAGAIN to the interrupt request
|
||||
auto out1 = new mockfs_buf_out;
|
||||
out1->header.unique = in->header.unique;
|
||||
out1->header.error = -EAGAIN;
|
||||
out1->header.len = sizeof(out1->header);
|
||||
out.push_back(out1);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
||||
|
||||
/*
|
||||
* A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and
|
||||
* complete the original operation whenever it damn well pleases.
|
||||
*/
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
|
||||
TEST_F(Interrupt, DISABLED_ignore)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
pthread_t self;
|
||||
uint64_t write_unique;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_getattr(ino, 0);
|
||||
expect_write(ino, &write_unique);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([&](auto in __unused, auto &out) {
|
||||
// Ignore FUSE_INTERRUPT; respond to the FUSE_WRITE
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.unique = write_unique;
|
||||
SET_OUT_HEADER_LEN(out0, write);
|
||||
out0->body.write.size = bufsize;
|
||||
out.push_back(out0);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
||||
|
||||
/*
|
||||
* A syscall that gets interrupted while blocking on FUSE I/O should send a
|
||||
* FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR
|
||||
* in response to the _original_ operation. The kernel should ultimately
|
||||
* return EINTR to userspace
|
||||
*/
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
|
||||
TEST_F(Interrupt, DISABLED_in_progress)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
pthread_t self;
|
||||
uint64_t write_unique;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_getattr(ino, 0);
|
||||
expect_write(ino, &write_unique);
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([&](auto in __unused, auto &out) {
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.error = -EINTR;
|
||||
out0->header.unique = write_unique;
|
||||
out0->header.len = sizeof(out0->header);
|
||||
out.push_back(out0);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
ASSERT_EQ(-1, write(fd, CONTENTS, bufsize));
|
||||
EXPECT_EQ(EINTR, errno);
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
||||
|
||||
/*
|
||||
* If the FUSE filesystem receives the FUSE_INTERRUPT operation before
|
||||
* processing the original, then it should wait for "some timeout" for the
|
||||
* original operation to arrive. If not, it should send EAGAIN to the
|
||||
* INTERRUPT operation, and the kernel should requeue the INTERRUPT.
|
||||
*
|
||||
* In this test, we'll pretend that the INTERRUPT arrives too soon, gets
|
||||
* EAGAINed, then the kernel requeues it, and the second time around it
|
||||
* successfully interrupts the original
|
||||
*/
|
||||
/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
|
||||
TEST_F(Interrupt, DISABLED_too_soon)
|
||||
{
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const char *CONTENTS = "abcdefgh";
|
||||
uint64_t ino = 42;
|
||||
int fd;
|
||||
ssize_t bufsize = strlen(CONTENTS);
|
||||
pthread_t self;
|
||||
uint64_t write_unique;
|
||||
|
||||
self = pthread_self();
|
||||
|
||||
expect_lookup(RELPATH, ino);
|
||||
expect_open(ino, 0, 1);
|
||||
expect_getattr(ino, 0);
|
||||
expect_write(ino, &write_unique);
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke(ReturnErrno(EAGAIN)))
|
||||
.RetiresOnSaturation();
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
return (in->header.opcode == FUSE_INTERRUPT &&
|
||||
in->body.interrupt.unique == write_unique);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.error = -EINTR;
|
||||
out0->header.unique = write_unique;
|
||||
out0->header.len = sizeof(out0->header);
|
||||
out.push_back(out0);
|
||||
}));
|
||||
|
||||
fd = open(FULLPATH, O_WRONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
||||
setup_interruptor(self);
|
||||
ASSERT_EQ(-1, write(fd, CONTENTS, bufsize));
|
||||
EXPECT_EQ(EINTR, errno);
|
||||
|
||||
/* Deliberately leak fd. close(2) will be tested in release.cc */
|
||||
}
|
@ -92,12 +92,12 @@ TEST_F(Link, ok)
|
||||
(0 == strcmp(name, RELPATH)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
out->body.entry.nodeid = ino;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_EQ(0, link(FULLDST, FULLPATH)) << strerror(errno);
|
||||
}
|
||||
|
@ -123,12 +123,12 @@ TEST_F(Getlk, DISABLED_no_locks)
|
||||
in->body.getlk.lk.pid == 10);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, getlk);
|
||||
out->body.getlk.lk = in->body.getlk.lk;
|
||||
out->body.getlk.lk.type = F_UNLCK;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_RDWR);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -170,14 +170,14 @@ TEST_F(Getlk, DISABLED_lock_exists)
|
||||
in->body.getlk.lk.pid == 10);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, getlk);
|
||||
out->body.getlk.lk.start = 100;
|
||||
out->body.getlk.lk.end = 199;
|
||||
out->body.getlk.lk.type = F_WRLCK;
|
||||
out->body.getlk.lk.pid = (uint32_t)pid2;;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_RDWR);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -251,12 +251,12 @@ TEST_F(Setlk, DISABLED_set)
|
||||
in->body.getlk.lk.pid == 10);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, getlk);
|
||||
out->body.getlk.lk = in->body.getlk.lk;
|
||||
out->body.getlk.lk.type = F_UNLCK;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_RDWR);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -296,12 +296,12 @@ TEST_F(Setlk, DISABLED_set_eof)
|
||||
in->body.getlk.lk.pid == 10);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, getlk);
|
||||
out->body.getlk.lk = in->body.getlk.lk;
|
||||
out->body.getlk.lk.type = F_UNLCK;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_RDWR);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -414,12 +414,12 @@ TEST_F(Setlkw, DISABLED_set)
|
||||
in->body.getlk.lk.pid == 10);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, getlk);
|
||||
out->body.getlk.lk = in->body.getlk.lk;
|
||||
out->body.getlk.lk.type = F_UNLCK;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_RDWR);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
|
@ -52,7 +52,8 @@ TEST_F(Lookup, DISABLED_attr_cache)
|
||||
const uint64_t generation = 13;
|
||||
struct stat sb;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.nodeid = ino;
|
||||
@ -72,7 +73,7 @@ TEST_F(Lookup, DISABLED_attr_cache)
|
||||
out->body.entry.attr.gid = 11;
|
||||
out->body.entry.attr.rdev = 12;
|
||||
out->body.entry.generation = generation;
|
||||
}));
|
||||
})));
|
||||
/* stat(2) issues a VOP_LOOKUP followed by a VOP_GETATTR */
|
||||
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
|
||||
EXPECT_EQ(1, sb.st_size);
|
||||
@ -118,14 +119,15 @@ TEST_F(Lookup, attr_cache_timeout)
|
||||
*/
|
||||
long timeout_ns = 250'000'000;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr_valid_nsec = timeout_ns;
|
||||
out->body.entry.attr.ino = ino; // Must match nodeid
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
}));
|
||||
})));
|
||||
expect_getattr(ino, 0);
|
||||
|
||||
/* access(2) will issue a VOP_LOOKUP but not a VOP_GETATTR */
|
||||
@ -154,13 +156,14 @@ TEST_F(Lookup, entry_cache)
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.entry_valid = UINT64_MAX;
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
out->body.entry.nodeid = 14;
|
||||
}));
|
||||
})));
|
||||
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
|
||||
/* The second access(2) should use the cache */
|
||||
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
|
||||
@ -224,13 +227,13 @@ TEST_F(Lookup, DISABLED_entry_cache_timeout)
|
||||
long timeout_ns = 250'000'000;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).Times(2)
|
||||
.WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.entry_valid_nsec = timeout_ns;
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
out->body.entry.nodeid = 14;
|
||||
}));
|
||||
})));
|
||||
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
|
||||
usleep(2 * timeout_ns / 1000);
|
||||
/* The cache has timed out; VOP_LOOKUP should query the daemon*/
|
||||
@ -248,12 +251,13 @@ TEST_F(Lookup, ok)
|
||||
const char FULLPATH[] = "mountpoint/some_file.txt";
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
out->body.entry.nodeid = 14;
|
||||
}));
|
||||
})));
|
||||
/*
|
||||
* access(2) is one of the few syscalls that will not (always) follow
|
||||
* up a successful VOP_LOOKUP with another VOP.
|
||||
@ -270,18 +274,20 @@ TEST_F(Lookup, subdir)
|
||||
uint64_t dir_ino = 2;
|
||||
uint64_t file_ino = 3;
|
||||
|
||||
EXPECT_LOOKUP(1, DIRPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, DIRPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFDIR | 0755;
|
||||
out->body.entry.nodeid = dir_ino;
|
||||
}));
|
||||
EXPECT_LOOKUP(dir_ino, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
})));
|
||||
EXPECT_LOOKUP(dir_ino, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
out->body.entry.nodeid = file_ino;
|
||||
}));
|
||||
})));
|
||||
/*
|
||||
* access(2) is one of the few syscalls that will not (always) follow
|
||||
* up a successful VOP_LOOKUP with another VOP.
|
||||
|
@ -95,14 +95,14 @@ TEST_F(Mkdir, DISABLED_entry_cache_negative)
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.create.entry.attr.mode = S_IFDIR | mode;
|
||||
out->body.create.entry.nodeid = ino;
|
||||
out->body.create.entry.entry_valid = UINT64_MAX;
|
||||
out->body.create.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
|
||||
}
|
||||
@ -134,13 +134,13 @@ TEST_F(Mkdir, DISABLED_entry_cache_negative_purge)
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFDIR | mode;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
|
||||
|
||||
@ -168,14 +168,14 @@ TEST_F(Mkdir, ok)
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.create.entry.attr.mode = S_IFDIR | mode;
|
||||
out->body.create.entry.nodeid = ino;
|
||||
out->body.create.entry.entry_valid = UINT64_MAX;
|
||||
out->body.create.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ void test_ok(mode_t mode, dev_t dev) {
|
||||
(0 == strcmp(RELPATH, name)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, create);
|
||||
out->body.create.entry.attr.mode = mode;
|
||||
@ -75,7 +75,7 @@ void test_ok(mode_t mode, dev_t dev) {
|
||||
out->body.create.entry.entry_valid = UINT64_MAX;
|
||||
out->body.create.entry.attr_valid = UINT64_MAX;
|
||||
out->body.create.entry.attr.rdev = dev;
|
||||
}));
|
||||
})));
|
||||
EXPECT_EQ(0, mknod(FULLPATH, mode, dev)) << strerror(errno);
|
||||
}
|
||||
|
||||
|
@ -105,28 +105,43 @@ const char* opcode2opname(uint32_t opcode)
|
||||
return (table[opcode]);
|
||||
}
|
||||
|
||||
std::function<void (const struct mockfs_buf_in *in, struct mockfs_buf_out *out)>
|
||||
ProcessMockerT
|
||||
ReturnErrno(int error)
|
||||
{
|
||||
return([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.error = -error;
|
||||
out->header.len = sizeof(out->header);
|
||||
return([=](auto in, auto &out) {
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.unique = in->header.unique;
|
||||
out0->header.error = -error;
|
||||
out0->header.len = sizeof(out0->header);
|
||||
out.push_back(out0);
|
||||
});
|
||||
}
|
||||
|
||||
/* Helper function used for returning negative cache entries for LOOKUP */
|
||||
std::function<void (const struct mockfs_buf_in *in, struct mockfs_buf_out *out)>
|
||||
ProcessMockerT
|
||||
ReturnNegativeCache(const struct timespec *entry_valid)
|
||||
{
|
||||
return([=](auto in, auto out) {
|
||||
return([=](auto in, auto &out) {
|
||||
/* nodeid means ENOENT and cache it */
|
||||
out->body.entry.nodeid = 0;
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.error = 0;
|
||||
out->body.entry.entry_valid = entry_valid->tv_sec;
|
||||
out->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->body.entry.nodeid = 0;
|
||||
out0->header.unique = in->header.unique;
|
||||
out0->header.error = 0;
|
||||
out0->body.entry.entry_valid = entry_valid->tv_sec;
|
||||
out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
|
||||
SET_OUT_HEADER_LEN(out0, entry);
|
||||
out.push_back(out0);
|
||||
});
|
||||
}
|
||||
|
||||
ProcessMockerT
|
||||
ReturnImmediate(std::function<void(const struct mockfs_buf_in *in,
|
||||
struct mockfs_buf_out *out)> f)
|
||||
{
|
||||
return([=](auto in, auto &out) {
|
||||
auto out0 = new mockfs_buf_out;
|
||||
f(in, out0);
|
||||
out.push_back(out0);
|
||||
});
|
||||
}
|
||||
|
||||
@ -309,14 +324,12 @@ void MockFS::kill_daemon() {
|
||||
|
||||
void MockFS::loop() {
|
||||
mockfs_buf_in *in;
|
||||
mockfs_buf_out *out;
|
||||
std::vector<mockfs_buf_out*> out;
|
||||
|
||||
in = (mockfs_buf_in*) malloc(sizeof(*in));
|
||||
out = (mockfs_buf_out*) malloc(sizeof(*out));
|
||||
ASSERT_TRUE(in != NULL);
|
||||
while (!quit) {
|
||||
bzero(in, sizeof(*in));
|
||||
bzero(out, sizeof(*out));
|
||||
read_request(in);
|
||||
if (quit)
|
||||
break;
|
||||
@ -332,19 +345,14 @@ void MockFS::loop() {
|
||||
*/
|
||||
process_default(in, out);
|
||||
}
|
||||
if (in->header.opcode == FUSE_FORGET) {
|
||||
/*Alone among the opcodes, FORGET expects no response*/
|
||||
continue;
|
||||
for (auto &it: out) {
|
||||
ASSERT_TRUE(write(m_fuse_fd, it, it->header.len) > 0 ||
|
||||
errno == EAGAIN)
|
||||
<< strerror(errno);
|
||||
delete it;
|
||||
}
|
||||
if (out->header.error == FUSE_NORESPONSE) {
|
||||
/* Used by tests of slow opcodes. No response ATM */
|
||||
continue;
|
||||
}
|
||||
ASSERT_TRUE(write(m_fuse_fd, out, out->header.len) > 0 ||
|
||||
errno == EAGAIN)
|
||||
<< strerror(errno);
|
||||
out.clear();
|
||||
}
|
||||
free(out);
|
||||
free(in);
|
||||
}
|
||||
|
||||
@ -369,10 +377,14 @@ bool MockFS::pid_ok(pid_t pid) {
|
||||
}
|
||||
}
|
||||
|
||||
void MockFS::process_default(const mockfs_buf_in *in, mockfs_buf_out* out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.error = -EOPNOTSUPP;
|
||||
out->header.len = sizeof(out->header);
|
||||
void MockFS::process_default(const mockfs_buf_in *in,
|
||||
std::vector<mockfs_buf_out*> &out)
|
||||
{
|
||||
auto out0 = new mockfs_buf_out;
|
||||
out0->header.unique = in->header.unique;
|
||||
out0->header.error = -EOPNOTSUPP;
|
||||
out0->header.len = sizeof(out0->header);
|
||||
out.push_back(out0);
|
||||
}
|
||||
|
||||
void MockFS::read_request(mockfs_buf_in *in) {
|
||||
|
@ -132,11 +132,16 @@ union fuse_payloads_out {
|
||||
struct mockfs_buf_out {
|
||||
fuse_out_header header;
|
||||
union fuse_payloads_out body;
|
||||
|
||||
/* Default constructor: zero everything */
|
||||
mockfs_buf_out() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
};
|
||||
|
||||
/* A function that can be invoked in place of MockFS::process */
|
||||
typedef std::function<void (const struct mockfs_buf_in *in,
|
||||
struct mockfs_buf_out *out)>
|
||||
std::vector<struct mockfs_buf_out*> &out)>
|
||||
ProcessMockerT;
|
||||
|
||||
/*
|
||||
@ -146,8 +151,12 @@ ProcessMockerT;
|
||||
ProcessMockerT ReturnErrno(int error);
|
||||
|
||||
/* Helper function used for returning negative cache entries for LOOKUP */
|
||||
std::function<void (const struct mockfs_buf_in *in, struct mockfs_buf_out *out)>
|
||||
ReturnNegativeCache(const struct timespec *entry_valid);
|
||||
ProcessMockerT ReturnNegativeCache(const struct timespec *entry_valid);
|
||||
|
||||
/* Helper function used for returning a single immediate response */
|
||||
ProcessMockerT ReturnImmediate(
|
||||
std::function<void(const struct mockfs_buf_in *in,
|
||||
struct mockfs_buf_out *out)> f);
|
||||
|
||||
/*
|
||||
* Fake FUSE filesystem
|
||||
@ -182,7 +191,8 @@ class MockFS {
|
||||
bool pid_ok(pid_t pid);
|
||||
|
||||
/* Default request handler */
|
||||
void process_default(const mockfs_buf_in*, mockfs_buf_out*);
|
||||
void process_default(const mockfs_buf_in*,
|
||||
std::vector<mockfs_buf_out*>&);
|
||||
|
||||
/* Entry point for the daemon thread */
|
||||
static void* service(void*);
|
||||
@ -207,12 +217,14 @@ class MockFS {
|
||||
/*
|
||||
* Request handler
|
||||
*
|
||||
* This method is expected to provide the response to each FUSE
|
||||
* operation. Responses must be immediate (so this method can't be used
|
||||
* for testing a daemon with queue depth > 1). Test cases must define
|
||||
* each response using Googlemock expectations
|
||||
* This method is expected to provide the responses to each FUSE
|
||||
* operation. For an immediate response, push one buffer into out.
|
||||
* For a delayed response, push nothing. For an immediate response
|
||||
* plus a delayed response to an earlier operation, push two bufs.
|
||||
* Test cases must define each response using Googlemock expectations
|
||||
*/
|
||||
MOCK_METHOD2(process, void(const mockfs_buf_in*, mockfs_buf_out*));
|
||||
MOCK_METHOD2(process, void(const mockfs_buf_in*,
|
||||
std::vector<mockfs_buf_out*>&));
|
||||
|
||||
/* Gracefully unmount */
|
||||
void unmount();
|
||||
|
@ -56,11 +56,11 @@ void test_ok(int os_flags, int fuse_flags) {
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(out->header);
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
}));
|
||||
})));
|
||||
|
||||
/* Until the attr cache is working, we may send an additional GETATTR */
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
@ -69,12 +69,12 @@ void test_ok(int os_flags, int fuse_flags) {
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, os_flags);
|
||||
EXPECT_LE(0, fd) << strerror(errno);
|
||||
|
@ -109,10 +109,10 @@ TEST_F(Opendir, open)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_LE(0, open(FULLPATH, O_DIRECTORY)) << strerror(errno);
|
||||
}
|
||||
@ -129,10 +129,10 @@ TEST_F(Opendir, opendir)
|
||||
return (in->header.opcode == FUSE_STATFS);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, statfs);
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -140,10 +140,10 @@ TEST_F(Opendir, opendir)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
}));
|
||||
})));
|
||||
|
||||
errno = 0;
|
||||
EXPECT_NE(NULL, opendir(FULLPATH)) << strerror(errno);
|
||||
|
@ -242,11 +242,11 @@ TEST_F(Read, mmap)
|
||||
in->body.read.size >= bufsize);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(struct fuse_out_header) + bufsize;
|
||||
memmove(out->body.bytes, CONTENTS, bufsize);
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_RDONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -411,11 +411,11 @@ TEST_F(Read, sendfile)
|
||||
in->body.read.size >= bufsize);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(struct fuse_out_header) + bufsize;
|
||||
memmove(out->body.bytes, CONTENTS, bufsize);
|
||||
}));
|
||||
})));
|
||||
|
||||
ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
|
||||
<< strerror(errno);
|
||||
|
@ -55,7 +55,7 @@ void expect_readdir(uint64_t ino, uint64_t off, vector<struct dirent> &ents)
|
||||
in->body.readdir.offset == off);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
struct fuse_dirent *fde = (struct fuse_dirent*)out->body.bytes;
|
||||
int i = 0;
|
||||
|
||||
@ -92,7 +92,7 @@ void expect_readdir(uint64_t ino, uint64_t off, vector<struct dirent> &ents)
|
||||
i++;
|
||||
}
|
||||
out->header.len += sizeof(out->header);
|
||||
}));
|
||||
})));
|
||||
|
||||
}
|
||||
};
|
||||
@ -208,11 +208,11 @@ TEST_F(Readdir, nodots)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.error = 0;
|
||||
out->header.len = sizeof(out->header);
|
||||
}));
|
||||
})));
|
||||
|
||||
errno = 0;
|
||||
dir = opendir(FULLPATH);
|
||||
|
@ -82,11 +82,11 @@ TEST_F(Readlink, ok)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
strlcpy(out->body.str, dst, sizeof(out->body.str));
|
||||
out->header.len = sizeof(out->header) + strlen(dst) + 1;
|
||||
}));
|
||||
})));
|
||||
|
||||
|
||||
EXPECT_EQ((ssize_t)strlen(dst) + 1,
|
||||
|
@ -75,11 +75,11 @@ TEST_F(ReleaseDir, dup)
|
||||
in->body.readdir.offset == 0);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.error = 0;
|
||||
out->header.len = sizeof(out->header);
|
||||
}));
|
||||
})));
|
||||
expect_releasedir(ino, ReturnErrno(0));
|
||||
|
||||
dir = opendir(FULLPATH);
|
||||
|
@ -41,13 +41,14 @@ class Rmdir: public FuseTest {
|
||||
public:
|
||||
void expect_lookup(const char *relpath, uint64_t ino)
|
||||
{
|
||||
EXPECT_LOOKUP(1, relpath).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, relpath)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFDIR | 0755;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr.nlink = 2;
|
||||
}));
|
||||
})));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -51,13 +51,14 @@ TEST_F(Setattr, chmod)
|
||||
const mode_t oldmode = 0755;
|
||||
const mode_t newmode = 0644;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | oldmode;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr.mode = S_IFREG | oldmode;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([](auto in) {
|
||||
@ -69,12 +70,12 @@ TEST_F(Setattr, chmod)
|
||||
in->body.setattr.mode == newmode);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | newmode;
|
||||
}));
|
||||
})));
|
||||
EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno);
|
||||
}
|
||||
|
||||
@ -89,14 +90,15 @@ TEST_F(Setattr, chown)
|
||||
const uid_t olduser = 33;
|
||||
const uid_t newuser = 44;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr.gid = oldgroup;
|
||||
out->body.entry.attr.uid = olduser;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([](auto in) {
|
||||
@ -109,14 +111,14 @@ TEST_F(Setattr, chown)
|
||||
in->body.setattr.gid == newgroup);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
out->body.attr.attr.uid = newuser;
|
||||
out->body.attr.attr.gid = newgroup;
|
||||
}));
|
||||
})));
|
||||
EXPECT_EQ(0, chown(FULLPATH, newuser, newgroup)) << strerror(errno);
|
||||
}
|
||||
|
||||
@ -133,14 +135,15 @@ TEST_F(Setattr, eperm)
|
||||
const char RELPATH[] = "some_file.txt";
|
||||
const uint64_t ino = 42;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0777;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr.uid = in->header.uid;
|
||||
out->body.entry.attr.gid = in->header.gid;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([](auto in) {
|
||||
@ -163,13 +166,14 @@ TEST_F(Setattr, fchmod)
|
||||
const mode_t oldmode = 0755;
|
||||
const mode_t newmode = 0644;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | oldmode;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -177,11 +181,11 @@ TEST_F(Setattr, fchmod)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(out->header);
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
}));
|
||||
})));
|
||||
|
||||
/* Until the attr cache is working, we may send an additional GETATTR */
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
@ -190,12 +194,12 @@ TEST_F(Setattr, fchmod)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | oldmode;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -207,12 +211,12 @@ TEST_F(Setattr, fchmod)
|
||||
in->body.setattr.mode == newmode);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | newmode;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_RDONLY);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -231,14 +235,15 @@ TEST_F(Setattr, ftruncate)
|
||||
const off_t oldsize = 99;
|
||||
const off_t newsize = 12345;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH)
|
||||
.WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0755;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr_valid = UINT64_MAX;
|
||||
out->body.entry.attr.size = oldsize;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -246,12 +251,12 @@ TEST_F(Setattr, ftruncate)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(out->header);
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
out->body.open.fh = fh;
|
||||
}));
|
||||
})));
|
||||
|
||||
/* Until the attr cache is working, we may send an additional GETATTR */
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
@ -260,13 +265,13 @@ TEST_F(Setattr, ftruncate)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0755;
|
||||
out->body.attr.attr.size = oldsize;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -278,13 +283,13 @@ TEST_F(Setattr, ftruncate)
|
||||
in->body.setattr.fh == fh);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0755;
|
||||
out->body.attr.attr.size = newsize;
|
||||
}));
|
||||
})));
|
||||
|
||||
fd = open(FULLPATH, O_RDWR);
|
||||
ASSERT_LE(0, fd) << strerror(errno);
|
||||
@ -300,13 +305,13 @@ TEST_F(Setattr, truncate) {
|
||||
const uint64_t oldsize = 100'000'000;
|
||||
const uint64_t newsize = 20'000'000;
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr.size = oldsize;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([](auto in) {
|
||||
@ -318,13 +323,13 @@ TEST_F(Setattr, truncate) {
|
||||
in->body.setattr.size == newsize);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
out->body.attr.attr.size = newsize;
|
||||
}));
|
||||
})));
|
||||
EXPECT_EQ(0, truncate(FULLPATH, newsize)) << strerror(errno);
|
||||
}
|
||||
|
||||
@ -342,7 +347,7 @@ TEST_F(Setattr, utimensat) {
|
||||
{.tv_sec = 7, .tv_nsec = 8},
|
||||
};
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
@ -352,7 +357,7 @@ TEST_F(Setattr, utimensat) {
|
||||
out->body.entry.attr.atimensec = oldtimes[0].tv_nsec;
|
||||
out->body.entry.attr.mtime = oldtimes[1].tv_sec;
|
||||
out->body.entry.attr.mtimensec = oldtimes[1].tv_nsec;
|
||||
}));
|
||||
})));
|
||||
|
||||
/*
|
||||
* Until bug 235775 is fixed, utimensat will make an extra FUSE_GETATTR
|
||||
@ -364,7 +369,7 @@ TEST_F(Setattr, utimensat) {
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
@ -373,7 +378,7 @@ TEST_F(Setattr, utimensat) {
|
||||
out->body.attr.attr.atimensec = oldtimes[0].tv_nsec;
|
||||
out->body.attr.attr.mtime = oldtimes[1].tv_sec;
|
||||
out->body.attr.attr.mtimensec = oldtimes[1].tv_nsec;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -390,7 +395,7 @@ TEST_F(Setattr, utimensat) {
|
||||
newtimes[1].tv_nsec);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
@ -399,7 +404,7 @@ TEST_F(Setattr, utimensat) {
|
||||
out->body.attr.attr.atimensec = newtimes[0].tv_nsec;
|
||||
out->body.attr.attr.mtime = newtimes[1].tv_sec;
|
||||
out->body.attr.attr.mtimensec = newtimes[1].tv_nsec;
|
||||
}));
|
||||
})));
|
||||
EXPECT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0))
|
||||
<< strerror(errno);
|
||||
}
|
||||
@ -418,7 +423,7 @@ TEST_F(Setattr, utimensat_mtime_only) {
|
||||
{.tv_sec = 7, .tv_nsec = 8},
|
||||
};
|
||||
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
|
||||
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFREG | 0644;
|
||||
@ -428,7 +433,7 @@ TEST_F(Setattr, utimensat_mtime_only) {
|
||||
out->body.entry.attr.atimensec = oldtimes[0].tv_nsec;
|
||||
out->body.entry.attr.mtime = oldtimes[1].tv_sec;
|
||||
out->body.entry.attr.mtimensec = oldtimes[1].tv_nsec;
|
||||
}));
|
||||
})));
|
||||
|
||||
/*
|
||||
* Until bug 235775 is fixed, utimensat will make an extra FUSE_GETATTR
|
||||
@ -440,7 +445,7 @@ TEST_F(Setattr, utimensat_mtime_only) {
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
@ -449,7 +454,7 @@ TEST_F(Setattr, utimensat_mtime_only) {
|
||||
out->body.attr.attr.atimensec = oldtimes[0].tv_nsec;
|
||||
out->body.attr.attr.mtime = oldtimes[1].tv_sec;
|
||||
out->body.attr.attr.mtimensec = oldtimes[1].tv_nsec;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -463,7 +468,7 @@ TEST_F(Setattr, utimensat_mtime_only) {
|
||||
newtimes[1].tv_nsec);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
@ -472,7 +477,7 @@ TEST_F(Setattr, utimensat_mtime_only) {
|
||||
out->body.attr.attr.atimensec = oldtimes[0].tv_nsec;
|
||||
out->body.attr.attr.mtime = newtimes[1].tv_sec;
|
||||
out->body.attr.attr.mtimensec = newtimes[1].tv_nsec;
|
||||
}));
|
||||
})));
|
||||
EXPECT_EQ(0, utimensat(AT_FDCWD, FULLPATH, &newtimes[0], 0))
|
||||
<< strerror(errno);
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ TEST_F(Statfs, ok)
|
||||
return (in->header.opcode == FUSE_STATFS);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, statfs);
|
||||
out->body.statfs.st.blocks = 1000;
|
||||
@ -96,7 +96,7 @@ TEST_F(Statfs, ok)
|
||||
out->body.statfs.st.ffree = 6;
|
||||
out->body.statfs.st.namelen = 128;
|
||||
out->body.statfs.st.frsize = 1024;
|
||||
}));
|
||||
})));
|
||||
|
||||
ASSERT_NE(NULL, getcwd(mp, PATH_MAX)) << strerror(errno);
|
||||
strlcat(mp, "/mountpoint", PATH_MAX);
|
||||
|
@ -80,12 +80,12 @@ TEST_F(Symlink, ok)
|
||||
(0 == strcmp(name, RELPATH)));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = S_IFLNK | 0777;
|
||||
out->body.entry.nodeid = ino;
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_EQ(0, symlink(dst, FULLPATH)) << strerror(errno);
|
||||
}
|
||||
|
@ -93,14 +93,14 @@ void FuseTest::expect_getattr(uint64_t ino, uint64_t size)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
out->body.attr.attr.size = size;
|
||||
out->body.attr.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
}
|
||||
|
||||
void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
|
||||
@ -108,14 +108,14 @@ void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
|
||||
{
|
||||
EXPECT_LOOKUP(1, relpath)
|
||||
.Times(times)
|
||||
.WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, entry);
|
||||
out->body.entry.attr.mode = mode;
|
||||
out->body.entry.nodeid = ino;
|
||||
out->body.entry.attr.nlink = 1;
|
||||
out->body.entry.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
}
|
||||
|
||||
void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
|
||||
@ -127,13 +127,13 @@ void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).Times(times)
|
||||
.WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(out->header);
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
out->body.open.fh = FH;
|
||||
out->body.open.open_flags = flags;
|
||||
}));
|
||||
})));
|
||||
}
|
||||
|
||||
void FuseTest::expect_opendir(uint64_t ino)
|
||||
@ -143,10 +143,10 @@ void FuseTest::expect_opendir(uint64_t ino)
|
||||
return (in->header.opcode == FUSE_STATFS);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, statfs);
|
||||
}));
|
||||
})));
|
||||
|
||||
EXPECT_CALL(*m_mock, process(
|
||||
ResultOf([=](auto in) {
|
||||
@ -154,12 +154,12 @@ void FuseTest::expect_opendir(uint64_t ino)
|
||||
in->header.nodeid == ino);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(out->header);
|
||||
SET_OUT_HEADER_LEN(out, open);
|
||||
out->body.open.fh = FH;
|
||||
}));
|
||||
})));
|
||||
}
|
||||
|
||||
void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
|
||||
@ -174,11 +174,11 @@ void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
|
||||
in->body.read.size == isize);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
out->header.len = sizeof(struct fuse_out_header) + osize;
|
||||
memmove(out->body.bytes, contents, osize);
|
||||
})).RetiresOnSaturation();
|
||||
}))).RetiresOnSaturation();
|
||||
}
|
||||
|
||||
void FuseTest::expect_release(uint64_t ino, int times, int error)
|
||||
@ -217,11 +217,11 @@ void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
|
||||
0 == bcmp(buf, contents, isize));
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillOnce(Invoke([=](auto in, auto out) {
|
||||
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, write);
|
||||
out->body.write.size = osize;
|
||||
}));
|
||||
})));
|
||||
}
|
||||
|
||||
static void usage(char* progname) {
|
||||
|
@ -503,11 +503,11 @@ TEST_F(WriteBack, close)
|
||||
return (in->header.opcode == FUSE_SETATTR);
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
}));
|
||||
})));
|
||||
expect_release(ino, ReturnErrno(0));
|
||||
|
||||
fd = open(FULLPATH, O_RDWR);
|
||||
@ -635,14 +635,14 @@ TEST_F(WriteThrough, DISABLED_update_file_size)
|
||||
}, Eq(true)),
|
||||
_)
|
||||
).Times(2)
|
||||
.WillRepeatedly(Invoke([=](auto in, auto out) {
|
||||
.WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto out) {
|
||||
out->header.unique = in->header.unique;
|
||||
SET_OUT_HEADER_LEN(out, attr);
|
||||
out->body.attr.attr.ino = ino; // Must match nodeid
|
||||
out->body.attr.attr.mode = S_IFREG | 0644;
|
||||
out->body.attr.attr.size = 0;
|
||||
out->body.attr.attr_valid = UINT64_MAX;
|
||||
}));
|
||||
})));
|
||||
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
|
||||
|
||||
fd = open(FULLPATH, O_RDWR);
|
||||
|
Loading…
Reference in New Issue
Block a user