1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 The FreeBSD Foundation
5 *
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31extern "C" {
32#include <sys/types.h>
33#include <sys/extattr.h>
34#include <sys/mman.h>
35#include <sys/wait.h>
36#include <fcntl.h>
37#include <pthread.h>
38#include <semaphore.h>
39#include <signal.h>
40}
41
42#include "mockfs.hh"
43#include "utils.hh"
44
45using namespace testing;
46
47/* Initial size of files used by these tests */
48const off_t FILESIZE = 1000;
49/* Access mode used by all directories in these tests */
50const mode_t MODE = 0755;
51const char FULLDIRPATH0[] = "mountpoint/some_dir";
52const char RELDIRPATH0[] = "some_dir";
53const char FULLDIRPATH1[] = "mountpoint/other_dir";
54const char RELDIRPATH1[] = "other_dir";
55
56static sem_t *blocked_semaphore;
57static sem_t *signaled_semaphore;
58
59static bool killer_should_sleep = false;
60
61/* Don't do anything; all we care about is that the syscall gets interrupted */
62void sigusr2_handler(int __unused sig) {
63	if (verbosity > 1) {
64		printf("Signaled!  thread %p\n", pthread_self());
65	}
66
67}
68
69void* killer(void* target) {
70	/* Wait until the main thread is blocked in fdisp_wait_answ */
71	if (killer_should_sleep)
72		nap();
73	else
74		sem_wait(blocked_semaphore);
75	if (verbosity > 1)
76		printf("Signalling!  thread %p\n", target);
77	pthread_kill((pthread_t)target, SIGUSR2);
78	if (signaled_semaphore != NULL)
79		sem_post(signaled_semaphore);
80
81	return(NULL);
82}
83
84class Interrupt: public FuseTest {
85public:
86pthread_t m_child;
87
88Interrupt(): m_child(NULL) {};
89
90void expect_lookup(const char *relpath, uint64_t ino)
91{
92	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, FILESIZE, 1);
93}
94
95/*
96 * Expect a FUSE_MKDIR but don't reply.  Instead, just record the unique value
97 * to the provided pointer
98 */
99void expect_mkdir(uint64_t *mkdir_unique)
100{
101	EXPECT_CALL(*m_mock, process(
102		ResultOf([=](auto in) {
103			return (in.header.opcode == FUSE_MKDIR);
104		}, Eq(true)),
105		_)
106	).WillOnce(Invoke([=](auto in, auto &out __unused) {
107		*mkdir_unique = in.header.unique;
108		sem_post(blocked_semaphore);
109	}));
110}
111
112/*
113 * Expect a FUSE_READ but don't reply.  Instead, just record the unique value
114 * to the provided pointer
115 */
116void expect_read(uint64_t ino, uint64_t *read_unique)
117{
118	EXPECT_CALL(*m_mock, process(
119		ResultOf([=](auto in) {
120			return (in.header.opcode == FUSE_READ &&
121				in.header.nodeid == ino);
122		}, Eq(true)),
123		_)
124	).WillOnce(Invoke([=](auto in, auto &out __unused) {
125		*read_unique = in.header.unique;
126		sem_post(blocked_semaphore);
127	}));
128}
129
130/*
131 * Expect a FUSE_WRITE but don't reply.  Instead, just record the unique value
132 * to the provided pointer
133 */
134void expect_write(uint64_t ino, uint64_t *write_unique)
135{
136	EXPECT_CALL(*m_mock, process(
137		ResultOf([=](auto in) {
138			return (in.header.opcode == FUSE_WRITE &&
139				in.header.nodeid == ino);
140		}, Eq(true)),
141		_)
142	).WillOnce(Invoke([=](auto in, auto &out __unused) {
143		*write_unique = in.header.unique;
144		sem_post(blocked_semaphore);
145	}));
146}
147
148void setup_interruptor(pthread_t target, bool sleep = false)
149{
150	ASSERT_NE(SIG_ERR, signal(SIGUSR2, sigusr2_handler)) << strerror(errno);
151	killer_should_sleep = sleep;
152	ASSERT_EQ(0, pthread_create(&m_child, NULL, killer, (void*)target))
153		<< strerror(errno);
154}
155
156void SetUp() {
157	const int mprot = PROT_READ | PROT_WRITE;
158	const int mflags = MAP_ANON | MAP_SHARED;
159
160	signaled_semaphore = NULL;
161
162	blocked_semaphore = (sem_t*)mmap(NULL, sizeof(*blocked_semaphore),
163		mprot, mflags, -1, 0);
164	ASSERT_NE(MAP_FAILED, blocked_semaphore) << strerror(errno);
165	ASSERT_EQ(0, sem_init(blocked_semaphore, 1, 0)) << strerror(errno);
166	ASSERT_EQ(0, siginterrupt(SIGUSR2, 1));
167
168	FuseTest::SetUp();
169}
170
171void TearDown() {
172	struct sigaction sa;
173
174	if (m_child != NULL) {
175		pthread_join(m_child, NULL);
176	}
177	bzero(&sa, sizeof(sa));
178	sa.sa_handler = SIG_DFL;
179	sigaction(SIGUSR2, &sa, NULL);
180
181	sem_destroy(blocked_semaphore);
182	munmap(blocked_semaphore, sizeof(*blocked_semaphore));
183
184	FuseTest::TearDown();
185}
186};
187
188class Intr: public Interrupt {};
189
190class Nointr: public Interrupt {
191	void SetUp() {
192		m_nointr = true;
193		Interrupt::SetUp();
194	}
195};
196
197static void* mkdir0(void* arg __unused) {
198	ssize_t r;
199
200	r = mkdir(FULLDIRPATH0, MODE);
201	if (r >= 0)
202		return 0;
203	else
204		return (void*)(intptr_t)errno;
205}
206
207static void* read1(void* arg) {
208	const size_t bufsize = FILESIZE;
209	char buf[bufsize];
210	int fd = (int)(intptr_t)arg;
211	ssize_t r;
212
213	r = read(fd, buf, bufsize);
214	if (r >= 0)
215		return 0;
216	else
217		return (void*)(intptr_t)errno;
218}
219
220/*
221 * An interrupt operation that gets received after the original command is
222 * complete should generate an EAGAIN response.
223 */
224/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
225TEST_F(Intr, already_complete)
226{
227	uint64_t ino = 42;
228	pthread_t self;
229	uint64_t mkdir_unique = 0;
230	Sequence seq;
231
232	self = pthread_self();
233
234	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
235	.InSequence(seq)
236	.WillOnce(Invoke(ReturnErrno(ENOENT)));
237	expect_mkdir(&mkdir_unique);
238	EXPECT_CALL(*m_mock, process(
239		ResultOf([&](auto in) {
240			return (in.header.opcode == FUSE_INTERRUPT &&
241				in.body.interrupt.unique == mkdir_unique);
242		}, Eq(true)),
243		_)
244	).WillOnce(Invoke([&](auto in, auto &out) {
245		// First complete the mkdir request
246		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
247		out0->header.unique = mkdir_unique;
248		SET_OUT_HEADER_LEN(*out0, entry);
249		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
250		out0->body.create.entry.nodeid = ino;
251		out.push_back(std::move(out0));
252
253		// Then, respond EAGAIN to the interrupt request
254		std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
255		out1->header.unique = in.header.unique;
256		out1->header.error = -EAGAIN;
257		out1->header.len = sizeof(out1->header);
258		out.push_back(std::move(out1));
259	}));
260	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
261	.InSequence(seq)
262	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
263		SET_OUT_HEADER_LEN(out, entry);
264		out.body.entry.attr.mode = S_IFDIR | MODE;
265		out.body.entry.nodeid = ino;
266		out.body.entry.attr.nlink = 2;
267	})));
268
269	setup_interruptor(self);
270	EXPECT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
271	/*
272	 * The final syscall simply ensures that the test's main thread doesn't
273	 * end before the daemon finishes responding to the FUSE_INTERRUPT.
274	 */
275	EXPECT_EQ(0, access(FULLDIRPATH0, F_OK)) << strerror(errno);
276}
277
278/*
279 * If a FUSE file system returns ENOSYS for a FUSE_INTERRUPT operation, the
280 * kernel should not attempt to interrupt any other operations on that mount
281 * point.
282 */
283TEST_F(Intr, enosys)
284{
285	uint64_t ino0 = 42, ino1 = 43;;
286	uint64_t mkdir_unique;
287	pthread_t self, th0;
288	sem_t sem0, sem1;
289	void *thr0_value;
290	Sequence seq;
291
292	self = pthread_self();
293	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
294	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
295
296	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH1)
297	.WillOnce(Invoke(ReturnErrno(ENOENT)));
298	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
299	.WillOnce(Invoke(ReturnErrno(ENOENT)));
300	expect_mkdir(&mkdir_unique);
301	EXPECT_CALL(*m_mock, process(
302		ResultOf([&](auto in) {
303			return (in.header.opcode == FUSE_INTERRUPT &&
304				in.body.interrupt.unique == mkdir_unique);
305		}, Eq(true)),
306		_)
307	).InSequence(seq)
308	.WillOnce(Invoke([&](auto in, auto &out) {
309		// reject FUSE_INTERRUPT and respond to the FUSE_MKDIR
310		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
311		std::unique_ptr<mockfs_buf_out> out1(new mockfs_buf_out);
312
313		out0->header.unique = in.header.unique;
314		out0->header.error = -ENOSYS;
315		out0->header.len = sizeof(out0->header);
316		out.push_back(std::move(out0));
317
318		SET_OUT_HEADER_LEN(*out1, entry);
319		out1->body.create.entry.attr.mode = S_IFDIR | MODE;
320		out1->body.create.entry.nodeid = ino1;
321		out1->header.unique = mkdir_unique;
322		out.push_back(std::move(out1));
323	}));
324	EXPECT_CALL(*m_mock, process(
325		ResultOf([&](auto in) {
326			return (in.header.opcode == FUSE_MKDIR);
327		}, Eq(true)),
328		_)
329	).InSequence(seq)
330	.WillOnce(Invoke([&](auto in, auto &out) {
331		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
332
333		sem_post(&sem0);
334		sem_wait(&sem1);
335
336		SET_OUT_HEADER_LEN(*out0, entry);
337		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
338		out0->body.create.entry.nodeid = ino0;
339		out0->header.unique = in.header.unique;
340		out.push_back(std::move(out0));
341	}));
342
343	setup_interruptor(self);
344	/* First mkdir operation should finish synchronously */
345	ASSERT_EQ(0, mkdir(FULLDIRPATH1, MODE)) << strerror(errno);
346
347	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
348		<< strerror(errno);
349
350	sem_wait(&sem0);
351	/*
352	 * th0 should be blocked waiting for the fuse daemon thread.
353	 * Signal it.  No FUSE_INTERRUPT should result
354	 */
355	pthread_kill(th0, SIGUSR1);
356	/* Allow the daemon thread to proceed */
357	sem_post(&sem1);
358	pthread_join(th0, &thr0_value);
359	/* Second mkdir should've finished without error */
360	EXPECT_EQ(0, (intptr_t)thr0_value);
361}
362
363/*
364 * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and
365 * complete the original operation whenever it damn well pleases.
366 */
367/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
368TEST_F(Intr, ignore)
369{
370	uint64_t ino = 42;
371	pthread_t self;
372	uint64_t mkdir_unique;
373
374	self = pthread_self();
375
376	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
377	.WillOnce(Invoke(ReturnErrno(ENOENT)));
378	expect_mkdir(&mkdir_unique);
379	EXPECT_CALL(*m_mock, process(
380		ResultOf([&](auto in) {
381			return (in.header.opcode == FUSE_INTERRUPT &&
382				in.body.interrupt.unique == mkdir_unique);
383		}, Eq(true)),
384		_)
385	).WillOnce(Invoke([&](auto in __unused, auto &out) {
386		// Ignore FUSE_INTERRUPT; respond to the FUSE_MKDIR
387		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
388		out0->header.unique = mkdir_unique;
389		SET_OUT_HEADER_LEN(*out0, entry);
390		out0->body.create.entry.attr.mode = S_IFDIR | MODE;
391		out0->body.create.entry.nodeid = ino;
392		out.push_back(std::move(out0));
393	}));
394
395	setup_interruptor(self);
396	ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
397}
398
399/*
400 * A restartable operation (basically, anything except write or setextattr)
401 * that hasn't yet been sent to userland can be interrupted without sending
402 * FUSE_INTERRUPT, and will be automatically restarted.
403 */
404TEST_F(Intr, in_kernel_restartable)
405{
406	const char FULLPATH1[] = "mountpoint/other_file.txt";
407	const char RELPATH1[] = "other_file.txt";
408	uint64_t ino0 = 42, ino1 = 43;
409	int fd1;
410	pthread_t self, th0, th1;
411	sem_t sem0, sem1;
412	void *thr0_value, *thr1_value;
413
414	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
415	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
416	self = pthread_self();
417
418	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
419	.WillOnce(Invoke(ReturnErrno(ENOENT)));
420	expect_lookup(RELPATH1, ino1);
421	expect_open(ino1, 0, 1);
422	EXPECT_CALL(*m_mock, process(
423		ResultOf([=](auto in) {
424			return (in.header.opcode == FUSE_MKDIR);
425		}, Eq(true)),
426		_)
427	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
428		/* Let the next write proceed */
429		sem_post(&sem1);
430		/* Pause the daemon thread so it won't read the next op */
431		sem_wait(&sem0);
432
433		SET_OUT_HEADER_LEN(out, entry);
434		out.body.create.entry.attr.mode = S_IFDIR | MODE;
435		out.body.create.entry.nodeid = ino0;
436	})));
437	FuseTest::expect_read(ino1, 0, FILESIZE, 0, NULL);
438
439	fd1 = open(FULLPATH1, O_RDONLY);
440	ASSERT_LE(0, fd1) << strerror(errno);
441
442	/* Use a separate thread for each operation */
443	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
444		<< strerror(errno);
445
446	sem_wait(&sem1);	/* Sequence the two operations */
447
448	ASSERT_EQ(0, pthread_create(&th1, NULL, read1, (void*)(intptr_t)fd1))
449		<< strerror(errno);
450
451	setup_interruptor(self, true);
452
453	pause();		/* Wait for signal */
454
455	/* Unstick the daemon */
456	ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
457
458	/* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
459	nap();
460
461	pthread_join(th1, &thr1_value);
462	pthread_join(th0, &thr0_value);
463	EXPECT_EQ(0, (intptr_t)thr1_value);
464	EXPECT_EQ(0, (intptr_t)thr0_value);
465	sem_destroy(&sem1);
466	sem_destroy(&sem0);
467
468	leak(fd1);
469}
470
471/*
472 * An operation that hasn't yet been sent to userland can be interrupted
473 * without sending FUSE_INTERRUPT.  If it's a non-restartable operation (write
474 * or setextattr) it will return EINTR.
475 */
476TEST_F(Intr, in_kernel_nonrestartable)
477{
478	const char FULLPATH1[] = "mountpoint/other_file.txt";
479	const char RELPATH1[] = "other_file.txt";
480	const char value[] = "whatever";
481	ssize_t value_len = strlen(value) + 1;
482	uint64_t ino0 = 42, ino1 = 43;
483	int ns = EXTATTR_NAMESPACE_USER;
484	int fd1;
485	pthread_t self, th0;
486	sem_t sem0, sem1;
487	void *thr0_value;
488	ssize_t r;
489
490	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
491	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
492	self = pthread_self();
493
494	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
495	.WillOnce(Invoke(ReturnErrno(ENOENT)));
496	expect_lookup(RELPATH1, ino1);
497	expect_open(ino1, 0, 1);
498	EXPECT_CALL(*m_mock, process(
499		ResultOf([=](auto in) {
500			return (in.header.opcode == FUSE_MKDIR);
501		}, Eq(true)),
502		_)
503	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
504		/* Let the next write proceed */
505		sem_post(&sem1);
506		/* Pause the daemon thread so it won't read the next op */
507		sem_wait(&sem0);
508
509		SET_OUT_HEADER_LEN(out, entry);
510		out.body.create.entry.attr.mode = S_IFDIR | MODE;
511		out.body.create.entry.nodeid = ino0;
512	})));
513
514	fd1 = open(FULLPATH1, O_WRONLY);
515	ASSERT_LE(0, fd1) << strerror(errno);
516
517	/* Use a separate thread for the first write */
518	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
519		<< strerror(errno);
520
521	sem_wait(&sem1);	/* Sequence the two operations */
522
523	setup_interruptor(self, true);
524
525	r = extattr_set_fd(fd1, ns, "foo", (const void*)value, value_len);
526	EXPECT_NE(0, r);
527	EXPECT_EQ(EINTR, errno);
528
529	/* Unstick the daemon */
530	ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno);
531
532	/* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */
533	nap();
534
535	pthread_join(th0, &thr0_value);
536	EXPECT_EQ(0, (intptr_t)thr0_value);
537	sem_destroy(&sem1);
538	sem_destroy(&sem0);
539
540	leak(fd1);
541}
542
543/*
544 * A syscall that gets interrupted while blocking on FUSE I/O should send a
545 * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR
546 * in response to the _original_ operation.  The kernel should ultimately
547 * return EINTR to userspace
548 */
549/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
550TEST_F(Intr, in_progress)
551{
552	pthread_t self;
553	uint64_t mkdir_unique;
554
555	self = pthread_self();
556
557	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
558	.WillOnce(Invoke(ReturnErrno(ENOENT)));
559	expect_mkdir(&mkdir_unique);
560	EXPECT_CALL(*m_mock, process(
561		ResultOf([&](auto in) {
562			return (in.header.opcode == FUSE_INTERRUPT &&
563				in.body.interrupt.unique == mkdir_unique);
564		}, Eq(true)),
565		_)
566	).WillOnce(Invoke([&](auto in __unused, auto &out) {
567		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
568		out0->header.error = -EINTR;
569		out0->header.unique = mkdir_unique;
570		out0->header.len = sizeof(out0->header);
571		out.push_back(std::move(out0));
572	}));
573
574	setup_interruptor(self);
575	ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
576	EXPECT_EQ(EINTR, errno);
577}
578
579/* Reads should also be interruptible */
580TEST_F(Intr, in_progress_read)
581{
582	const char FULLPATH[] = "mountpoint/some_file.txt";
583	const char RELPATH[] = "some_file.txt";
584	const size_t bufsize = 80;
585	char buf[bufsize];
586	uint64_t ino = 42;
587	int fd;
588	pthread_t self;
589	uint64_t read_unique;
590
591	self = pthread_self();
592
593	expect_lookup(RELPATH, ino);
594	expect_open(ino, 0, 1);
595	expect_read(ino, &read_unique);
596	EXPECT_CALL(*m_mock, process(
597		ResultOf([&](auto in) {
598			return (in.header.opcode == FUSE_INTERRUPT &&
599				in.body.interrupt.unique == read_unique);
600		}, Eq(true)),
601		_)
602	).WillOnce(Invoke([&](auto in __unused, auto &out) {
603		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
604		out0->header.error = -EINTR;
605		out0->header.unique = read_unique;
606		out0->header.len = sizeof(out0->header);
607		out.push_back(std::move(out0));
608	}));
609
610	fd = open(FULLPATH, O_RDONLY);
611	ASSERT_LE(0, fd) << strerror(errno);
612
613	setup_interruptor(self);
614	ASSERT_EQ(-1, read(fd, buf, bufsize));
615	EXPECT_EQ(EINTR, errno);
616
617	leak(fd);
618}
619
620/*
621 * When mounted with -o nointr, fusefs will block signals while waiting for the
622 * server.
623 */
624TEST_F(Nointr, block)
625{
626	uint64_t ino = 42;
627	pthread_t self;
628	sem_t sem0;
629
630	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
631	signaled_semaphore = &sem0;
632	self = pthread_self();
633
634	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
635	.WillOnce(Invoke(ReturnErrno(ENOENT)));
636	EXPECT_CALL(*m_mock, process(
637		ResultOf([=](auto in) {
638			return (in.header.opcode == FUSE_MKDIR);
639		}, Eq(true)),
640		_)
641	).WillOnce(Invoke(ReturnImmediate([&](auto in __unused, auto& out) {
642		/* Let the killer proceed */
643		sem_post(blocked_semaphore);
644
645		/* Wait until after the signal has been sent */
646		sem_wait(signaled_semaphore);
647		/* Allow time for the mkdir thread to receive the signal */
648		nap();
649
650		/* Finally, complete the original op */
651		SET_OUT_HEADER_LEN(out, entry);
652		out.body.create.entry.attr.mode = S_IFDIR | MODE;
653		out.body.create.entry.nodeid = ino;
654	})));
655	EXPECT_CALL(*m_mock, process(
656		ResultOf([&](auto in) {
657			return (in.header.opcode == FUSE_INTERRUPT);
658		}, Eq(true)),
659		_)
660	).Times(0);
661
662	setup_interruptor(self);
663	ASSERT_EQ(0, mkdir(FULLDIRPATH0, MODE)) << strerror(errno);
664
665	sem_destroy(&sem0);
666}
667
668/* FUSE_INTERRUPT operations should take priority over other pending ops */
669TEST_F(Intr, priority)
670{
671	Sequence seq;
672	uint64_t ino1 = 43;
673	uint64_t mkdir_unique;
674	pthread_t th0;
675	sem_t sem0, sem1;
676
677	ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno);
678	ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno);
679
680	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
681	.WillOnce(Invoke(ReturnErrno(ENOENT)));
682	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH1)
683	.WillOnce(Invoke(ReturnErrno(ENOENT)));
684	EXPECT_CALL(*m_mock, process(
685		ResultOf([=](auto in) {
686			return (in.header.opcode == FUSE_MKDIR);
687		}, Eq(true)),
688		_)
689	).InSequence(seq)
690	.WillOnce(Invoke(ReturnImmediate([&](auto in, auto& out) {
691		mkdir_unique = in.header.unique;
692
693		/* Let the next mkdir proceed */
694		sem_post(&sem1);
695
696		/* Pause the daemon thread so it won't read the next op */
697		sem_wait(&sem0);
698
699		/* Finally, interrupt the original op */
700		out.header.error = -EINTR;
701		out.header.unique = mkdir_unique;
702		out.header.len = sizeof(out.header);
703	})));
704	/*
705	 * FUSE_INTERRUPT should be received before the second FUSE_MKDIR,
706	 * even though it was generated later
707	 */
708	EXPECT_CALL(*m_mock, process(
709		ResultOf([&](auto in) {
710			return (in.header.opcode == FUSE_INTERRUPT &&
711				in.body.interrupt.unique == mkdir_unique);
712		}, Eq(true)),
713		_)
714	).InSequence(seq)
715	.WillOnce(Invoke(ReturnErrno(EAGAIN)));
716	EXPECT_CALL(*m_mock, process(
717		ResultOf([&](auto in) {
718			return (in.header.opcode == FUSE_MKDIR);
719		}, Eq(true)),
720		_)
721	).InSequence(seq)
722	.WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
723		SET_OUT_HEADER_LEN(out, entry);
724		out.body.create.entry.attr.mode = S_IFDIR | MODE;
725		out.body.create.entry.nodeid = ino1;
726	})));
727
728	/* Use a separate thread for the first mkdir */
729	ASSERT_EQ(0, pthread_create(&th0, NULL, mkdir0, NULL))
730		<< strerror(errno);
731
732	signaled_semaphore = &sem0;
733
734	sem_wait(&sem1);	/* Sequence the two mkdirs */
735	setup_interruptor(th0, true);
736	ASSERT_EQ(0, mkdir(FULLDIRPATH1, MODE)) << strerror(errno);
737
738	pthread_join(th0, NULL);
739	sem_destroy(&sem1);
740	sem_destroy(&sem0);
741}
742
743/*
744 * If the FUSE filesystem receives the FUSE_INTERRUPT operation before
745 * processing the original, then it should wait for "some timeout" for the
746 * original operation to arrive.  If not, it should send EAGAIN to the
747 * INTERRUPT operation, and the kernel should requeue the INTERRUPT.
748 *
749 * In this test, we'll pretend that the INTERRUPT arrives too soon, gets
750 * EAGAINed, then the kernel requeues it, and the second time around it
751 * successfully interrupts the original
752 */
753/* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */
754TEST_F(Intr, too_soon)
755{
756	Sequence seq;
757	pthread_t self;
758	uint64_t mkdir_unique;
759
760	self = pthread_self();
761
762	EXPECT_LOOKUP(FUSE_ROOT_ID, RELDIRPATH0)
763	.WillOnce(Invoke(ReturnErrno(ENOENT)));
764	expect_mkdir(&mkdir_unique);
765
766	EXPECT_CALL(*m_mock, process(
767		ResultOf([&](auto in) {
768			return (in.header.opcode == FUSE_INTERRUPT &&
769				in.body.interrupt.unique == mkdir_unique);
770		}, Eq(true)),
771		_)
772	).InSequence(seq)
773	.WillOnce(Invoke(ReturnErrno(EAGAIN)));
774
775	EXPECT_CALL(*m_mock, process(
776		ResultOf([&](auto in) {
777			return (in.header.opcode == FUSE_INTERRUPT &&
778				in.body.interrupt.unique == mkdir_unique);
779		}, Eq(true)),
780		_)
781	).InSequence(seq)
782	.WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
783		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
784		out0->header.error = -EINTR;
785		out0->header.unique = mkdir_unique;
786		out0->header.len = sizeof(out0->header);
787		out.push_back(std::move(out0));
788	}));
789
790	setup_interruptor(self);
791	ASSERT_EQ(-1, mkdir(FULLDIRPATH0, MODE));
792	EXPECT_EQ(EINTR, errno);
793}
794
795
796// TODO: add a test where write returns EWOULDBLOCK
797