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