xref: /freebsd/tests/sys/fs/fusefs/interrupt.cc (revision b3e76948)
19821f1d3SAlan Somers /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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.
299821f1d3SAlan Somers  */
309821f1d3SAlan Somers 
319821f1d3SAlan Somers extern "C" {
32f0f7fc1bSAlan Somers #include <sys/types.h>
33f0f7fc1bSAlan Somers #include <sys/extattr.h>
34f528b38fSAlan Somers #include <sys/mman.h>
35a1542146SAlan Somers #include <sys/wait.h>
369821f1d3SAlan Somers #include <fcntl.h>
379821f1d3SAlan Somers #include <pthread.h>
383d070fdcSAlan Somers #include <semaphore.h>
399821f1d3SAlan Somers #include <signal.h>
409821f1d3SAlan Somers }
419821f1d3SAlan Somers 
429821f1d3SAlan Somers #include "mockfs.hh"
439821f1d3SAlan Somers #include "utils.hh"
449821f1d3SAlan Somers 
459821f1d3SAlan Somers using namespace testing;
469821f1d3SAlan Somers 
47f0f7fc1bSAlan Somers /* Initial size of files used by these tests */
48f0f7fc1bSAlan Somers const off_t FILESIZE = 1000;
499a177029SAlan Somers /* Access mode used by all directories in these tests */
509a177029SAlan Somers const mode_t MODE = 0755;
519a177029SAlan Somers const char FULLDIRPATH0[] = "mountpoint/some_dir";
529a177029SAlan Somers const char RELDIRPATH0[] = "some_dir";
53102c7ac0SAlan Somers const char FULLDIRPATH1[] = "mountpoint/other_dir";
54102c7ac0SAlan Somers const char RELDIRPATH1[] = "other_dir";
55f0f7fc1bSAlan Somers 
56f528b38fSAlan Somers static sem_t *blocked_semaphore;
57268c28edSAlan Somers static sem_t *signaled_semaphore;
58268c28edSAlan Somers 
59f528b38fSAlan Somers static bool killer_should_sleep = false;
60f528b38fSAlan Somers 
619821f1d3SAlan Somers /* Don't do anything; all we care about is that the syscall gets interrupted */
sigusr2_handler(int __unused sig)629821f1d3SAlan Somers void sigusr2_handler(int __unused sig) {
63723c7768SAlan Somers 	if (verbosity > 1) {
64723c7768SAlan Somers 		printf("Signaled!  thread %p\n", pthread_self());
65723c7768SAlan Somers 	}
66723c7768SAlan Somers 
679821f1d3SAlan Somers }
689821f1d3SAlan Somers 
killer(void * target)699821f1d3SAlan Somers void* killer(void* target) {
70f528b38fSAlan Somers 	/* Wait until the main thread is blocked in fdisp_wait_answ */
71f528b38fSAlan Somers 	if (killer_should_sleep)
72a87257acSAlan Somers 		nap();
73f528b38fSAlan Somers 	else
74f528b38fSAlan Somers 		sem_wait(blocked_semaphore);
759821f1d3SAlan Somers 	if (verbosity > 1)
76723c7768SAlan Somers 		printf("Signalling!  thread %p\n", target);
77723c7768SAlan Somers 	pthread_kill((pthread_t)target, SIGUSR2);
78268c28edSAlan Somers 	if (signaled_semaphore != NULL)
79268c28edSAlan Somers 		sem_post(signaled_semaphore);
809821f1d3SAlan Somers 
819821f1d3SAlan Somers 	return(NULL);
829821f1d3SAlan Somers }
839821f1d3SAlan Somers 
849821f1d3SAlan Somers class Interrupt: public FuseTest {
859821f1d3SAlan Somers public:
869821f1d3SAlan Somers pthread_t m_child;
879821f1d3SAlan Somers 
Interrupt()889821f1d3SAlan Somers Interrupt(): m_child(NULL) {};
899821f1d3SAlan Somers 
expect_lookup(const char * relpath,uint64_t ino)909821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino)
919821f1d3SAlan Somers {
92f0f7fc1bSAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, FILESIZE, 1);
93a1542146SAlan Somers }
94a1542146SAlan Somers 
95a1542146SAlan Somers /*
969a177029SAlan Somers  * Expect a FUSE_MKDIR but don't reply.  Instead, just record the unique value
979a177029SAlan Somers  * to the provided pointer
989a177029SAlan Somers  */
expect_mkdir(uint64_t * mkdir_unique)999a177029SAlan Somers void expect_mkdir(uint64_t *mkdir_unique)
1009a177029SAlan Somers {
1019a177029SAlan Somers 	EXPECT_CALL(*m_mock, process(
1029a177029SAlan Somers 		ResultOf([=](auto in) {
10329edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
1049a177029SAlan Somers 		}, Eq(true)),
1059a177029SAlan Somers 		_)
1069a177029SAlan Somers 	).WillOnce(Invoke([=](auto in, auto &out __unused) {
10729edc611SAlan Somers 		*mkdir_unique = in.header.unique;
108f528b38fSAlan Somers 		sem_post(blocked_semaphore);
1099a177029SAlan Somers 	}));
1109a177029SAlan Somers }
1119a177029SAlan Somers 
1129a177029SAlan Somers /*
113a1542146SAlan Somers  * Expect a FUSE_READ but don't reply.  Instead, just record the unique value
114a1542146SAlan Somers  * to the provided pointer
115a1542146SAlan Somers  */
expect_read(uint64_t ino,uint64_t * read_unique)116a1542146SAlan Somers void expect_read(uint64_t ino, uint64_t *read_unique)
117a1542146SAlan Somers {
118a1542146SAlan Somers 	EXPECT_CALL(*m_mock, process(
119a1542146SAlan Somers 		ResultOf([=](auto in) {
12029edc611SAlan Somers 			return (in.header.opcode == FUSE_READ &&
12129edc611SAlan Somers 				in.header.nodeid == ino);
122a1542146SAlan Somers 		}, Eq(true)),
123a1542146SAlan Somers 		_)
124a1542146SAlan Somers 	).WillOnce(Invoke([=](auto in, auto &out __unused) {
12529edc611SAlan Somers 		*read_unique = in.header.unique;
126f528b38fSAlan Somers 		sem_post(blocked_semaphore);
127a1542146SAlan Somers 	}));
1289821f1d3SAlan Somers }
1299821f1d3SAlan Somers 
1309821f1d3SAlan Somers /*
1319821f1d3SAlan Somers  * Expect a FUSE_WRITE but don't reply.  Instead, just record the unique value
1329821f1d3SAlan Somers  * to the provided pointer
1339821f1d3SAlan Somers  */
expect_write(uint64_t ino,uint64_t * write_unique)1349821f1d3SAlan Somers void expect_write(uint64_t ino, uint64_t *write_unique)
1359821f1d3SAlan Somers {
1369821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
1379821f1d3SAlan Somers 		ResultOf([=](auto in) {
13829edc611SAlan Somers 			return (in.header.opcode == FUSE_WRITE &&
13929edc611SAlan Somers 				in.header.nodeid == ino);
1409821f1d3SAlan Somers 		}, Eq(true)),
1419821f1d3SAlan Somers 		_)
1429821f1d3SAlan Somers 	).WillOnce(Invoke([=](auto in, auto &out __unused) {
14329edc611SAlan Somers 		*write_unique = in.header.unique;
144f528b38fSAlan Somers 		sem_post(blocked_semaphore);
1459821f1d3SAlan Somers 	}));
1469821f1d3SAlan Somers }
1479821f1d3SAlan Somers 
setup_interruptor(pthread_t target,bool sleep=false)148f528b38fSAlan Somers void setup_interruptor(pthread_t target, bool sleep = false)
1499821f1d3SAlan Somers {
150a1542146SAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGUSR2, sigusr2_handler)) << strerror(errno);
151f528b38fSAlan Somers 	killer_should_sleep = sleep;
152268c28edSAlan Somers 	ASSERT_EQ(0, pthread_create(&m_child, NULL, killer, (void*)target))
1539821f1d3SAlan Somers 		<< strerror(errno);
1549821f1d3SAlan Somers }
1559821f1d3SAlan Somers 
SetUp()156268c28edSAlan Somers void SetUp() {
157f528b38fSAlan Somers 	const int mprot = PROT_READ | PROT_WRITE;
158f528b38fSAlan Somers 	const int mflags = MAP_ANON | MAP_SHARED;
159f528b38fSAlan Somers 
160268c28edSAlan Somers 	signaled_semaphore = NULL;
161f528b38fSAlan Somers 
162f528b38fSAlan Somers 	blocked_semaphore = (sem_t*)mmap(NULL, sizeof(*blocked_semaphore),
163f528b38fSAlan Somers 		mprot, mflags, -1, 0);
164f528b38fSAlan Somers 	ASSERT_NE(MAP_FAILED, blocked_semaphore) << strerror(errno);
165f528b38fSAlan Somers 	ASSERT_EQ(0, sem_init(blocked_semaphore, 1, 0)) << strerror(errno);
166a81776c2SAlan Somers 	ASSERT_EQ(0, siginterrupt(SIGUSR2, 1));
167f528b38fSAlan Somers 
168268c28edSAlan Somers 	FuseTest::SetUp();
169268c28edSAlan Somers }
170268c28edSAlan Somers 
TearDown()1719821f1d3SAlan Somers void TearDown() {
172723c7768SAlan Somers 	struct sigaction sa;
173723c7768SAlan Somers 
1749821f1d3SAlan Somers 	if (m_child != NULL) {
1759821f1d3SAlan Somers 		pthread_join(m_child, NULL);
1769821f1d3SAlan Somers 	}
177723c7768SAlan Somers 	bzero(&sa, sizeof(sa));
178723c7768SAlan Somers 	sa.sa_handler = SIG_DFL;
179723c7768SAlan Somers 	sigaction(SIGUSR2, &sa, NULL);
1809821f1d3SAlan Somers 
181f528b38fSAlan Somers 	sem_destroy(blocked_semaphore);
182f528b38fSAlan Somers 	munmap(blocked_semaphore, sizeof(*blocked_semaphore));
183f528b38fSAlan Somers 
1849821f1d3SAlan Somers 	FuseTest::TearDown();
1859821f1d3SAlan Somers }
1869821f1d3SAlan Somers };
1879821f1d3SAlan Somers 
188ed74f781SAlan Somers class Intr: public Interrupt {};
189ed74f781SAlan Somers 
190ed74f781SAlan Somers class Nointr: public Interrupt {
SetUp()191ed74f781SAlan Somers 	void SetUp() {
192ed74f781SAlan Somers 		m_nointr = true;
193ed74f781SAlan Somers 		Interrupt::SetUp();
194ed74f781SAlan Somers 	}
195ed74f781SAlan Somers };
196ed74f781SAlan Somers 
mkdir0(void * arg __unused)197102c7ac0SAlan Somers static void* mkdir0(void* arg __unused) {
198102c7ac0SAlan Somers 	ssize_t r;
199102c7ac0SAlan Somers 
200102c7ac0SAlan Somers 	r = mkdir(FULLDIRPATH0, MODE);
201102c7ac0SAlan Somers 	if (r >= 0)
202102c7ac0SAlan Somers 		return 0;
203102c7ac0SAlan Somers 	else
204102c7ac0SAlan Somers 		return (void*)(intptr_t)errno;
205102c7ac0SAlan Somers }
206102c7ac0SAlan Somers 
read1(void * arg)207102c7ac0SAlan Somers static void* read1(void* arg) {
208102c7ac0SAlan Somers 	const size_t bufsize = FILESIZE;
209102c7ac0SAlan Somers 	char buf[bufsize];
210102c7ac0SAlan Somers 	int fd = (int)(intptr_t)arg;
211102c7ac0SAlan Somers 	ssize_t r;
212102c7ac0SAlan Somers 
213102c7ac0SAlan Somers 	r = read(fd, buf, bufsize);
214102c7ac0SAlan Somers 	if (r >= 0)
215102c7ac0SAlan Somers 		return 0;
216102c7ac0SAlan Somers 	else
217102c7ac0SAlan Somers 		return (void*)(intptr_t)errno;
218102c7ac0SAlan Somers }
219102c7ac0SAlan Somers 
2209821f1d3SAlan Somers /*
2219821f1d3SAlan Somers  * An interrupt operation that gets received after the original command is
2229821f1d3SAlan Somers  * complete should generate an EAGAIN response.
2239821f1d3SAlan Somers  */
2249821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
TEST_F(Intr,already_complete)225ed74f781SAlan Somers TEST_F(Intr, already_complete)
2269821f1d3SAlan Somers {
2279821f1d3SAlan Somers 	uint64_t ino = 42;
2289821f1d3SAlan Somers 	pthread_t self;
2299a177029SAlan Somers 	uint64_t mkdir_unique = 0;
230fd182076SAlan Somers 	Sequence seq;
2319821f1d3SAlan Somers 
2329821f1d3SAlan Somers 	self = pthread_self();
2339821f1d3SAlan Somers 
234a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
235fd182076SAlan Somers 	.InSequence(seq)
236fd182076SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
2379a177029SAlan Somers 	expect_mkdir(&mkdir_unique);
2389821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
2399821f1d3SAlan Somers 		ResultOf([&](auto in) {
24029edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
24129edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
2429821f1d3SAlan Somers 		}, Eq(true)),
2439821f1d3SAlan Somers 		_)
2449821f1d3SAlan Somers 	).WillOnce(Invoke([&](auto in, auto &out) {
2459a177029SAlan Somers 		// First complete the mkdir request
24629edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
2479a177029SAlan Somers 		out0->header.unique = mkdir_unique;
24829edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
2499a177029SAlan Somers 		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
2509a177029SAlan Somers 		out0->body.create.entry.nodeid = ino;
25129edc611SAlan Somers 		out.push_back(std::move(out0));
2529821f1d3SAlan Somers 
2539821f1d3SAlan Somers 		// Then, respond EAGAIN to the interrupt request
25429edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
25529edc611SAlan Somers 		out1->header.unique = in.header.unique;
2569821f1d3SAlan Somers 		out1->header.error = -EAGAIN;
2579821f1d3SAlan Somers 		out1->header.len = sizeof(out1->header);
25829edc611SAlan Somers 		out.push_back(std::move(out1));
2599821f1d3SAlan Somers 	}));
260a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
261fd182076SAlan Somers 	.InSequence(seq)
26229edc611SAlan Somers 	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
263fd182076SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
26429edc611SAlan Somers 		out.body.entry.attr.mode = S_IFDIR | MODE;
26529edc611SAlan Somers 		out.body.entry.nodeid = ino;
26629edc611SAlan Somers 		out.body.entry.attr.nlink = 2;
267fd182076SAlan Somers 	})));
2689821f1d3SAlan Somers 
2699821f1d3SAlan Somers 	setup_interruptor(self);
2709a177029SAlan Somers 	EXPECT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
271fd182076SAlan Somers 	/*
272fd182076SAlan Somers 	 * The final syscall simply ensures that the test's main thread doesn't
273fd182076SAlan Somers 	 * end before the daemon finishes responding to the FUSE_INTERRUPT.
274fd182076SAlan Somers 	 */
275fd182076SAlan Somers 	EXPECT_EQ(0, access(FULLDIRPATH0, F_OK)) << strerror(errno);
2769821f1d3SAlan Somers }
2779821f1d3SAlan Somers 
2789821f1d3SAlan Somers /*
279102c7ac0SAlan Somers  * If a FUSE file system returns ENOSYS for a FUSE_INTERRUPT operation, the
280102c7ac0SAlan Somers  * kernel should not attempt to interrupt any other operations on that mount
281102c7ac0SAlan Somers  * point.
282102c7ac0SAlan Somers  */
TEST_F(Intr,enosys)283ed74f781SAlan Somers TEST_F(Intr, enosys)
284102c7ac0SAlan Somers {
285102c7ac0SAlan Somers 	uint64_t ino0 = 42, ino1 = 43;;
286102c7ac0SAlan Somers 	uint64_t mkdir_unique;
287102c7ac0SAlan Somers 	pthread_t self, th0;
288102c7ac0SAlan Somers 	sem_t sem0, sem1;
289102c7ac0SAlan Somers 	void *thr0_value;
290102c7ac0SAlan Somers 	Sequence seq;
291102c7ac0SAlan Somers 
292102c7ac0SAlan Somers 	self = pthread_self();
293102c7ac0SAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
294102c7ac0SAlan Somers 	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
295102c7ac0SAlan Somers 
296a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH1)
297a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
298a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
299a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
300102c7ac0SAlan Somers 	expect_mkdir(&mkdir_unique);
301102c7ac0SAlan Somers 	EXPECT_CALL(*m_mock, process(
302102c7ac0SAlan Somers 		ResultOf([&](auto in) {
30329edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
30429edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
305102c7ac0SAlan Somers 		}, Eq(true)),
306102c7ac0SAlan Somers 		_)
307102c7ac0SAlan Somers 	).InSequence(seq)
308102c7ac0SAlan Somers 	.WillOnce(Invoke([&](auto in, auto &out) {
309fd182076SAlan Somers 		// reject FUSE_INTERRUPT and respond to the FUSE_MKDIR
31029edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
31129edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
312102c7ac0SAlan Somers 
31329edc611SAlan Somers 		out0->header.unique = in.header.unique;
314102c7ac0SAlan Somers 		out0->header.error = -ENOSYS;
315102c7ac0SAlan Somers 		out0->header.len = sizeof(out0->header);
31629edc611SAlan Somers 		out.push_back(std::move(out0));
317102c7ac0SAlan Somers 
31829edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out1, entry);
319102c7ac0SAlan Somers 		out1->body.create.entry.attr.mode = S_IFDIR | MODE;
320102c7ac0SAlan Somers 		out1->body.create.entry.nodeid = ino1;
321102c7ac0SAlan Somers 		out1->header.unique = mkdir_unique;
32229edc611SAlan Somers 		out.push_back(std::move(out1));
323102c7ac0SAlan Somers 	}));
324102c7ac0SAlan Somers 	EXPECT_CALL(*m_mock, process(
325102c7ac0SAlan Somers 		ResultOf([&](auto in) {
32629edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
327102c7ac0SAlan Somers 		}, Eq(true)),
328102c7ac0SAlan Somers 		_)
329102c7ac0SAlan Somers 	).InSequence(seq)
330102c7ac0SAlan Somers 	.WillOnce(Invoke([&](auto in, auto &out) {
33129edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
332102c7ac0SAlan Somers 
333102c7ac0SAlan Somers 		sem_post(&sem0);
334102c7ac0SAlan Somers 		sem_wait(&sem1);
335102c7ac0SAlan Somers 
33629edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
337102c7ac0SAlan Somers 		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
338102c7ac0SAlan Somers 		out0->body.create.entry.nodeid = ino0;
33929edc611SAlan Somers 		out0->header.unique = in.header.unique;
34029edc611SAlan Somers 		out.push_back(std::move(out0));
341102c7ac0SAlan Somers 	}));
342102c7ac0SAlan Somers 
343102c7ac0SAlan Somers 	setup_interruptor(self);
344102c7ac0SAlan Somers 	/* First mkdir operation should finish synchronously */
345102c7ac0SAlan Somers 	ASSERT_EQ(0, mkdir(FULLDIRPATH1, MODE)) << strerror(errno);
346102c7ac0SAlan Somers 
347102c7ac0SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
348102c7ac0SAlan Somers 		<< strerror(errno);
349102c7ac0SAlan Somers 
350102c7ac0SAlan Somers 	sem_wait(&sem0);
351102c7ac0SAlan Somers 	/*
352102c7ac0SAlan Somers 	 * th0 should be blocked waiting for the fuse daemon thread.
353102c7ac0SAlan Somers 	 * Signal it.  No FUSE_INTERRUPT should result
354102c7ac0SAlan Somers 	 */
355102c7ac0SAlan Somers 	pthread_kill(th0, SIGUSR1);
356102c7ac0SAlan Somers 	/* Allow the daemon thread to proceed */
357102c7ac0SAlan Somers 	sem_post(&sem1);
358102c7ac0SAlan Somers 	pthread_join(th0, &thr0_value);
359102c7ac0SAlan Somers 	/* Second mkdir should've finished without error */
360102c7ac0SAlan Somers 	EXPECT_EQ(0, (intptr_t)thr0_value);
361102c7ac0SAlan Somers }
362102c7ac0SAlan Somers 
363102c7ac0SAlan Somers /*
3649821f1d3SAlan Somers  * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and
3659821f1d3SAlan Somers  * complete the original operation whenever it damn well pleases.
3669821f1d3SAlan Somers  */
3679821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
TEST_F(Intr,ignore)368ed74f781SAlan Somers TEST_F(Intr, ignore)
3699821f1d3SAlan Somers {
3709821f1d3SAlan Somers 	uint64_t ino = 42;
3719821f1d3SAlan Somers 	pthread_t self;
3729a177029SAlan Somers 	uint64_t mkdir_unique;
3739821f1d3SAlan Somers 
3749821f1d3SAlan Somers 	self = pthread_self();
3759821f1d3SAlan Somers 
376a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
377a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
3789a177029SAlan Somers 	expect_mkdir(&mkdir_unique);
3799821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
380723c7768SAlan Somers 		ResultOf([&](auto in) {
38129edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
38229edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
3839821f1d3SAlan Somers 		}, Eq(true)),
3849821f1d3SAlan Somers 		_)
3859821f1d3SAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out) {
386102c7ac0SAlan Somers 		// Ignore FUSE_INTERRUPT; respond to the FUSE_MKDIR
38729edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
3889a177029SAlan Somers 		out0->header.unique = mkdir_unique;
38929edc611SAlan Somers 		SET_OUT_HEADER_LEN(*out0, entry);
3909a177029SAlan Somers 		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
3919a177029SAlan Somers 		out0->body.create.entry.nodeid = ino;
39229edc611SAlan Somers 		out.push_back(std::move(out0));
3939821f1d3SAlan Somers 	}));
3949821f1d3SAlan Somers 
3959821f1d3SAlan Somers 	setup_interruptor(self);
3969a177029SAlan Somers 	ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
3979821f1d3SAlan Somers }
3989821f1d3SAlan Somers 
3993d070fdcSAlan Somers /*
400f0f7fc1bSAlan Somers  * A restartable operation (basically, anything except write or setextattr)
401f0f7fc1bSAlan Somers  * that hasn't yet been sent to userland can be interrupted without sending
402f0f7fc1bSAlan Somers  * FUSE_INTERRUPT, and will be automatically restarted.
403f0f7fc1bSAlan Somers  */
TEST_F(Intr,in_kernel_restartable)404ed74f781SAlan Somers TEST_F(Intr, in_kernel_restartable)
405f0f7fc1bSAlan Somers {
406f0f7fc1bSAlan Somers 	const char FULLPATH1[] = "mountpoint/other_file.txt";
407f0f7fc1bSAlan Somers 	const char RELPATH1[] = "other_file.txt";
408f0f7fc1bSAlan Somers 	uint64_t ino0 = 42, ino1 = 43;
4099a177029SAlan Somers 	int fd1;
410f0f7fc1bSAlan Somers 	pthread_t self, th0, th1;
411f0f7fc1bSAlan Somers 	sem_t sem0, sem1;
412f0f7fc1bSAlan Somers 	void *thr0_value, *thr1_value;
413f0f7fc1bSAlan Somers 
414f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
415f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
416f0f7fc1bSAlan Somers 	self = pthread_self();
417f0f7fc1bSAlan Somers 
418a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
419a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
420f0f7fc1bSAlan Somers 	expect_lookup(RELPATH1, ino1);
421f0f7fc1bSAlan Somers 	expect_open(ino1, 0, 1);
422f0f7fc1bSAlan Somers 	EXPECT_CALL(*m_mock, process(
423f0f7fc1bSAlan Somers 		ResultOf([=](auto in) {
42429edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
425f0f7fc1bSAlan Somers 		}, Eq(true)),
426f0f7fc1bSAlan Somers 		_)
42729edc611SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
428f0f7fc1bSAlan Somers 		/* Let the next write proceed */
429f0f7fc1bSAlan Somers 		sem_post(&sem1);
430f0f7fc1bSAlan Somers 		/* Pause the daemon thread so it won't read the next op */
431f0f7fc1bSAlan Somers 		sem_wait(&sem0);
432f0f7fc1bSAlan Somers 
4339a177029SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
43429edc611SAlan Somers 		out.body.create.entry.attr.mode = S_IFDIR | MODE;
43529edc611SAlan Somers 		out.body.create.entry.nodeid = ino0;
436f0f7fc1bSAlan Somers 	})));
437f0f7fc1bSAlan Somers 	FuseTest::expect_read(ino1, 0, FILESIZE, 0, NULL);
438f0f7fc1bSAlan Somers 
439f0f7fc1bSAlan Somers 	fd1 = open(FULLPATH1, O_RDONLY);
440f0f7fc1bSAlan Somers 	ASSERT_LE(0, fd1) << strerror(errno);
441f0f7fc1bSAlan Somers 
442f0f7fc1bSAlan Somers 	/* Use a separate thread for each operation */
4439a177029SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
444f0f7fc1bSAlan Somers 		<< strerror(errno);
445f0f7fc1bSAlan Somers 
446f0f7fc1bSAlan Somers 	sem_wait(&sem1);	/* Sequence the two operations */
447f0f7fc1bSAlan Somers 
448f0f7fc1bSAlan Somers 	ASSERT_EQ(0, pthread_create(&th1, NULL, read1, (void*)(intptr_t)fd1))
449f0f7fc1bSAlan Somers 		<< strerror(errno);
450f0f7fc1bSAlan Somers 
451f528b38fSAlan Somers 	setup_interruptor(self, true);
452f0f7fc1bSAlan Somers 
453f0f7fc1bSAlan Somers 	pause();		/* Wait for signal */
454f0f7fc1bSAlan Somers 
455f0f7fc1bSAlan Somers 	/* Unstick the daemon */
456f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
457f0f7fc1bSAlan Somers 
458f0f7fc1bSAlan Somers 	/* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
459a87257acSAlan Somers 	nap();
460f0f7fc1bSAlan Somers 
461f0f7fc1bSAlan Somers 	pthread_join(th1, &thr1_value);
462f0f7fc1bSAlan Somers 	pthread_join(th0, &thr0_value);
463f0f7fc1bSAlan Somers 	EXPECT_EQ(0, (intptr_t)thr1_value);
464f0f7fc1bSAlan Somers 	EXPECT_EQ(0, (intptr_t)thr0_value);
465f0f7fc1bSAlan Somers 	sem_destroy(&sem1);
466f0f7fc1bSAlan Somers 	sem_destroy(&sem0);
4678e765737SAlan Somers 
4688e765737SAlan Somers 	leak(fd1);
469f0f7fc1bSAlan Somers }
470f0f7fc1bSAlan Somers 
471f0f7fc1bSAlan Somers /*
4729a177029SAlan Somers  * An operation that hasn't yet been sent to userland can be interrupted
4739a177029SAlan Somers  * without sending FUSE_INTERRUPT.  If it's a non-restartable operation (write
4749a177029SAlan Somers  * or setextattr) it will return EINTR.
475f0f7fc1bSAlan Somers  */
TEST_F(Intr,in_kernel_nonrestartable)476ed74f781SAlan Somers TEST_F(Intr, in_kernel_nonrestartable)
477f0f7fc1bSAlan Somers {
478f0f7fc1bSAlan Somers 	const char FULLPATH1[] = "mountpoint/other_file.txt";
479f0f7fc1bSAlan Somers 	const char RELPATH1[] = "other_file.txt";
480f0f7fc1bSAlan Somers 	const char value[] = "whatever";
481f0f7fc1bSAlan Somers 	ssize_t value_len = strlen(value) + 1;
482f0f7fc1bSAlan Somers 	uint64_t ino0 = 42, ino1 = 43;
483f0f7fc1bSAlan Somers 	int ns = EXTATTR_NAMESPACE_USER;
4849a177029SAlan Somers 	int fd1;
485f0f7fc1bSAlan Somers 	pthread_t self, th0;
486f0f7fc1bSAlan Somers 	sem_t sem0, sem1;
487f0f7fc1bSAlan Somers 	void *thr0_value;
488f0f7fc1bSAlan Somers 	ssize_t r;
489f0f7fc1bSAlan Somers 
490f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
491f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
492f0f7fc1bSAlan Somers 	self = pthread_self();
493f0f7fc1bSAlan Somers 
494a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
495a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
496f0f7fc1bSAlan Somers 	expect_lookup(RELPATH1, ino1);
497f0f7fc1bSAlan Somers 	expect_open(ino1, 0, 1);
498f0f7fc1bSAlan Somers 	EXPECT_CALL(*m_mock, process(
499f0f7fc1bSAlan Somers 		ResultOf([=](auto in) {
50029edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
501f0f7fc1bSAlan Somers 		}, Eq(true)),
502f0f7fc1bSAlan Somers 		_)
50329edc611SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
504f0f7fc1bSAlan Somers 		/* Let the next write proceed */
505f0f7fc1bSAlan Somers 		sem_post(&sem1);
506f0f7fc1bSAlan Somers 		/* Pause the daemon thread so it won't read the next op */
507f0f7fc1bSAlan Somers 		sem_wait(&sem0);
508f0f7fc1bSAlan Somers 
5099a177029SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
51029edc611SAlan Somers 		out.body.create.entry.attr.mode = S_IFDIR | MODE;
51129edc611SAlan Somers 		out.body.create.entry.nodeid = ino0;
512f0f7fc1bSAlan Somers 	})));
513f0f7fc1bSAlan Somers 
514f0f7fc1bSAlan Somers 	fd1 = open(FULLPATH1, O_WRONLY);
515f0f7fc1bSAlan Somers 	ASSERT_LE(0, fd1) << strerror(errno);
516f0f7fc1bSAlan Somers 
517f0f7fc1bSAlan Somers 	/* Use a separate thread for the first write */
5189a177029SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
519f0f7fc1bSAlan Somers 		<< strerror(errno);
520f0f7fc1bSAlan Somers 
521f0f7fc1bSAlan Somers 	sem_wait(&sem1);	/* Sequence the two operations */
522f528b38fSAlan Somers 
523f528b38fSAlan Somers 	setup_interruptor(self, true);
524f528b38fSAlan Somers 
5255a0b9a27SAlan Somers 	r = extattr_set_fd(fd1, ns, "foo", (const void*)value, value_len);
5265a0b9a27SAlan Somers 	EXPECT_NE(0, r);
527f0f7fc1bSAlan Somers 	EXPECT_EQ(EINTR, errno);
528f0f7fc1bSAlan Somers 
529f0f7fc1bSAlan Somers 	/* Unstick the daemon */
530f0f7fc1bSAlan Somers 	ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
531f0f7fc1bSAlan Somers 
532f0f7fc1bSAlan Somers 	/* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
533a87257acSAlan Somers 	nap();
534f0f7fc1bSAlan Somers 
535f0f7fc1bSAlan Somers 	pthread_join(th0, &thr0_value);
536f0f7fc1bSAlan Somers 	EXPECT_EQ(0, (intptr_t)thr0_value);
537f0f7fc1bSAlan Somers 	sem_destroy(&sem1);
538f0f7fc1bSAlan Somers 	sem_destroy(&sem0);
5398e765737SAlan Somers 
5408e765737SAlan Somers 	leak(fd1);
541f0f7fc1bSAlan Somers }
542f0f7fc1bSAlan Somers 
543f0f7fc1bSAlan Somers /*
5449821f1d3SAlan Somers  * A syscall that gets interrupted while blocking on FUSE I/O should send a
5459821f1d3SAlan Somers  * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR
5469821f1d3SAlan Somers  * in response to the _original_ operation.  The kernel should ultimately
5479821f1d3SAlan Somers  * return EINTR to userspace
5489821f1d3SAlan Somers  */
5499821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
TEST_F(Intr,in_progress)550ed74f781SAlan Somers TEST_F(Intr, in_progress)
5519821f1d3SAlan Somers {
5529821f1d3SAlan Somers 	pthread_t self;
5539a177029SAlan Somers 	uint64_t mkdir_unique;
5549821f1d3SAlan Somers 
5559821f1d3SAlan Somers 	self = pthread_self();
5569821f1d3SAlan Somers 
557a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
558a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
5599a177029SAlan Somers 	expect_mkdir(&mkdir_unique);
5609821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
561723c7768SAlan Somers 		ResultOf([&](auto in) {
56229edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
56329edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
5649821f1d3SAlan Somers 		}, Eq(true)),
5659821f1d3SAlan Somers 		_)
5669821f1d3SAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out) {
56729edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
5689821f1d3SAlan Somers 		out0->header.error = -EINTR;
5699a177029SAlan Somers 		out0->header.unique = mkdir_unique;
5709821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
57129edc611SAlan Somers 		out.push_back(std::move(out0));
5729821f1d3SAlan Somers 	}));
5739821f1d3SAlan Somers 
5749821f1d3SAlan Somers 	setup_interruptor(self);
5759a177029SAlan Somers 	ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
5769821f1d3SAlan Somers 	EXPECT_EQ(EINTR, errno);
5779821f1d3SAlan Somers }
5789821f1d3SAlan Somers 
579a1542146SAlan Somers /* Reads should also be interruptible */
TEST_F(Intr,in_progress_read)580ed74f781SAlan Somers TEST_F(Intr, in_progress_read)
581a1542146SAlan Somers {
582a1542146SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
583a1542146SAlan Somers 	const char RELPATH[] = "some_file.txt";
584a1542146SAlan Somers 	const size_t bufsize = 80;
585a1542146SAlan Somers 	char buf[bufsize];
586a1542146SAlan Somers 	uint64_t ino = 42;
587a1542146SAlan Somers 	int fd;
588a1542146SAlan Somers 	pthread_t self;
589a1542146SAlan Somers 	uint64_t read_unique;
590a1542146SAlan Somers 
591a1542146SAlan Somers 	self = pthread_self();
592a1542146SAlan Somers 
593a1542146SAlan Somers 	expect_lookup(RELPATH, ino);
594a1542146SAlan Somers 	expect_open(ino, 0, 1);
595a1542146SAlan Somers 	expect_read(ino, &read_unique);
596a1542146SAlan Somers 	EXPECT_CALL(*m_mock, process(
597a1542146SAlan Somers 		ResultOf([&](auto in) {
59829edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
59929edc611SAlan Somers 				in.body.interrupt.unique == read_unique);
600a1542146SAlan Somers 		}, Eq(true)),
601a1542146SAlan Somers 		_)
602a1542146SAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out) {
60329edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
604a1542146SAlan Somers 		out0->header.error = -EINTR;
605a1542146SAlan Somers 		out0->header.unique = read_unique;
606a1542146SAlan Somers 		out0->header.len = sizeof(out0->header);
60729edc611SAlan Somers 		out.push_back(std::move(out0));
608a1542146SAlan Somers 	}));
609a1542146SAlan Somers 
610a1542146SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
611a1542146SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
612a1542146SAlan Somers 
613a1542146SAlan Somers 	setup_interruptor(self);
614a1542146SAlan Somers 	ASSERT_EQ(-1, read(fd, buf, bufsize));
615a1542146SAlan Somers 	EXPECT_EQ(EINTR, errno);
6168e765737SAlan Somers 
6178e765737SAlan Somers 	leak(fd);
618a1542146SAlan Somers }
619a1542146SAlan Somers 
620ed74f781SAlan Somers /*
621ed74f781SAlan Somers  * When mounted with -o nointr, fusefs will block signals while waiting for the
622ed74f781SAlan Somers  * server.
623ed74f781SAlan Somers  */
TEST_F(Nointr,block)624ed74f781SAlan Somers TEST_F(Nointr, block)
625ed74f781SAlan Somers {
626ed74f781SAlan Somers 	uint64_t ino = 42;
627ed74f781SAlan Somers 	pthread_t self;
628ed74f781SAlan Somers 	sem_t sem0;
629ed74f781SAlan Somers 
630ed74f781SAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
631ed74f781SAlan Somers 	signaled_semaphore = &sem0;
632ed74f781SAlan Somers 	self = pthread_self();
633ed74f781SAlan Somers 
634ed74f781SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
635ed74f781SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
636ed74f781SAlan Somers 	EXPECT_CALL(*m_mock, process(
637ed74f781SAlan Somers 		ResultOf([=](auto in) {
638ed74f781SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
639ed74f781SAlan Somers 		}, Eq(true)),
640ed74f781SAlan Somers 		_)
641ed74f781SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
642ed74f781SAlan Somers 		/* Let the killer proceed */
643ed74f781SAlan Somers 		sem_post(blocked_semaphore);
644ed74f781SAlan Somers 
645ed74f781SAlan Somers 		/* Wait until after the signal has been sent */
646ed74f781SAlan Somers 		sem_wait(signaled_semaphore);
647ed74f781SAlan Somers 		/* Allow time for the mkdir thread to receive the signal */
648ed74f781SAlan Somers 		nap();
649ed74f781SAlan Somers 
650ed74f781SAlan Somers 		/* Finally, complete the original op */
651ed74f781SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
652ed74f781SAlan Somers 		out.body.create.entry.attr.mode = S_IFDIR | MODE;
653ed74f781SAlan Somers 		out.body.create.entry.nodeid = ino;
654ed74f781SAlan Somers 	})));
655ed74f781SAlan Somers 	EXPECT_CALL(*m_mock, process(
656ed74f781SAlan Somers 		ResultOf([&](auto in) {
657ed74f781SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT);
658ed74f781SAlan Somers 		}, Eq(true)),
659ed74f781SAlan Somers 		_)
660ed74f781SAlan Somers 	).Times(0);
661ed74f781SAlan Somers 
662ed74f781SAlan Somers 	setup_interruptor(self);
663ed74f781SAlan Somers 	ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
664ed74f781SAlan Somers 
665ed74f781SAlan Somers 	sem_destroy(&sem0);
666ed74f781SAlan Somers }
667ed74f781SAlan Somers 
668268c28edSAlan Somers /* FUSE_INTERRUPT operations should take priority over other pending ops */
TEST_F(Intr,priority)669ed74f781SAlan Somers TEST_F(Intr, priority)
670268c28edSAlan Somers {
671268c28edSAlan Somers 	Sequence seq;
6729a177029SAlan Somers 	uint64_t ino1 = 43;
6739a177029SAlan Somers 	uint64_t mkdir_unique;
6745a0b9a27SAlan Somers 	pthread_t th0;
675268c28edSAlan Somers 	sem_t sem0, sem1;
676268c28edSAlan Somers 
677268c28edSAlan Somers 	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
678268c28edSAlan Somers 	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
679268c28edSAlan Somers 
680a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
681a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
682a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH1)
683a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
684268c28edSAlan Somers 	EXPECT_CALL(*m_mock, process(
685268c28edSAlan Somers 		ResultOf([=](auto in) {
68629edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
687268c28edSAlan Somers 		}, Eq(true)),
688268c28edSAlan Somers 		_)
689268c28edSAlan Somers 	).InSequence(seq)
69029edc611SAlan Somers 	.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
69129edc611SAlan Somers 		mkdir_unique = in.header.unique;
692268c28edSAlan Somers 
6939a177029SAlan Somers 		/* Let the next mkdir proceed */
694268c28edSAlan Somers 		sem_post(&sem1);
695268c28edSAlan Somers 
696268c28edSAlan Somers 		/* Pause the daemon thread so it won't read the next op */
697268c28edSAlan Somers 		sem_wait(&sem0);
698268c28edSAlan Somers 
699268c28edSAlan Somers 		/* Finally, interrupt the original op */
70029edc611SAlan Somers 		out.header.error = -EINTR;
70129edc611SAlan Somers 		out.header.unique = mkdir_unique;
70229edc611SAlan Somers 		out.header.len = sizeof(out.header);
703268c28edSAlan Somers 	})));
704268c28edSAlan Somers 	/*
705102c7ac0SAlan Somers 	 * FUSE_INTERRUPT should be received before the second FUSE_MKDIR,
706102c7ac0SAlan Somers 	 * even though it was generated later
707268c28edSAlan Somers 	 */
708268c28edSAlan Somers 	EXPECT_CALL(*m_mock, process(
709268c28edSAlan Somers 		ResultOf([&](auto in) {
71029edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
71129edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
712268c28edSAlan Somers 		}, Eq(true)),
713268c28edSAlan Somers 		_)
714268c28edSAlan Somers 	).InSequence(seq)
715268c28edSAlan Somers 	.WillOnce(Invoke(ReturnErrno(EAGAIN)));
716268c28edSAlan Somers 	EXPECT_CALL(*m_mock, process(
717268c28edSAlan Somers 		ResultOf([&](auto in) {
71829edc611SAlan Somers 			return (in.header.opcode == FUSE_MKDIR);
719268c28edSAlan Somers 		}, Eq(true)),
720268c28edSAlan Somers 		_)
721268c28edSAlan Somers 	).InSequence(seq)
72229edc611SAlan Somers 	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
7239a177029SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
72429edc611SAlan Somers 		out.body.create.entry.attr.mode = S_IFDIR | MODE;
72529edc611SAlan Somers 		out.body.create.entry.nodeid = ino1;
726268c28edSAlan Somers 	})));
727268c28edSAlan Somers 
7289a177029SAlan Somers 	/* Use a separate thread for the first mkdir */
7299a177029SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
730268c28edSAlan Somers 		<< strerror(errno);
731268c28edSAlan Somers 
732268c28edSAlan Somers 	signaled_semaphore = &sem0;
733268c28edSAlan Somers 
7349a177029SAlan Somers 	sem_wait(&sem1);	/* Sequence the two mkdirs */
735f528b38fSAlan Somers 	setup_interruptor(th0, true);
736102c7ac0SAlan Somers 	ASSERT_EQ(0, mkdir(FULLDIRPATH1, MODE)) << strerror(errno);
737268c28edSAlan Somers 
738268c28edSAlan Somers 	pthread_join(th0, NULL);
739268c28edSAlan Somers 	sem_destroy(&sem1);
740268c28edSAlan Somers 	sem_destroy(&sem0);
741268c28edSAlan Somers }
742268c28edSAlan Somers 
7439821f1d3SAlan Somers /*
7449821f1d3SAlan Somers  * If the FUSE filesystem receives the FUSE_INTERRUPT operation before
7459821f1d3SAlan Somers  * processing the original, then it should wait for "some timeout" for the
7469821f1d3SAlan Somers  * original operation to arrive.  If not, it should send EAGAIN to the
7479821f1d3SAlan Somers  * INTERRUPT operation, and the kernel should requeue the INTERRUPT.
7489821f1d3SAlan Somers  *
7499821f1d3SAlan Somers  * In this test, we'll pretend that the INTERRUPT arrives too soon, gets
7509821f1d3SAlan Somers  * EAGAINed, then the kernel requeues it, and the second time around it
7519821f1d3SAlan Somers  * successfully interrupts the original
7529821f1d3SAlan Somers  */
7539821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
TEST_F(Intr,too_soon)754ed74f781SAlan Somers TEST_F(Intr, too_soon)
7559821f1d3SAlan Somers {
756723c7768SAlan Somers 	Sequence seq;
7579821f1d3SAlan Somers 	pthread_t self;
7589a177029SAlan Somers 	uint64_t mkdir_unique;
7599821f1d3SAlan Somers 
7609821f1d3SAlan Somers 	self = pthread_self();
7619821f1d3SAlan Somers 
762a34cdd26SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
763a34cdd26SAlan Somers 	.WillOnce(Invoke(ReturnErrno(ENOENT)));
7649a177029SAlan Somers 	expect_mkdir(&mkdir_unique);
7659821f1d3SAlan Somers 
7669821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
767723c7768SAlan Somers 		ResultOf([&](auto in) {
76829edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
76929edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
7709821f1d3SAlan Somers 		}, Eq(true)),
7719821f1d3SAlan Somers 		_)
772723c7768SAlan Somers 	).InSequence(seq)
773723c7768SAlan Somers 	.WillOnce(Invoke(ReturnErrno(EAGAIN)));
7749821f1d3SAlan Somers 
7759821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
776723c7768SAlan Somers 		ResultOf([&](auto in) {
77729edc611SAlan Somers 			return (in.header.opcode == FUSE_INTERRUPT &&
77829edc611SAlan Somers 				in.body.interrupt.unique == mkdir_unique);
7799821f1d3SAlan Somers 		}, Eq(true)),
7809821f1d3SAlan Somers 		_)
781723c7768SAlan Somers 	).InSequence(seq)
782723c7768SAlan Somers 	.WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
78329edc611SAlan Somers 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
7849821f1d3SAlan Somers 		out0->header.error = -EINTR;
7859a177029SAlan Somers 		out0->header.unique = mkdir_unique;
7869821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
78729edc611SAlan Somers 		out.push_back(std::move(out0));
7889821f1d3SAlan Somers 	}));
7899821f1d3SAlan Somers 
7909821f1d3SAlan Somers 	setup_interruptor(self);
7919a177029SAlan Somers 	ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
7929821f1d3SAlan Somers 	EXPECT_EQ(EINTR, errno);
7939821f1d3SAlan Somers }
794723c7768SAlan Somers 
795723c7768SAlan Somers 
796723c7768SAlan Somers // TODO: add a test where write returns EWOULDBLOCK
797