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:
Alan Somers 2019-03-14 17:20:24 +00:00
parent 3592c9fe12
commit 94ef9d62cc
24 changed files with 551 additions and 194 deletions

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View 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 */
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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.

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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;
}));
})));
}
};

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);