xref: /freebsd/tests/sys/fs/fusefs/interrupt.cc (revision 8e765737)
19821f1d3SAlan Somers /*-
29821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
39821f1d3SAlan Somers  *
49821f1d3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers  *
69821f1d3SAlan Somers  * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers  * from the FreeBSD Foundation.
89821f1d3SAlan Somers  *
99821f1d3SAlan Somers  * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers  * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers  * are met:
129821f1d3SAlan Somers  * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers  *    documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers  *
189821f1d3SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers  * SUCH DAMAGE.
291fa8ebfbSAlan Somers  *
301fa8ebfbSAlan Somers  * $FreeBSD$
319821f1d3SAlan Somers  */
329821f1d3SAlan Somers 
339821f1d3SAlan Somers extern "C" {
34f0f7fc1bSAlan Somers #include <sys/types.h>
35f0f7fc1bSAlan Somers #include <sys/extattr.h>
36f528b38fSAlan Somers #include <sys/mman.h>
37a1542146SAlan Somers #include <sys/wait.h>
389821f1d3SAlan Somers #include <fcntl.h>
399821f1d3SAlan Somers #include <pthread.h>
403d070fdcSAlan Somers #include <semaphore.h>
419821f1d3SAlan Somers #include <signal.h>
429821f1d3SAlan Somers }
439821f1d3SAlan Somers 
449821f1d3SAlan Somers #include "mockfs.hh"
459821f1d3SAlan Somers #include "utils.hh"
469821f1d3SAlan Somers 
479821f1d3SAlan Somers using namespace testing;
489821f1d3SAlan Somers 
49f0f7fc1bSAlan Somers /* Initial size of files used by these tests */
50f0f7fc1bSAlan Somers const off_t FILESIZE = 1000;
519a177029SAlan Somers /* Access mode used by all directories in these tests */
529a177029SAlan Somers const mode_t MODE = 0755;
539a177029SAlan Somers const char FULLDIRPATH0[] = "mountpoint/some_dir";
549a177029SAlan Somers const char RELDIRPATH0[] = "some_dir";
55102c7ac0SAlan Somers const char FULLDIRPATH1[] = "mountpoint/other_dir";
56102c7ac0SAlan Somers const char RELDIRPATH1[] = "other_dir";
57f0f7fc1bSAlan Somers 
58f528b38fSAlan Somers static sem_t *blocked_semaphore;
59268c28edSAlan Somers static sem_t *signaled_semaphore;
60268c28edSAlan Somers 
61f528b38fSAlan Somers static bool killer_should_sleep = false;
62f528b38fSAlan Somers 
639821f1d3SAlan Somers /* Don't do anything; all we care about is that the syscall gets interrupted */
649821f1d3SAlan Somers void sigusr2_handler(int __unused sig) {
65723c7768SAlan Somers 	if (verbosity > 1) {
66723c7768SAlan Somers 		printf("Signaled!  thread %p\n", pthread_self());
67723c7768SAlan Somers 	}
68723c7768SAlan Somers 
699821f1d3SAlan Somers }
709821f1d3SAlan Somers 
719821f1d3SAlan Somers void* killer(void* target) {
72f528b38fSAlan Somers 	/* Wait until the main thread is blocked in fdisp_wait_answ */
73f528b38fSAlan Somers 	if (killer_should_sleep)
74a87257acSAlan Somers 		nap();
75f528b38fSAlan Somers 	else
76f528b38fSAlan Somers 		sem_wait(blocked_semaphore);
779821f1d3SAlan Somers 	if (verbosity > 1)
78723c7768SAlan Somers 		printf("Signalling!  thread %p\n", target);
79723c7768SAlan Somers 	pthread_kill((pthread_t)target, SIGUSR2);
80268c28edSAlan Somers 	if (signaled_semaphore != NULL)
81268c28edSAlan Somers 		sem_post(signaled_semaphore);
829821f1d3SAlan Somers 
839821f1d3SAlan Somers 	return(NULL);
849821f1d3SAlan Somers }
859821f1d3SAlan Somers 
869821f1d3SAlan Somers class Interrupt: public FuseTest {
879821f1d3SAlan Somers public:
889821f1d3SAlan Somers pthread_t m_child;
899821f1d3SAlan Somers 
909821f1d3SAlan Somers Interrupt(): m_child(NULL) {};
919821f1d3SAlan Somers 
929821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino)
939821f1d3SAlan Somers {
94f0f7fc1bSAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, FILESIZE, 1);
95a1542146SAlan Somers }
96a1542146SAlan Somers 
97a1542146SAlan Somers /*
989a177029SAlan Somers  * Expect a FUSE_MKDIR but don't reply.  Instead, just record the unique value
999a177029SAlan Somers  * to the provided pointer
1009a177029SAlan Somers  */
1019a177029SAlan Somers void expect_mkdir(uint64_t *mkdir_unique)
1029a177029SAlan Somers {
1039a177029SAlan Somers 	EXPECT_CALL(*m_mock, process(
1049a177029SAlan Somers 		ResultOf([=](auto in) {
10529edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
1069a177029SAlan Somers 		}, Eq(true)),
1079a177029SAlan Somers 		_)
1089a177029SAlan Somers 	).WillOnce(Invoke([=](auto in, auto &out __unused) {
10929edc611SAlan Somers 		*mkdir_unique = in.header.unique;
110f528b38fSAlan Somers 		sem_post(blocked_semaphore);
1119a177029SAlan Somers 	}));
1129a177029SAlan Somers }
1139a177029SAlan Somers 
1149a177029SAlan Somers /*
115a1542146SAlan Somers  * Expect a FUSE_READ but don't reply.  Instead, just record the unique value
116a1542146SAlan Somers  * to the provided pointer
117a1542146SAlan Somers  */
118a1542146SAlan Somers void expect_read(uint64_t ino, uint64_t *read_unique)
119a1542146SAlan Somers {
120a1542146SAlan Somers 	EXPECT_CALL(*m_mock, process(
121a1542146SAlan Somers 		ResultOf([=](auto in) {
12229edc611SAlan Somers 			return (in.header.opcode == FUSE_READ &&
12329edc611SAlan Somers 				in.header.nodeid == ino);
124a1542146SAlan Somers 		}, Eq(true)),
125a1542146SAlan Somers 		_)
126a1542146SAlan Somers 	).WillOnce(Invoke([=](auto in, auto &out __unused) {
12729edc611SAlan Somers 		*read_unique = in.header.unique;
128f528b38fSAlan Somers 		sem_post(blocked_semaphore);
129a1542146SAlan Somers 	}));
1309821f1d3SAlan Somers }
1319821f1d3SAlan Somers 
1329821f1d3SAlan Somers /*
1339821f1d3SAlan Somers  * Expect a FUSE_WRITE but don't reply.  Instead, just record the unique value
1349821f1d3SAlan Somers  * to the provided pointer
1359821f1d3SAlan Somers  */
1369821f1d3SAlan Somers void expect_write(uint64_t ino, uint64_t *write_unique)
1379821f1d3SAlan Somers {
1389821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
1399821f1d3SAlan Somers 		ResultOf([=](auto in) {
14029edc611SAlan Somers 			return (in.header.opcode == FUSE_WRITE &&
14129edc611SAlan Somers 				in.header.nodeid == ino);
1429821f1d3SAlan Somers 		}, Eq(true)),
1439821f1d3SAlan Somers 		_)
1449821f1d3SAlan Somers 	).WillOnce(Invoke([=](auto in, auto &out __unused) {
14529edc611SAlan Somers 		*write_unique = in.header.unique;
146f528b38fSAlan Somers 		sem_post(blocked_semaphore);
1479821f1d3SAlan Somers 	}));
1489821f1d3SAlan Somers }
1499821f1d3SAlan Somers 
150f528b38fSAlan Somers void setup_interruptor(pthread_t target, bool sleep = false)
1519821f1d3SAlan Somers {
152a1542146SAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGUSR2, sigusr2_handler)) << strerror(errno);
153f528b38fSAlan Somers 	killer_should_sleep = sleep;
154268c28edSAlan Somers 	ASSERT_EQ(0, pthread_create(&m_child, NULL, killer, (void*)target))
1559821f1d3SAlan Somers 		<< strerror(errno);
1569821f1d3SAlan Somers }
1579821f1d3SAlan Somers 
158268c28edSAlan Somers void SetUp() {
159f528b38fSAlan Somers 	const int mprot = PROT_READ | PROT_WRITE;
160f528b38fSAlan Somers 	const int mflags = MAP_ANON | MAP_SHARED;
161f528b38fSAlan Somers 
162268c28edSAlan Somers 	signaled_semaphore = NULL;
163f528b38fSAlan Somers 
164f528b38fSAlan Somers 	blocked_semaphore = (sem_t*)mmap(NULL, sizeof(*blocked_semaphore),
165f528b38fSAlan Somers 		mprot, mflags, -1, 0);
166f528b38fSAlan Somers 	ASSERT_NE(MAP_FAILED, blocked_semaphore) << strerror(errno);
167f528b38fSAlan Somers 	ASSERT_EQ(0, sem_init(blocked_semaphore, 1, 0)) << strerror(errno);
168a81776c2SAlan Somers 	ASSERT_EQ(0, siginterrupt(SIGUSR2, 1));
169f528b38fSAlan Somers 
170268c28edSAlan Somers 	FuseTest::SetUp();
171268c28edSAlan Somers }
172268c28edSAlan Somers 
1739821f1d3SAlan Somers void TearDown() {
174723c7768SAlan Somers 	struct sigaction sa;
175723c7768SAlan Somers 
1769821f1d3SAlan Somers 	if (m_child != NULL) {
1779821f1d3SAlan Somers 		pthread_join(m_child, NULL);
1789821f1d3SAlan Somers 	}
179723c7768SAlan Somers 	bzero(&sa, sizeof(sa));
180723c7768SAlan Somers 	sa.sa_handler = SIG_DFL;
181723c7768SAlan Somers 	sigaction(SIGUSR2, &sa, NULL);
1829821f1d3SAlan Somers 
183f528b38fSAlan Somers 	sem_destroy(blocked_semaphore);
184f528b38fSAlan Somers 	munmap(blocked_semaphore, sizeof(*blocked_semaphore));
185f528b38fSAlan Somers 
1869821f1d3SAlan Somers 	FuseTest::TearDown();
1879821f1d3SAlan Somers }
1889821f1d3SAlan Somers };
1899821f1d3SAlan Somers 
190ed74f781SAlan Somers class Intr: public Interrupt {};
191ed74f781SAlan Somers 
192ed74f781SAlan Somers class Nointr: public Interrupt {
193ed74f781SAlan Somers 	void SetUp() {
194ed74f781SAlan Somers 		m_nointr = true;
195ed74f781SAlan Somers 		Interrupt::SetUp();
196ed74f781SAlan Somers 	}
197ed74f781SAlan Somers };
198ed74f781SAlan Somers 
199102c7ac0SAlan Somers static void* mkdir0(void* arg __unused) {
200102c7ac0SAlan Somers 	ssize_t r;
201102c7ac0SAlan Somers 
202102c7ac0SAlan Somers 	r = mkdir(FULLDIRPATH0, MODE);
203102c7ac0SAlan Somers 	if (r >= 0)
204102c7ac0SAlan Somers 		return 0;
205102c7ac0SAlan Somers 	else
206102c7ac0SAlan Somers 		return (void*)(intptr_t)errno;
207102c7ac0SAlan Somers }
208102c7ac0SAlan Somers 
209102c7ac0SAlan Somers static void* read1(void* arg) {
210102c7ac0SAlan Somers 	const size_t bufsize = FILESIZE;
211102c7ac0SAlan Somers 	char buf[bufsize];
212102c7ac0SAlan Somers 	int fd = (int)(intptr_t)arg;
213102c7ac0SAlan Somers 	ssize_t r;
214102c7ac0SAlan Somers 
215102c7ac0SAlan Somers 	r = read(fd, buf, bufsize);
216102c7ac0SAlan Somers 	if (r >= 0)
217102c7ac0SAlan Somers 		return 0;
218102c7ac0SAlan Somers 	else
219102c7ac0SAlan Somers 		return (void*)(intptr_t)errno;
220102c7ac0SAlan Somers }
221102c7ac0SAlan Somers 
2229821f1d3SAlan Somers /*
2239821f1d3SAlan Somers  * An interrupt operation that gets received after the original command is
2249821f1d3SAlan Somers  * complete should generate an EAGAIN response.
2259821f1d3SAlan Somers  */
2269821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
227ed74f781SAlan Somers TEST_F(Intr, already_complete)
2289821f1d3SAlan Somers {
2299821f1d3SAlan Somers 	uint64_t ino = 42;
2309821f1d3SAlan Somers 	pthread_t self;
2319a177029SAlan Somers 	uint64_t mkdir_unique = 0;
232fd182076SAlan Somers 	Sequence seq;
2339821f1d3SAlan Somers 
2349821f1d3SAlan Somers 	self = pthread_self();
2359821f1d3SAlan Somers 
236a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
237fd182076SAlan Somers 	.InSequence(seq)
238fd182076SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
2399a177029SAlan Somers 	expect_mkdir(&mkdir_unique);
2409821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
2419821f1d3SAlan Somers 		ResultOf([&](auto in) {
24229edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
24329edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
2449821f1d3SAlan Somers 		}, Eq(true)),
2459821f1d3SAlan Somers 		_)
2469821f1d3SAlan Somers 	).WillOnce(Invoke([&](auto in, auto &out) {
2479a177029SAlan Somers 		// First complete the mkdir request
24829edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
2499a177029SAlan Somers 		out0->header.unique = mkdir_unique;
25029edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
2519a177029SAlan Somers 		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
2529a177029SAlan Somers 		out0->body.create.entry.nodeid = ino;
25329edc611SAlan Somers 		out.push_back(std::move(out0));
2549821f1d3SAlan Somers 
2559821f1d3SAlan Somers 		// Then, respond EAGAIN to the interrupt request
25629edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
25729edc611SAlan Somers 		out1->header.unique = in.header.unique;
2589821f1d3SAlan Somers 		out1->header.error = -EAGAIN;
2599821f1d3SAlan Somers 		out1->header.len = sizeof(out1->header);
26029edc611SAlan Somers 		out.push_back(std::move(out1));
2619821f1d3SAlan Somers 	}));
262a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
263fd182076SAlan Somers 	.InSequence(seq)
26429edc611SAlan Somers 	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
265fd182076SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
26629edc611SAlan Somers 		out.body.entry.attr.mode = S_IFDIR | MODE;
26729edc611SAlan Somers 		out.body.entry.nodeid = ino;
26829edc611SAlan Somers 		out.body.entry.attr.nlink = 2;
269fd182076SAlan Somers 	})));
2709821f1d3SAlan Somers 
2719821f1d3SAlan Somers 	setup_interruptor(self);
2729a177029SAlan Somers 	EXPECT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
273fd182076SAlan Somers 	/*
274fd182076SAlan Somers 	 * The final syscall simply ensures that the test's main thread doesn't
275fd182076SAlan Somers 	 * end before the daemon finishes responding to the FUSE_INTERRUPT.
276fd182076SAlan Somers 	 */
277fd182076SAlan Somers 	EXPECT_EQ(0, access(FULLDIRPATH0, F_OK)) << strerror(errno);
2789821f1d3SAlan Somers }
2799821f1d3SAlan Somers 
2809821f1d3SAlan Somers /*
281102c7ac0SAlan Somers  * If a FUSE file system returns ENOSYS for a FUSE_INTERRUPT operation, the
282102c7ac0SAlan Somers  * kernel should not attempt to interrupt any other operations on that mount
283102c7ac0SAlan Somers  * point.
284102c7ac0SAlan Somers  */
285ed74f781SAlan Somers TEST_F(Intr, enosys)
286102c7ac0SAlan Somers {
287102c7ac0SAlan Somers 	uint64_t ino0 = 42, ino1 = 43;;
288102c7ac0SAlan Somers 	uint64_t mkdir_unique;
289102c7ac0SAlan Somers 	pthread_t self, th0;
290102c7ac0SAlan Somers 	sem_t sem0, sem1;
291102c7ac0SAlan Somers 	void *thr0_value;
292102c7ac0SAlan Somers 	Sequence seq;
293102c7ac0SAlan Somers 
294102c7ac0SAlan Somers 	self = pthread_self();
295102c7ac0SAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
296102c7ac0SAlan Somers 	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
297102c7ac0SAlan Somers 
298a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH1)
299a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
300a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
301a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
302102c7ac0SAlan Somers 	expect_mkdir(&mkdir_unique);
303102c7ac0SAlan Somers 	EXPECT_CALL(*m_mock, process(
304102c7ac0SAlan Somers 		ResultOf([&](auto in) {
30529edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
30629edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
307102c7ac0SAlan Somers 		}, Eq(true)),
308102c7ac0SAlan Somers 		_)
309102c7ac0SAlan Somers 	).InSequence(seq)
310102c7ac0SAlan Somers 	.WillOnce(Invoke([&](auto in, auto &out) {
311fd182076SAlan Somers 		// reject FUSE_INTERRUPT and respond to the FUSE_MKDIR
31229edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
31329edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
314102c7ac0SAlan Somers 
31529edc611SAlan Somers 		out0->header.unique = in.header.unique;
316102c7ac0SAlan Somers 		out0->header.error = -ENOSYS;
317102c7ac0SAlan Somers 		out0->header.len = sizeof(out0->header);
31829edc611SAlan Somers 		out.push_back(std::move(out0));
319102c7ac0SAlan Somers 
32029edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out1, entry);
321102c7ac0SAlan Somers 		out1->body.create.entry.attr.mode = S_IFDIR | MODE;
322102c7ac0SAlan Somers 		out1->body.create.entry.nodeid = ino1;
323102c7ac0SAlan Somers 		out1->header.unique = mkdir_unique;
32429edc611SAlan Somers 		out.push_back(std::move(out1));
325102c7ac0SAlan Somers 	}));
326102c7ac0SAlan Somers 	EXPECT_CALL(*m_mock, process(
327102c7ac0SAlan Somers 		ResultOf([&](auto in) {
32829edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
329102c7ac0SAlan Somers 		}, Eq(true)),
330102c7ac0SAlan Somers 		_)
331102c7ac0SAlan Somers 	).InSequence(seq)
332102c7ac0SAlan Somers 	.WillOnce(Invoke([&](auto in, auto &out) {
33329edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
334102c7ac0SAlan Somers 
335102c7ac0SAlan Somers 		sem_post(&sem0);
336102c7ac0SAlan Somers 		sem_wait(&sem1);
337102c7ac0SAlan Somers 
33829edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
339102c7ac0SAlan Somers 		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
340102c7ac0SAlan Somers 		out0->body.create.entry.nodeid = ino0;
34129edc611SAlan Somers 		out0->header.unique = in.header.unique;
34229edc611SAlan Somers 		out.push_back(std::move(out0));
343102c7ac0SAlan Somers 	}));
344102c7ac0SAlan Somers 
345102c7ac0SAlan Somers 	setup_interruptor(self);
346102c7ac0SAlan Somers 	/* First mkdir operation should finish synchronously */
347102c7ac0SAlan Somers 	ASSERT_EQ(0, mkdir(FULLDIRPATH1, MODE)) << strerror(errno);
348102c7ac0SAlan Somers 
349102c7ac0SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
350102c7ac0SAlan Somers 		<< strerror(errno);
351102c7ac0SAlan Somers 
352102c7ac0SAlan Somers 	sem_wait(&sem0);
353102c7ac0SAlan Somers 	/*
354102c7ac0SAlan Somers 	 * th0 should be blocked waiting for the fuse daemon thread.
355102c7ac0SAlan Somers 	 * Signal it.  No FUSE_INTERRUPT should result
356102c7ac0SAlan Somers 	 */
357102c7ac0SAlan Somers 	pthread_kill(th0, SIGUSR1);
358102c7ac0SAlan Somers 	/* Allow the daemon thread to proceed */
359102c7ac0SAlan Somers 	sem_post(&sem1);
360102c7ac0SAlan Somers 	pthread_join(th0, &thr0_value);
361102c7ac0SAlan Somers 	/* Second mkdir should've finished without error */
362102c7ac0SAlan Somers 	EXPECT_EQ(0, (intptr_t)thr0_value);
363102c7ac0SAlan Somers }
364102c7ac0SAlan Somers 
365102c7ac0SAlan Somers /*
3669821f1d3SAlan Somers  * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and
3679821f1d3SAlan Somers  * complete the original operation whenever it damn well pleases.
3689821f1d3SAlan Somers  */
3699821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
370ed74f781SAlan Somers TEST_F(Intr, ignore)
3719821f1d3SAlan Somers {
3729821f1d3SAlan Somers 	uint64_t ino = 42;
3739821f1d3SAlan Somers 	pthread_t self;
3749a177029SAlan Somers 	uint64_t mkdir_unique;
3759821f1d3SAlan Somers 
3769821f1d3SAlan Somers 	self = pthread_self();
3779821f1d3SAlan Somers 
378a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
379a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
3809a177029SAlan Somers 	expect_mkdir(&mkdir_unique);
3819821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
382723c7768SAlan Somers 		ResultOf([&](auto in) {
38329edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
38429edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
3859821f1d3SAlan Somers 		}, Eq(true)),
3869821f1d3SAlan Somers 		_)
3879821f1d3SAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out) {
388102c7ac0SAlan Somers 		// Ignore FUSE_INTERRUPT; respond to the FUSE_MKDIR
38929edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
3909a177029SAlan Somers 		out0->header.unique = mkdir_unique;
39129edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
3929a177029SAlan Somers 		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
3939a177029SAlan Somers 		out0->body.create.entry.nodeid = ino;
39429edc611SAlan Somers 		out.push_back(std::move(out0));
3959821f1d3SAlan Somers 	}));
3969821f1d3SAlan Somers 
3979821f1d3SAlan Somers 	setup_interruptor(self);
3989a177029SAlan Somers 	ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
3999821f1d3SAlan Somers }
4009821f1d3SAlan Somers 
4013d070fdcSAlan Somers /*
402f0f7fc1bSAlan Somers  * A restartable operation (basically, anything except write or setextattr)
403f0f7fc1bSAlan Somers  * that hasn't yet been sent to userland can be interrupted without sending
404f0f7fc1bSAlan Somers  * FUSE_INTERRUPT, and will be automatically restarted.
405f0f7fc1bSAlan Somers  */
406ed74f781SAlan Somers TEST_F(Intr, in_kernel_restartable)
407f0f7fc1bSAlan Somers {
408f0f7fc1bSAlan Somers 	const char FULLPATH1[] = "mountpoint/other_file.txt";
409f0f7fc1bSAlan Somers 	const char RELPATH1[] = "other_file.txt";
410f0f7fc1bSAlan Somers 	uint64_t ino0 = 42, ino1 = 43;
4119a177029SAlan Somers 	int fd1;
412f0f7fc1bSAlan Somers 	pthread_t self, th0, th1;
413f0f7fc1bSAlan Somers 	sem_t sem0, sem1;
414f0f7fc1bSAlan Somers 	void *thr0_value, *thr1_value;
415f0f7fc1bSAlan Somers 
416f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
417f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
418f0f7fc1bSAlan Somers 	self = pthread_self();
419f0f7fc1bSAlan Somers 
420a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
421a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
422f0f7fc1bSAlan Somers 	expect_lookup(RELPATH1, ino1);
423f0f7fc1bSAlan Somers 	expect_open(ino1, 0, 1);
424f0f7fc1bSAlan Somers 	EXPECT_CALL(*m_mock, process(
425f0f7fc1bSAlan Somers 		ResultOf([=](auto in) {
42629edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
427f0f7fc1bSAlan Somers 		}, Eq(true)),
428f0f7fc1bSAlan Somers 		_)
42929edc611SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
430f0f7fc1bSAlan Somers 		/* Let the next write proceed */
431f0f7fc1bSAlan Somers 		sem_post(&sem1);
432f0f7fc1bSAlan Somers 		/* Pause the daemon thread so it won't read the next op */
433f0f7fc1bSAlan Somers 		sem_wait(&sem0);
434f0f7fc1bSAlan Somers 
4359a177029SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
43629edc611SAlan Somers 		out.body.create.entry.attr.mode = S_IFDIR | MODE;
43729edc611SAlan Somers 		out.body.create.entry.nodeid = ino0;
438f0f7fc1bSAlan Somers 	})));
439f0f7fc1bSAlan Somers 	FuseTest::expect_read(ino1, 0, FILESIZE, 0, NULL);
440f0f7fc1bSAlan Somers 
441f0f7fc1bSAlan Somers 	fd1 = open(FULLPATH1, O_RDONLY);
442f0f7fc1bSAlan Somers 	ASSERT_LE(0, fd1) << strerror(errno);
443f0f7fc1bSAlan Somers 
444f0f7fc1bSAlan Somers 	/* Use a separate thread for each operation */
4459a177029SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
446f0f7fc1bSAlan Somers 		<< strerror(errno);
447f0f7fc1bSAlan Somers 
448f0f7fc1bSAlan Somers 	sem_wait(&sem1);	/* Sequence the two operations */
449f0f7fc1bSAlan Somers 
450f0f7fc1bSAlan Somers 	ASSERT_EQ(0, pthread_create(&th1, NULL, read1, (void*)(intptr_t)fd1))
451f0f7fc1bSAlan Somers 		<< strerror(errno);
452f0f7fc1bSAlan Somers 
453f528b38fSAlan Somers 	setup_interruptor(self, true);
454f0f7fc1bSAlan Somers 
455f0f7fc1bSAlan Somers 	pause();		/* Wait for signal */
456f0f7fc1bSAlan Somers 
457f0f7fc1bSAlan Somers 	/* Unstick the daemon */
458f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
459f0f7fc1bSAlan Somers 
460f0f7fc1bSAlan Somers 	/* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
461a87257acSAlan Somers 	nap();
462f0f7fc1bSAlan Somers 
463f0f7fc1bSAlan Somers 	pthread_join(th1, &thr1_value);
464f0f7fc1bSAlan Somers 	pthread_join(th0, &thr0_value);
465f0f7fc1bSAlan Somers 	EXPECT_EQ(0, (intptr_t)thr1_value);
466f0f7fc1bSAlan Somers 	EXPECT_EQ(0, (intptr_t)thr0_value);
467f0f7fc1bSAlan Somers 	sem_destroy(&sem1);
468f0f7fc1bSAlan Somers 	sem_destroy(&sem0);
4698e765737SAlan Somers 
4708e765737SAlan Somers 	leak(fd1);
471f0f7fc1bSAlan Somers }
472f0f7fc1bSAlan Somers 
473f0f7fc1bSAlan Somers /*
4749a177029SAlan Somers  * An operation that hasn't yet been sent to userland can be interrupted
4759a177029SAlan Somers  * without sending FUSE_INTERRUPT.  If it's a non-restartable operation (write
4769a177029SAlan Somers  * or setextattr) it will return EINTR.
477f0f7fc1bSAlan Somers  */
478ed74f781SAlan Somers TEST_F(Intr, in_kernel_nonrestartable)
479f0f7fc1bSAlan Somers {
480f0f7fc1bSAlan Somers 	const char FULLPATH1[] = "mountpoint/other_file.txt";
481f0f7fc1bSAlan Somers 	const char RELPATH1[] = "other_file.txt";
482f0f7fc1bSAlan Somers 	const char value[] = "whatever";
483f0f7fc1bSAlan Somers 	ssize_t value_len = strlen(value) + 1;
484f0f7fc1bSAlan Somers 	uint64_t ino0 = 42, ino1 = 43;
485f0f7fc1bSAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
4869a177029SAlan Somers 	int fd1;
487f0f7fc1bSAlan Somers 	pthread_t self, th0;
488f0f7fc1bSAlan Somers 	sem_t sem0, sem1;
489f0f7fc1bSAlan Somers 	void *thr0_value;
490f0f7fc1bSAlan Somers 	ssize_t r;
491f0f7fc1bSAlan Somers 
492f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
493f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
494f0f7fc1bSAlan Somers 	self = pthread_self();
495f0f7fc1bSAlan Somers 
496a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
497a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
498f0f7fc1bSAlan Somers 	expect_lookup(RELPATH1, ino1);
499f0f7fc1bSAlan Somers 	expect_open(ino1, 0, 1);
500f0f7fc1bSAlan Somers 	EXPECT_CALL(*m_mock, process(
501f0f7fc1bSAlan Somers 		ResultOf([=](auto in) {
50229edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
503f0f7fc1bSAlan Somers 		}, Eq(true)),
504f0f7fc1bSAlan Somers 		_)
50529edc611SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
506f0f7fc1bSAlan Somers 		/* Let the next write proceed */
507f0f7fc1bSAlan Somers 		sem_post(&sem1);
508f0f7fc1bSAlan Somers 		/* Pause the daemon thread so it won't read the next op */
509f0f7fc1bSAlan Somers 		sem_wait(&sem0);
510f0f7fc1bSAlan Somers 
5119a177029SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
51229edc611SAlan Somers 		out.body.create.entry.attr.mode = S_IFDIR | MODE;
51329edc611SAlan Somers 		out.body.create.entry.nodeid = ino0;
514f0f7fc1bSAlan Somers 	})));
515f0f7fc1bSAlan Somers 
516f0f7fc1bSAlan Somers 	fd1 = open(FULLPATH1, O_WRONLY);
517f0f7fc1bSAlan Somers 	ASSERT_LE(0, fd1) << strerror(errno);
518f0f7fc1bSAlan Somers 
519f0f7fc1bSAlan Somers 	/* Use a separate thread for the first write */
5209a177029SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
521f0f7fc1bSAlan Somers 		<< strerror(errno);
522f0f7fc1bSAlan Somers 
523f0f7fc1bSAlan Somers 	sem_wait(&sem1);	/* Sequence the two operations */
524f528b38fSAlan Somers 
525f528b38fSAlan Somers 	setup_interruptor(self, true);
526f528b38fSAlan Somers 
5275a0b9a27SAlan Somers 	r = extattr_set_fd(fd1, ns, "foo", (const void*)value, value_len);
5285a0b9a27SAlan Somers 	EXPECT_NE(0, r);
529f0f7fc1bSAlan Somers 	EXPECT_EQ(EINTR, errno);
530f0f7fc1bSAlan Somers 
531f0f7fc1bSAlan Somers 	/* Unstick the daemon */
532f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
533f0f7fc1bSAlan Somers 
534f0f7fc1bSAlan Somers 	/* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
535a87257acSAlan Somers 	nap();
536f0f7fc1bSAlan Somers 
537f0f7fc1bSAlan Somers 	pthread_join(th0, &thr0_value);
538f0f7fc1bSAlan Somers 	EXPECT_EQ(0, (intptr_t)thr0_value);
539f0f7fc1bSAlan Somers 	sem_destroy(&sem1);
540f0f7fc1bSAlan Somers 	sem_destroy(&sem0);
5418e765737SAlan Somers 
5428e765737SAlan Somers 	leak(fd1);
543f0f7fc1bSAlan Somers }
544f0f7fc1bSAlan Somers 
545f0f7fc1bSAlan Somers /*
5469821f1d3SAlan Somers  * A syscall that gets interrupted while blocking on FUSE I/O should send a
5479821f1d3SAlan Somers  * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR
5489821f1d3SAlan Somers  * in response to the _original_ operation.  The kernel should ultimately
5499821f1d3SAlan Somers  * return EINTR to userspace
5509821f1d3SAlan Somers  */
5519821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
552ed74f781SAlan Somers TEST_F(Intr, in_progress)
5539821f1d3SAlan Somers {
5549821f1d3SAlan Somers 	pthread_t self;
5559a177029SAlan Somers 	uint64_t mkdir_unique;
5569821f1d3SAlan Somers 
5579821f1d3SAlan Somers 	self = pthread_self();
5589821f1d3SAlan Somers 
559a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
560a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
5619a177029SAlan Somers 	expect_mkdir(&mkdir_unique);
5629821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
563723c7768SAlan Somers 		ResultOf([&](auto in) {
56429edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
56529edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
5669821f1d3SAlan Somers 		}, Eq(true)),
5679821f1d3SAlan Somers 		_)
5689821f1d3SAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out) {
56929edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
5709821f1d3SAlan Somers 		out0->header.error = -EINTR;
5719a177029SAlan Somers 		out0->header.unique = mkdir_unique;
5729821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
57329edc611SAlan Somers 		out.push_back(std::move(out0));
5749821f1d3SAlan Somers 	}));
5759821f1d3SAlan Somers 
5769821f1d3SAlan Somers 	setup_interruptor(self);
5779a177029SAlan Somers 	ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
5789821f1d3SAlan Somers 	EXPECT_EQ(EINTR, errno);
5799821f1d3SAlan Somers }
5809821f1d3SAlan Somers 
581a1542146SAlan Somers /* Reads should also be interruptible */
582ed74f781SAlan Somers TEST_F(Intr, in_progress_read)
583a1542146SAlan Somers {
584a1542146SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
585a1542146SAlan Somers 	const char RELPATH[] = "some_file.txt";
586a1542146SAlan Somers 	const size_t bufsize = 80;
587a1542146SAlan Somers 	char buf[bufsize];
588a1542146SAlan Somers 	uint64_t ino = 42;
589a1542146SAlan Somers 	int fd;
590a1542146SAlan Somers 	pthread_t self;
591a1542146SAlan Somers 	uint64_t read_unique;
592a1542146SAlan Somers 
593a1542146SAlan Somers 	self = pthread_self();
594a1542146SAlan Somers 
595a1542146SAlan Somers 	expect_lookup(RELPATH, ino);
596a1542146SAlan Somers 	expect_open(ino, 0, 1);
597a1542146SAlan Somers 	expect_read(ino, &read_unique);
598a1542146SAlan Somers 	EXPECT_CALL(*m_mock, process(
599a1542146SAlan Somers 		ResultOf([&](auto in) {
60029edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
60129edc611SAlan Somers 				in.body.interrupt.unique == read_unique);
602a1542146SAlan Somers 		}, Eq(true)),
603a1542146SAlan Somers 		_)
604a1542146SAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out) {
60529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
606a1542146SAlan Somers 		out0->header.error = -EINTR;
607a1542146SAlan Somers 		out0->header.unique = read_unique;
608a1542146SAlan Somers 		out0->header.len = sizeof(out0->header);
60929edc611SAlan Somers 		out.push_back(std::move(out0));
610a1542146SAlan Somers 	}));
611a1542146SAlan Somers 
612a1542146SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
613a1542146SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
614a1542146SAlan Somers 
615a1542146SAlan Somers 	setup_interruptor(self);
616a1542146SAlan Somers 	ASSERT_EQ(-1, read(fd, buf, bufsize));
617a1542146SAlan Somers 	EXPECT_EQ(EINTR, errno);
6188e765737SAlan Somers 
6198e765737SAlan Somers 	leak(fd);
620a1542146SAlan Somers }
621a1542146SAlan Somers 
622ed74f781SAlan Somers /*
623ed74f781SAlan Somers  * When mounted with -o nointr, fusefs will block signals while waiting for the
624ed74f781SAlan Somers  * server.
625ed74f781SAlan Somers  */
626ed74f781SAlan Somers TEST_F(Nointr, block)
627ed74f781SAlan Somers {
628ed74f781SAlan Somers 	uint64_t ino = 42;
629ed74f781SAlan Somers 	pthread_t self;
630ed74f781SAlan Somers 	sem_t sem0;
631ed74f781SAlan Somers 
632ed74f781SAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
633ed74f781SAlan Somers 	signaled_semaphore = &sem0;
634ed74f781SAlan Somers 	self = pthread_self();
635ed74f781SAlan Somers 
636ed74f781SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
637ed74f781SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
638ed74f781SAlan Somers 	EXPECT_CALL(*m_mock, process(
639ed74f781SAlan Somers 		ResultOf([=](auto in) {
640ed74f781SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
641ed74f781SAlan Somers 		}, Eq(true)),
642ed74f781SAlan Somers 		_)
643ed74f781SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
644ed74f781SAlan Somers 		/* Let the killer proceed */
645ed74f781SAlan Somers 		sem_post(blocked_semaphore);
646ed74f781SAlan Somers 
647ed74f781SAlan Somers 		/* Wait until after the signal has been sent */
648ed74f781SAlan Somers 		sem_wait(signaled_semaphore);
649ed74f781SAlan Somers 		/* Allow time for the mkdir thread to receive the signal */
650ed74f781SAlan Somers 		nap();
651ed74f781SAlan Somers 
652ed74f781SAlan Somers 		/* Finally, complete the original op */
653ed74f781SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
654ed74f781SAlan Somers 		out.body.create.entry.attr.mode = S_IFDIR | MODE;
655ed74f781SAlan Somers 		out.body.create.entry.nodeid = ino;
656ed74f781SAlan Somers 	})));
657ed74f781SAlan Somers 	EXPECT_CALL(*m_mock, process(
658ed74f781SAlan Somers 		ResultOf([&](auto in) {
659ed74f781SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT);
660ed74f781SAlan Somers 		}, Eq(true)),
661ed74f781SAlan Somers 		_)
662ed74f781SAlan Somers 	).Times(0);
663ed74f781SAlan Somers 
664ed74f781SAlan Somers 	setup_interruptor(self);
665ed74f781SAlan Somers 	ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
666ed74f781SAlan Somers 
667ed74f781SAlan Somers 	sem_destroy(&sem0);
668ed74f781SAlan Somers }
669ed74f781SAlan Somers 
670268c28edSAlan Somers /* FUSE_INTERRUPT operations should take priority over other pending ops */
671ed74f781SAlan Somers TEST_F(Intr, priority)
672268c28edSAlan Somers {
673268c28edSAlan Somers 	Sequence seq;
6749a177029SAlan Somers 	uint64_t ino1 = 43;
6759a177029SAlan Somers 	uint64_t mkdir_unique;
6765a0b9a27SAlan Somers 	pthread_t th0;
677268c28edSAlan Somers 	sem_t sem0, sem1;
678268c28edSAlan Somers 
679268c28edSAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
680268c28edSAlan Somers 	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
681268c28edSAlan Somers 
682a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
683a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
684a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH1)
685a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
686268c28edSAlan Somers 	EXPECT_CALL(*m_mock, process(
687268c28edSAlan Somers 		ResultOf([=](auto in) {
68829edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
689268c28edSAlan Somers 		}, Eq(true)),
690268c28edSAlan Somers 		_)
691268c28edSAlan Somers 	).InSequence(seq)
69229edc611SAlan Somers 	.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
69329edc611SAlan Somers 		mkdir_unique = in.header.unique;
694268c28edSAlan Somers 
6959a177029SAlan Somers 		/* Let the next mkdir proceed */
696268c28edSAlan Somers 		sem_post(&sem1);
697268c28edSAlan Somers 
698268c28edSAlan Somers 		/* Pause the daemon thread so it won't read the next op */
699268c28edSAlan Somers 		sem_wait(&sem0);
700268c28edSAlan Somers 
701268c28edSAlan Somers 		/* Finally, interrupt the original op */
70229edc611SAlan Somers 		out.header.error = -EINTR;
70329edc611SAlan Somers 		out.header.unique = mkdir_unique;
70429edc611SAlan Somers 		out.header.len = sizeof(out.header);
705268c28edSAlan Somers 	})));
706268c28edSAlan Somers 	/*
707102c7ac0SAlan Somers 	 * FUSE_INTERRUPT should be received before the second FUSE_MKDIR,
708102c7ac0SAlan Somers 	 * even though it was generated later
709268c28edSAlan Somers 	 */
710268c28edSAlan Somers 	EXPECT_CALL(*m_mock, process(
711268c28edSAlan Somers 		ResultOf([&](auto in) {
71229edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
71329edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
714268c28edSAlan Somers 		}, Eq(true)),
715268c28edSAlan Somers 		_)
716268c28edSAlan Somers 	).InSequence(seq)
717268c28edSAlan Somers 	.WillOnce(Invoke(ReturnErrno(EAGAIN)));
718268c28edSAlan Somers 	EXPECT_CALL(*m_mock, process(
719268c28edSAlan Somers 		ResultOf([&](auto in) {
72029edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
721268c28edSAlan Somers 		}, Eq(true)),
722268c28edSAlan Somers 		_)
723268c28edSAlan Somers 	).InSequence(seq)
72429edc611SAlan Somers 	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
7259a177029SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
72629edc611SAlan Somers 		out.body.create.entry.attr.mode = S_IFDIR | MODE;
72729edc611SAlan Somers 		out.body.create.entry.nodeid = ino1;
728268c28edSAlan Somers 	})));
729268c28edSAlan Somers 
7309a177029SAlan Somers 	/* Use a separate thread for the first mkdir */
7319a177029SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
732268c28edSAlan Somers 		<< strerror(errno);
733268c28edSAlan Somers 
734268c28edSAlan Somers 	signaled_semaphore = &sem0;
735268c28edSAlan Somers 
7369a177029SAlan Somers 	sem_wait(&sem1);	/* Sequence the two mkdirs */
737f528b38fSAlan Somers 	setup_interruptor(th0, true);
738102c7ac0SAlan Somers 	ASSERT_EQ(0, mkdir(FULLDIRPATH1, MODE)) << strerror(errno);
739268c28edSAlan Somers 
740268c28edSAlan Somers 	pthread_join(th0, NULL);
741268c28edSAlan Somers 	sem_destroy(&sem1);
742268c28edSAlan Somers 	sem_destroy(&sem0);
743268c28edSAlan Somers }
744268c28edSAlan Somers 
7459821f1d3SAlan Somers /*
7469821f1d3SAlan Somers  * If the FUSE filesystem receives the FUSE_INTERRUPT operation before
7479821f1d3SAlan Somers  * processing the original, then it should wait for "some timeout" for the
7489821f1d3SAlan Somers  * original operation to arrive.  If not, it should send EAGAIN to the
7499821f1d3SAlan Somers  * INTERRUPT operation, and the kernel should requeue the INTERRUPT.
7509821f1d3SAlan Somers  *
7519821f1d3SAlan Somers  * In this test, we'll pretend that the INTERRUPT arrives too soon, gets
7529821f1d3SAlan Somers  * EAGAINed, then the kernel requeues it, and the second time around it
7539821f1d3SAlan Somers  * successfully interrupts the original
7549821f1d3SAlan Somers  */
7559821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
756ed74f781SAlan Somers TEST_F(Intr, too_soon)
7579821f1d3SAlan Somers {
758723c7768SAlan Somers 	Sequence seq;
7599821f1d3SAlan Somers 	pthread_t self;
7609a177029SAlan Somers 	uint64_t mkdir_unique;
7619821f1d3SAlan Somers 
7629821f1d3SAlan Somers 	self = pthread_self();
7639821f1d3SAlan Somers 
764a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
765a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
7669a177029SAlan Somers 	expect_mkdir(&mkdir_unique);
7679821f1d3SAlan Somers 
7689821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
769723c7768SAlan Somers 		ResultOf([&](auto in) {
77029edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
77129edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
7729821f1d3SAlan Somers 		}, Eq(true)),
7739821f1d3SAlan Somers 		_)
774723c7768SAlan Somers 	).InSequence(seq)
775723c7768SAlan Somers 	.WillOnce(Invoke(ReturnErrno(EAGAIN)));
7769821f1d3SAlan Somers 
7779821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
778723c7768SAlan Somers 		ResultOf([&](auto in) {
77929edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
78029edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
7819821f1d3SAlan Somers 		}, Eq(true)),
7829821f1d3SAlan Somers 		_)
783723c7768SAlan Somers 	).InSequence(seq)
784723c7768SAlan Somers 	.WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
78529edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
7869821f1d3SAlan Somers 		out0->header.error = -EINTR;
7879a177029SAlan Somers 		out0->header.unique = mkdir_unique;
7889821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
78929edc611SAlan Somers 		out.push_back(std::move(out0));
7909821f1d3SAlan Somers 	}));
7919821f1d3SAlan Somers 
7929821f1d3SAlan Somers 	setup_interruptor(self);
7939a177029SAlan Somers 	ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
7949821f1d3SAlan Somers 	EXPECT_EQ(EINTR, errno);
7959821f1d3SAlan Somers }
796723c7768SAlan Somers 
797723c7768SAlan Somers 
798723c7768SAlan Somers // TODO: add a test where write returns EWOULDBLOCK
799