Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Mar 2019 17:20:24 +0000 (UTC)
From:      Alan Somers <asomers@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r345140 - projects/fuse2/tests/sys/fs/fuse
Message-ID:  <201903141720.x2EHKOo2096150@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201903141720.x2EHKOo2096150>