svn commit: r345140 - projects/fuse2/tests/sys/fs/fuse
Alan Somers
asomers at FreeBSD.org
Thu Mar 14 17:20:27 UTC 2019
Author: asomers
Date: Thu Mar 14 17:20:24 2019
New Revision: 345140
URL: https://svnweb.freebsd.org/changeset/base/345140
Log:
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
Added:
projects/fuse2/tests/sys/fs/fuse/interrupt.cc (contents, props changed)
Modified:
projects/fuse2/tests/sys/fs/fuse/Makefile
projects/fuse2/tests/sys/fs/fuse/create.cc
projects/fuse2/tests/sys/fs/fuse/fsync.cc
projects/fuse2/tests/sys/fs/fuse/getattr.cc
projects/fuse2/tests/sys/fs/fuse/link.cc
projects/fuse2/tests/sys/fs/fuse/locks.cc
projects/fuse2/tests/sys/fs/fuse/lookup.cc
projects/fuse2/tests/sys/fs/fuse/mkdir.cc
projects/fuse2/tests/sys/fs/fuse/mknod.cc
projects/fuse2/tests/sys/fs/fuse/mockfs.cc
projects/fuse2/tests/sys/fs/fuse/mockfs.hh
projects/fuse2/tests/sys/fs/fuse/open.cc
projects/fuse2/tests/sys/fs/fuse/opendir.cc
projects/fuse2/tests/sys/fs/fuse/read.cc
projects/fuse2/tests/sys/fs/fuse/readdir.cc
projects/fuse2/tests/sys/fs/fuse/readlink.cc
projects/fuse2/tests/sys/fs/fuse/releasedir.cc
projects/fuse2/tests/sys/fs/fuse/rmdir.cc
projects/fuse2/tests/sys/fs/fuse/setattr.cc
projects/fuse2/tests/sys/fs/fuse/statfs.cc
projects/fuse2/tests/sys/fs/fuse/symlink.cc
projects/fuse2/tests/sys/fs/fuse/utils.cc
projects/fuse2/tests/sys/fs/fuse/write.cc
Modified: projects/fuse2/tests/sys/fs/fuse/Makefile
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/Makefile Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/Makefile Thu Mar 14 17:20:24 2019 (r345140)
@@ -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
@@ -59,6 +63,11 @@ SRCS.getattr+= getattr.cc
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
Modified: projects/fuse2/tests/sys/fs/fuse/create.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/create.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/create.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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);
Modified: projects/fuse2/tests/sys/fs/fuse/fsync.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/fsync.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/fsync.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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);
Modified: projects/fuse2/tests/sys/fs/fuse/getattr.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/getattr.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/getattr.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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);
Added: projects/fuse2/tests/sys/fs/fuse/interrupt.cc
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ projects/fuse2/tests/sys/fs/fuse/interrupt.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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 */
+}
Modified: projects/fuse2/tests/sys/fs/fuse/link.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/link.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/link.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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);
}
Modified: projects/fuse2/tests/sys/fs/fuse/locks.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/locks.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/locks.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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);
Modified: projects/fuse2/tests/sys/fs/fuse/lookup.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/lookup.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/lookup.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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.
Modified: projects/fuse2/tests/sys/fs/fuse/mkdir.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/mkdir.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/mkdir.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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);
}
Modified: projects/fuse2/tests/sys/fs/fuse/mknod.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/mknod.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/mknod.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -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);
}
Modified: projects/fuse2/tests/sys/fs/fuse/mockfs.cc
==============================================================================
--- projects/fuse2/tests/sys/fs/fuse/mockfs.cc Thu Mar 14 17:18:00 2019 (r345139)
+++ projects/fuse2/tests/sys/fs/fuse/mockfs.cc Thu Mar 14 17:20:24 2019 (r345140)
@@ -105,31 +105,46 @@ 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);
+ });
+}
+
void sigint_handler(int __unused sig) {
quit = 1;
}
@@ -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)
+{
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-projects
mailing list