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. 299821f1d3SAlan Somers */ 309821f1d3SAlan Somers 319821f1d3SAlan Somers extern "C" { 32f0f7fc1bSAlan Somers #include <sys/types.h> 33f0f7fc1bSAlan Somers #include <sys/extattr.h> 34a1542146SAlan Somers #include <sys/wait.h> 359821f1d3SAlan Somers #include <fcntl.h> 369821f1d3SAlan Somers #include <pthread.h> 373d070fdcSAlan Somers #include <semaphore.h> 389821f1d3SAlan Somers #include <signal.h> 399821f1d3SAlan Somers } 409821f1d3SAlan Somers 419821f1d3SAlan Somers #include "mockfs.hh" 429821f1d3SAlan Somers #include "utils.hh" 439821f1d3SAlan Somers 449821f1d3SAlan Somers using namespace testing; 459821f1d3SAlan Somers 46f0f7fc1bSAlan Somers /* Initial size of files used by these tests */ 47f0f7fc1bSAlan Somers const off_t FILESIZE = 1000; 48f0f7fc1bSAlan Somers 499821f1d3SAlan Somers /* Don't do anything; all we care about is that the syscall gets interrupted */ 509821f1d3SAlan Somers void sigusr2_handler(int __unused sig) { 51723c7768SAlan Somers if (verbosity > 1) { 52723c7768SAlan Somers printf("Signaled! thread %p\n", pthread_self()); 53723c7768SAlan Somers } 54723c7768SAlan Somers 559821f1d3SAlan Somers } 569821f1d3SAlan Somers 579821f1d3SAlan Somers void* killer(void* target) { 589821f1d3SAlan Somers /* 599821f1d3SAlan Somers * Sleep for awhile so we can be mostly confident that the main thread 609821f1d3SAlan Somers * is already blocked in write(2) 619821f1d3SAlan Somers */ 629821f1d3SAlan Somers usleep(250'000); 639821f1d3SAlan Somers if (verbosity > 1) 64723c7768SAlan Somers printf("Signalling! thread %p\n", target); 65723c7768SAlan Somers pthread_kill((pthread_t)target, SIGUSR2); 669821f1d3SAlan Somers 679821f1d3SAlan Somers return(NULL); 689821f1d3SAlan Somers } 699821f1d3SAlan Somers 709821f1d3SAlan Somers class Interrupt: public FuseTest { 719821f1d3SAlan Somers public: 729821f1d3SAlan Somers pthread_t m_child; 739821f1d3SAlan Somers 749821f1d3SAlan Somers Interrupt(): m_child(NULL) {}; 759821f1d3SAlan Somers 769821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino) 779821f1d3SAlan Somers { 78f0f7fc1bSAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, FILESIZE, 1); 79a1542146SAlan Somers } 80a1542146SAlan Somers 81a1542146SAlan Somers /* 82a1542146SAlan Somers * Expect a FUSE_READ but don't reply. Instead, just record the unique value 83a1542146SAlan Somers * to the provided pointer 84a1542146SAlan Somers */ 85a1542146SAlan Somers void expect_read(uint64_t ino, uint64_t *read_unique) 86a1542146SAlan Somers { 87a1542146SAlan Somers EXPECT_CALL(*m_mock, process( 88a1542146SAlan Somers ResultOf([=](auto in) { 89a1542146SAlan Somers return (in->header.opcode == FUSE_READ && 90a1542146SAlan Somers in->header.nodeid == ino); 91a1542146SAlan Somers }, Eq(true)), 92a1542146SAlan Somers _) 93a1542146SAlan Somers ).WillOnce(Invoke([=](auto in, auto &out __unused) { 94a1542146SAlan Somers *read_unique = in->header.unique; 95a1542146SAlan Somers })); 969821f1d3SAlan Somers } 979821f1d3SAlan Somers 989821f1d3SAlan Somers /* 999821f1d3SAlan Somers * Expect a FUSE_WRITE but don't reply. Instead, just record the unique value 1009821f1d3SAlan Somers * to the provided pointer 1019821f1d3SAlan Somers */ 1029821f1d3SAlan Somers void expect_write(uint64_t ino, uint64_t *write_unique) 1039821f1d3SAlan Somers { 1049821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 1059821f1d3SAlan Somers ResultOf([=](auto in) { 1069821f1d3SAlan Somers return (in->header.opcode == FUSE_WRITE && 1079821f1d3SAlan Somers in->header.nodeid == ino); 1089821f1d3SAlan Somers }, Eq(true)), 1099821f1d3SAlan Somers _) 1109821f1d3SAlan Somers ).WillOnce(Invoke([=](auto in, auto &out __unused) { 1119821f1d3SAlan Somers *write_unique = in->header.unique; 1129821f1d3SAlan Somers })); 1139821f1d3SAlan Somers } 1149821f1d3SAlan Somers 1159821f1d3SAlan Somers void setup_interruptor(pthread_t self) 1169821f1d3SAlan Somers { 117a1542146SAlan Somers ASSERT_NE(SIG_ERR, signal(SIGUSR2, sigusr2_handler)) << strerror(errno); 1189821f1d3SAlan Somers ASSERT_EQ(0, pthread_create(&m_child, NULL, killer, (void*)self)) 1199821f1d3SAlan Somers << strerror(errno); 1209821f1d3SAlan Somers } 1219821f1d3SAlan Somers 1229821f1d3SAlan Somers void TearDown() { 123723c7768SAlan Somers struct sigaction sa; 124723c7768SAlan Somers 1259821f1d3SAlan Somers if (m_child != NULL) { 1269821f1d3SAlan Somers pthread_join(m_child, NULL); 1279821f1d3SAlan Somers } 128723c7768SAlan Somers bzero(&sa, sizeof(sa)); 129723c7768SAlan Somers sa.sa_handler = SIG_DFL; 130723c7768SAlan Somers sigaction(SIGUSR2, &sa, NULL); 1319821f1d3SAlan Somers 1329821f1d3SAlan Somers FuseTest::TearDown(); 1339821f1d3SAlan Somers } 1349821f1d3SAlan Somers }; 1359821f1d3SAlan Somers 1369821f1d3SAlan Somers /* 1379821f1d3SAlan Somers * An interrupt operation that gets received after the original command is 1389821f1d3SAlan Somers * complete should generate an EAGAIN response. 1399821f1d3SAlan Somers */ 1409821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ 141723c7768SAlan Somers TEST_F(Interrupt, already_complete) 1429821f1d3SAlan Somers { 1439821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1449821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1459821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 1469821f1d3SAlan Somers uint64_t ino = 42; 1479821f1d3SAlan Somers int fd; 1489821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1499821f1d3SAlan Somers pthread_t self; 1509821f1d3SAlan Somers uint64_t write_unique = 0; 1519821f1d3SAlan Somers 1529821f1d3SAlan Somers self = pthread_self(); 1539821f1d3SAlan Somers 1549821f1d3SAlan Somers expect_lookup(RELPATH, ino); 1559821f1d3SAlan Somers expect_open(ino, 0, 1); 1569821f1d3SAlan Somers expect_write(ino, &write_unique); 1579821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 1589821f1d3SAlan Somers ResultOf([&](auto in) { 1599821f1d3SAlan Somers return (in->header.opcode == FUSE_INTERRUPT && 1609821f1d3SAlan Somers in->body.interrupt.unique == write_unique); 1619821f1d3SAlan Somers }, Eq(true)), 1629821f1d3SAlan Somers _) 1639821f1d3SAlan Somers ).WillOnce(Invoke([&](auto in, auto &out) { 1649821f1d3SAlan Somers // First complete the write request 1659821f1d3SAlan Somers auto out0 = new mockfs_buf_out; 1669821f1d3SAlan Somers out0->header.unique = write_unique; 1679821f1d3SAlan Somers SET_OUT_HEADER_LEN(out0, write); 1689821f1d3SAlan Somers out0->body.write.size = bufsize; 1699821f1d3SAlan Somers out.push_back(out0); 1709821f1d3SAlan Somers 1719821f1d3SAlan Somers // Then, respond EAGAIN to the interrupt request 1729821f1d3SAlan Somers auto out1 = new mockfs_buf_out; 1739821f1d3SAlan Somers out1->header.unique = in->header.unique; 1749821f1d3SAlan Somers out1->header.error = -EAGAIN; 1759821f1d3SAlan Somers out1->header.len = sizeof(out1->header); 1769821f1d3SAlan Somers out.push_back(out1); 1779821f1d3SAlan Somers })); 1789821f1d3SAlan Somers 1799821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 1809821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1819821f1d3SAlan Somers 1829821f1d3SAlan Somers setup_interruptor(self); 183723c7768SAlan Somers EXPECT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1849821f1d3SAlan Somers 1859821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 1869821f1d3SAlan Somers } 1879821f1d3SAlan Somers 1889821f1d3SAlan Somers /* 189a1542146SAlan Somers * Upon receipt of a fatal signal, fusefs should return ASAP after sending 190a1542146SAlan Somers * FUSE_INTERRUPT. 191a1542146SAlan Somers */ 192a1542146SAlan Somers TEST_F(Interrupt, fatal_signal) 193a1542146SAlan Somers { 194a1542146SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 195a1542146SAlan Somers const char *CONTENTS = "abcdefgh"; 196a1542146SAlan Somers const char RELPATH[] = "some_file.txt"; 197a1542146SAlan Somers ssize_t bufsize = strlen(CONTENTS); 198a1542146SAlan Somers uint64_t ino = 42; 199a1542146SAlan Somers int status; 200a1542146SAlan Somers pthread_t self; 201a1542146SAlan Somers uint64_t write_unique; 202a1542146SAlan Somers 203a1542146SAlan Somers self = pthread_self(); 204a1542146SAlan Somers 205a1542146SAlan Somers expect_lookup(RELPATH, ino); 206a1542146SAlan Somers expect_open(ino, 0, 1); 207a1542146SAlan Somers expect_write(ino, &write_unique); 208a1542146SAlan Somers EXPECT_CALL(*m_mock, process( 209a1542146SAlan Somers ResultOf([&](auto in) { 210a1542146SAlan Somers return (in->header.opcode == FUSE_INTERRUPT && 211a1542146SAlan Somers in->body.interrupt.unique == write_unique); 212a1542146SAlan Somers }, Eq(true)), 213a1542146SAlan Somers _) 214a1542146SAlan Somers ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { 215a1542146SAlan Somers /* Don't respond. The process should exit anyway */ 216a1542146SAlan Somers })); 217a1542146SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 218a1542146SAlan Somers expect_release(ino, FH); 219a1542146SAlan Somers 220a1542146SAlan Somers fork(false, &status, [&] { 221a1542146SAlan Somers }, [&]() { 222a1542146SAlan Somers struct sigaction sa; 223a1542146SAlan Somers int fd, r; 224a1542146SAlan Somers pthread_t killer_th; 225a1542146SAlan Somers pthread_t self; 226a1542146SAlan Somers 227a1542146SAlan Somers fd = open(FULLPATH, O_WRONLY); 228a1542146SAlan Somers if (fd < 0) { 229a1542146SAlan Somers perror("open"); 230a1542146SAlan Somers return 1; 231a1542146SAlan Somers } 232a1542146SAlan Somers 233a1542146SAlan Somers /* SIGUSR2 terminates the process by default */ 234a1542146SAlan Somers bzero(&sa, sizeof(sa)); 235a1542146SAlan Somers sa.sa_handler = SIG_DFL; 236a1542146SAlan Somers r = sigaction(SIGUSR2, &sa, NULL); 237a1542146SAlan Somers if (r != 0) { 238a1542146SAlan Somers perror("sigaction"); 239a1542146SAlan Somers return 1; 240a1542146SAlan Somers } 241a1542146SAlan Somers self = pthread_self(); 242a1542146SAlan Somers r = pthread_create(&killer_th, NULL, killer, (void*)self); 243a1542146SAlan Somers if (r != 0) { 244a1542146SAlan Somers perror("pthread_create"); 245a1542146SAlan Somers return 1; 246a1542146SAlan Somers } 247a1542146SAlan Somers 248a1542146SAlan Somers write(fd, CONTENTS, bufsize); 249a1542146SAlan Somers return 1; 250a1542146SAlan Somers }); 251a1542146SAlan Somers ASSERT_EQ(SIGUSR2, WTERMSIG(status)); 252a1542146SAlan Somers 253a1542146SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 254a1542146SAlan Somers } 255a1542146SAlan Somers 256a1542146SAlan Somers /* 2579821f1d3SAlan Somers * A FUSE filesystem is legally allowed to ignore INTERRUPT operations, and 2589821f1d3SAlan Somers * complete the original operation whenever it damn well pleases. 2599821f1d3SAlan Somers */ 2609821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ 261723c7768SAlan Somers TEST_F(Interrupt, ignore) 2629821f1d3SAlan Somers { 2639821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2649821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2659821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 2669821f1d3SAlan Somers uint64_t ino = 42; 2679821f1d3SAlan Somers int fd; 2689821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 2699821f1d3SAlan Somers pthread_t self; 2709821f1d3SAlan Somers uint64_t write_unique; 2719821f1d3SAlan Somers 2729821f1d3SAlan Somers self = pthread_self(); 2739821f1d3SAlan Somers 2749821f1d3SAlan Somers expect_lookup(RELPATH, ino); 2759821f1d3SAlan Somers expect_open(ino, 0, 1); 2769821f1d3SAlan Somers expect_write(ino, &write_unique); 2779821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 278723c7768SAlan Somers ResultOf([&](auto in) { 2799821f1d3SAlan Somers return (in->header.opcode == FUSE_INTERRUPT && 2809821f1d3SAlan Somers in->body.interrupt.unique == write_unique); 2819821f1d3SAlan Somers }, Eq(true)), 2829821f1d3SAlan Somers _) 2839821f1d3SAlan Somers ).WillOnce(Invoke([&](auto in __unused, auto &out) { 2849821f1d3SAlan Somers // Ignore FUSE_INTERRUPT; respond to the FUSE_WRITE 2859821f1d3SAlan Somers auto out0 = new mockfs_buf_out; 2869821f1d3SAlan Somers out0->header.unique = write_unique; 2879821f1d3SAlan Somers SET_OUT_HEADER_LEN(out0, write); 2889821f1d3SAlan Somers out0->body.write.size = bufsize; 2899821f1d3SAlan Somers out.push_back(out0); 2909821f1d3SAlan Somers })); 2919821f1d3SAlan Somers 2929821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 2939821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 2949821f1d3SAlan Somers 2959821f1d3SAlan Somers setup_interruptor(self); 2969821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 2979821f1d3SAlan Somers 2989821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 2999821f1d3SAlan Somers } 3009821f1d3SAlan Somers 3013d070fdcSAlan Somers void* write0(void* arg) { 3023d070fdcSAlan Somers const char *CONTENTS = "abcdefgh"; 3033d070fdcSAlan Somers ssize_t bufsize = strlen(CONTENTS); 3043d070fdcSAlan Somers int fd = (int)(intptr_t)arg; 3053d070fdcSAlan Somers ssize_t r; 3063d070fdcSAlan Somers 3073d070fdcSAlan Somers r = write(fd, CONTENTS, bufsize); 3083d070fdcSAlan Somers if (r >= 0) 3093d070fdcSAlan Somers return 0; 3103d070fdcSAlan Somers else 3113d070fdcSAlan Somers return (void*)(intptr_t)errno; 3123d070fdcSAlan Somers } 3133d070fdcSAlan Somers 314f0f7fc1bSAlan Somers void* read1(void* arg) { 315f0f7fc1bSAlan Somers const size_t bufsize = FILESIZE; 316f0f7fc1bSAlan Somers char buf[bufsize]; 317f0f7fc1bSAlan Somers int fd = (int)(intptr_t)arg; 318f0f7fc1bSAlan Somers ssize_t r; 319f0f7fc1bSAlan Somers 320f0f7fc1bSAlan Somers r = read(fd, buf, bufsize); 321f0f7fc1bSAlan Somers if (r >= 0) 322f0f7fc1bSAlan Somers return 0; 323f0f7fc1bSAlan Somers else 324f0f7fc1bSAlan Somers return (void*)(intptr_t)errno; 325f0f7fc1bSAlan Somers } 326f0f7fc1bSAlan Somers 3273d070fdcSAlan Somers /* 3283d070fdcSAlan Somers * An operation that hasn't yet been sent to userland can be interrupted 3293d070fdcSAlan Somers * without sending FUSE_INTERRUPT 3303d070fdcSAlan Somers */ 3313d070fdcSAlan Somers TEST_F(Interrupt, in_kernel) 3323d070fdcSAlan Somers { 3333d070fdcSAlan Somers const char FULLPATH0[] = "mountpoint/some_file.txt"; 3343d070fdcSAlan Somers const char RELPATH0[] = "some_file.txt"; 3353d070fdcSAlan Somers const char FULLPATH1[] = "mountpoint/other_file.txt"; 3363d070fdcSAlan Somers const char RELPATH1[] = "other_file.txt"; 3373d070fdcSAlan Somers const char *CONTENTS = "ijklmnop"; 3383d070fdcSAlan Somers ssize_t bufsize = strlen(CONTENTS); 3393d070fdcSAlan Somers uint64_t ino0 = 42, ino1 = 43; 3403d070fdcSAlan Somers int fd0, fd1; 3413d070fdcSAlan Somers pthread_t self, th0; 3423d070fdcSAlan Somers sem_t sem0, sem1; 3433d070fdcSAlan Somers void *thr0_value; 3443d070fdcSAlan Somers 3453d070fdcSAlan Somers ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno); 3463d070fdcSAlan Somers ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno); 3473d070fdcSAlan Somers self = pthread_self(); 3483d070fdcSAlan Somers 3493d070fdcSAlan Somers expect_lookup(RELPATH0, ino0); 3503d070fdcSAlan Somers expect_open(ino0, 0, 1); 3513d070fdcSAlan Somers expect_lookup(RELPATH1, ino1); 3523d070fdcSAlan Somers expect_open(ino1, 0, 1); 3533d070fdcSAlan Somers EXPECT_CALL(*m_mock, process( 3543d070fdcSAlan Somers ResultOf([=](auto in) { 3553d070fdcSAlan Somers return (in->header.opcode == FUSE_WRITE && 3563d070fdcSAlan Somers in->header.nodeid == ino0); 3573d070fdcSAlan Somers }, Eq(true)), 3583d070fdcSAlan Somers _) 3593d070fdcSAlan Somers ).WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) { 3603d070fdcSAlan Somers /* Let the next write proceed */ 3613d070fdcSAlan Somers sem_post(&sem1); 3623d070fdcSAlan Somers /* Pause the daemon thread so it won't read the next op */ 3633d070fdcSAlan Somers sem_wait(&sem0); 3643d070fdcSAlan Somers 3653d070fdcSAlan Somers SET_OUT_HEADER_LEN(out, write); 3663d070fdcSAlan Somers out->body.write.size = in->body.write.size; 3673d070fdcSAlan Somers }))); 3683d070fdcSAlan Somers 3693d070fdcSAlan Somers fd0 = open(FULLPATH0, O_WRONLY); 3703d070fdcSAlan Somers ASSERT_LE(0, fd0) << strerror(errno); 3713d070fdcSAlan Somers fd1 = open(FULLPATH1, O_WRONLY); 3723d070fdcSAlan Somers ASSERT_LE(0, fd1) << strerror(errno); 3733d070fdcSAlan Somers 3743d070fdcSAlan Somers /* Use a separate thread for the first write */ 3753d070fdcSAlan Somers ASSERT_EQ(0, pthread_create(&th0, NULL, write0, (void*)(intptr_t)fd0)) 3763d070fdcSAlan Somers << strerror(errno); 3773d070fdcSAlan Somers 3783d070fdcSAlan Somers setup_interruptor(self); 3793d070fdcSAlan Somers 3803d070fdcSAlan Somers sem_wait(&sem1); /* Sequence the two writes */ 3813d070fdcSAlan Somers ASSERT_EQ(-1, write(fd1, CONTENTS, bufsize)); 3823d070fdcSAlan Somers EXPECT_EQ(EINTR, errno); 3833d070fdcSAlan Somers 3843d070fdcSAlan Somers /* Unstick the daemon */ 3853d070fdcSAlan Somers ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno); 3863d070fdcSAlan Somers 3873d070fdcSAlan Somers /* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */ 3883d070fdcSAlan Somers usleep(250'000); 3893d070fdcSAlan Somers 3903d070fdcSAlan Somers pthread_join(th0, &thr0_value); 3913d070fdcSAlan Somers EXPECT_EQ(0, (intptr_t)thr0_value); 3923d070fdcSAlan Somers sem_destroy(&sem1); 3933d070fdcSAlan Somers sem_destroy(&sem0); 3943d070fdcSAlan Somers } 3953d070fdcSAlan Somers 3969821f1d3SAlan Somers /* 397f0f7fc1bSAlan Somers * A restartable operation (basically, anything except write or setextattr) 398f0f7fc1bSAlan Somers * that hasn't yet been sent to userland can be interrupted without sending 399f0f7fc1bSAlan Somers * FUSE_INTERRUPT, and will be automatically restarted. 400f0f7fc1bSAlan Somers */ 401f0f7fc1bSAlan Somers TEST_F(Interrupt, in_kernel_restartable) 402f0f7fc1bSAlan Somers { 403f0f7fc1bSAlan Somers const char FULLPATH0[] = "mountpoint/some_file.txt"; 404f0f7fc1bSAlan Somers const char RELPATH0[] = "some_file.txt"; 405f0f7fc1bSAlan Somers const char FULLPATH1[] = "mountpoint/other_file.txt"; 406f0f7fc1bSAlan Somers const char RELPATH1[] = "other_file.txt"; 407f0f7fc1bSAlan Somers uint64_t ino0 = 42, ino1 = 43; 408f0f7fc1bSAlan Somers int fd0, fd1; 409f0f7fc1bSAlan Somers pthread_t self, th0, th1; 410f0f7fc1bSAlan Somers sem_t sem0, sem1; 411f0f7fc1bSAlan Somers void *thr0_value, *thr1_value; 412f0f7fc1bSAlan Somers 413f0f7fc1bSAlan Somers ASSERT_EQ(0, sem_init(&sem0, 0, 0)) << strerror(errno); 414f0f7fc1bSAlan Somers ASSERT_EQ(0, sem_init(&sem1, 0, 0)) << strerror(errno); 415f0f7fc1bSAlan Somers self = pthread_self(); 416f0f7fc1bSAlan Somers 417f0f7fc1bSAlan Somers expect_lookup(RELPATH0, ino0); 418f0f7fc1bSAlan Somers expect_open(ino0, 0, 1); 419f0f7fc1bSAlan Somers expect_lookup(RELPATH1, ino1); 420f0f7fc1bSAlan Somers expect_open(ino1, 0, 1); 421f0f7fc1bSAlan Somers EXPECT_CALL(*m_mock, process( 422f0f7fc1bSAlan Somers ResultOf([=](auto in) { 423f0f7fc1bSAlan Somers return (in->header.opcode == FUSE_WRITE && 424f0f7fc1bSAlan Somers in->header.nodeid == ino0); 425f0f7fc1bSAlan Somers }, Eq(true)), 426f0f7fc1bSAlan Somers _) 427f0f7fc1bSAlan Somers ).WillOnce(Invoke(ReturnImmediate([&](auto in, 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 433f0f7fc1bSAlan Somers SET_OUT_HEADER_LEN(out, write); 434f0f7fc1bSAlan Somers out->body.write.size = in->body.write.size; 435f0f7fc1bSAlan Somers }))); 436f0f7fc1bSAlan Somers FuseTest::expect_read(ino1, 0, FILESIZE, 0, NULL); 437f0f7fc1bSAlan Somers 438f0f7fc1bSAlan Somers fd0 = open(FULLPATH0, O_WRONLY); 439f0f7fc1bSAlan Somers ASSERT_LE(0, fd0) << strerror(errno); 440f0f7fc1bSAlan Somers fd1 = open(FULLPATH1, O_RDONLY); 441f0f7fc1bSAlan Somers ASSERT_LE(0, fd1) << strerror(errno); 442f0f7fc1bSAlan Somers 443f0f7fc1bSAlan Somers /* Use a separate thread for each operation */ 444f0f7fc1bSAlan Somers ASSERT_EQ(0, pthread_create(&th0, NULL, write0, (void*)(intptr_t)fd0)) 445f0f7fc1bSAlan Somers << strerror(errno); 446f0f7fc1bSAlan Somers 447f0f7fc1bSAlan Somers sem_wait(&sem1); /* Sequence the two operations */ 448f0f7fc1bSAlan Somers 449f0f7fc1bSAlan Somers ASSERT_EQ(0, pthread_create(&th1, NULL, read1, (void*)(intptr_t)fd1)) 450f0f7fc1bSAlan Somers << strerror(errno); 451f0f7fc1bSAlan Somers 452f0f7fc1bSAlan Somers setup_interruptor(self); 453f0f7fc1bSAlan Somers 454f0f7fc1bSAlan Somers pause(); /* Wait for signal */ 455f0f7fc1bSAlan Somers 456f0f7fc1bSAlan Somers /* Unstick the daemon */ 457f0f7fc1bSAlan Somers ASSERT_EQ(0, sem_post(&sem0)) << strerror(errno); 458f0f7fc1bSAlan Somers 459f0f7fc1bSAlan Somers /* Wait awhile to make sure the signal generates no FUSE_INTERRUPT */ 460f0f7fc1bSAlan Somers usleep(250'000); 461f0f7fc1bSAlan Somers 462f0f7fc1bSAlan Somers pthread_join(th1, &thr1_value); 463f0f7fc1bSAlan Somers pthread_join(th0, &thr0_value); 464f0f7fc1bSAlan Somers EXPECT_EQ(0, (intptr_t)thr1_value); 465f0f7fc1bSAlan Somers EXPECT_EQ(0, (intptr_t)thr0_value); 466f0f7fc1bSAlan Somers sem_destroy(&sem1); 467f0f7fc1bSAlan Somers sem_destroy(&sem0); 468f0f7fc1bSAlan Somers } 469f0f7fc1bSAlan Somers 470f0f7fc1bSAlan Somers /* 471f0f7fc1bSAlan Somers * Like FUSE_WRITE, FUSE_SETXATTR is non-restartable because it calls uiomove 472f0f7fc1bSAlan Somers * before blocking in fticket_wait_answ 473f0f7fc1bSAlan Somers */ 474f0f7fc1bSAlan Somers TEST_F(Interrupt, in_kernel_setxattr) 475f0f7fc1bSAlan Somers { 476f0f7fc1bSAlan Somers const char FULLPATH0[] = "mountpoint/some_file.txt"; 477f0f7fc1bSAlan Somers const char RELPATH0[] = "some_file.txt"; 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; 484f0f7fc1bSAlan Somers int fd0, 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 494f0f7fc1bSAlan Somers expect_lookup(RELPATH0, ino0); 495f0f7fc1bSAlan Somers expect_open(ino0, 0, 1); 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) { 500f0f7fc1bSAlan Somers return (in->header.opcode == FUSE_WRITE && 501f0f7fc1bSAlan Somers in->header.nodeid == ino0); 502f0f7fc1bSAlan Somers }, Eq(true)), 503f0f7fc1bSAlan Somers _) 504f0f7fc1bSAlan Somers ).WillOnce(Invoke(ReturnImmediate([&](auto in, auto out) { 505f0f7fc1bSAlan Somers /* Let the next write proceed */ 506f0f7fc1bSAlan Somers sem_post(&sem1); 507f0f7fc1bSAlan Somers /* Pause the daemon thread so it won't read the next op */ 508f0f7fc1bSAlan Somers sem_wait(&sem0); 509f0f7fc1bSAlan Somers 510f0f7fc1bSAlan Somers SET_OUT_HEADER_LEN(out, write); 511f0f7fc1bSAlan Somers out->body.write.size = in->body.write.size; 512f0f7fc1bSAlan Somers }))); 513f0f7fc1bSAlan Somers 514f0f7fc1bSAlan Somers fd0 = open(FULLPATH0, O_WRONLY); 515f0f7fc1bSAlan Somers ASSERT_LE(0, fd0) << strerror(errno); 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 */ 520f0f7fc1bSAlan Somers ASSERT_EQ(0, pthread_create(&th0, NULL, write0, (void*)(intptr_t)fd0)) 521f0f7fc1bSAlan Somers << strerror(errno); 522f0f7fc1bSAlan Somers 523f0f7fc1bSAlan Somers setup_interruptor(self); 524f0f7fc1bSAlan Somers 525f0f7fc1bSAlan Somers sem_wait(&sem1); /* Sequence the two operations */ 526f0f7fc1bSAlan Somers r = extattr_set_fd(fd1, ns, "foo", (void*)value, value_len); 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 */ 533f0f7fc1bSAlan Somers usleep(250'000); 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); 539f0f7fc1bSAlan Somers } 540f0f7fc1bSAlan Somers 541f0f7fc1bSAlan Somers /* 5429821f1d3SAlan Somers * A syscall that gets interrupted while blocking on FUSE I/O should send a 5439821f1d3SAlan Somers * FUSE_INTERRUPT command to the fuse filesystem, which should then send EINTR 5449821f1d3SAlan Somers * in response to the _original_ operation. The kernel should ultimately 5459821f1d3SAlan Somers * return EINTR to userspace 5469821f1d3SAlan Somers */ 5479821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ 548723c7768SAlan Somers TEST_F(Interrupt, in_progress) 5499821f1d3SAlan Somers { 5509821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 5519821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 5529821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 5539821f1d3SAlan Somers uint64_t ino = 42; 5549821f1d3SAlan Somers int fd; 5559821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 5569821f1d3SAlan Somers pthread_t self; 5579821f1d3SAlan Somers uint64_t write_unique; 5589821f1d3SAlan Somers 5599821f1d3SAlan Somers self = pthread_self(); 5609821f1d3SAlan Somers 5619821f1d3SAlan Somers expect_lookup(RELPATH, ino); 5629821f1d3SAlan Somers expect_open(ino, 0, 1); 5639821f1d3SAlan Somers expect_write(ino, &write_unique); 5649821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 565723c7768SAlan Somers ResultOf([&](auto in) { 5669821f1d3SAlan Somers return (in->header.opcode == FUSE_INTERRUPT && 5679821f1d3SAlan Somers in->body.interrupt.unique == write_unique); 5689821f1d3SAlan Somers }, Eq(true)), 5699821f1d3SAlan Somers _) 5709821f1d3SAlan Somers ).WillOnce(Invoke([&](auto in __unused, auto &out) { 5719821f1d3SAlan Somers auto out0 = new mockfs_buf_out; 5729821f1d3SAlan Somers out0->header.error = -EINTR; 5739821f1d3SAlan Somers out0->header.unique = write_unique; 5749821f1d3SAlan Somers out0->header.len = sizeof(out0->header); 5759821f1d3SAlan Somers out.push_back(out0); 5769821f1d3SAlan Somers })); 5779821f1d3SAlan Somers 5789821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 5799821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 5809821f1d3SAlan Somers 5819821f1d3SAlan Somers setup_interruptor(self); 5829821f1d3SAlan Somers ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)); 5839821f1d3SAlan Somers EXPECT_EQ(EINTR, errno); 5849821f1d3SAlan Somers 5859821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 5869821f1d3SAlan Somers } 5879821f1d3SAlan Somers 588a1542146SAlan Somers /* Reads should also be interruptible */ 589a1542146SAlan Somers TEST_F(Interrupt, in_progress_read) 590a1542146SAlan Somers { 591a1542146SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 592a1542146SAlan Somers const char RELPATH[] = "some_file.txt"; 593a1542146SAlan Somers const size_t bufsize = 80; 594a1542146SAlan Somers char buf[bufsize]; 595a1542146SAlan Somers uint64_t ino = 42; 596a1542146SAlan Somers int fd; 597a1542146SAlan Somers pthread_t self; 598a1542146SAlan Somers uint64_t read_unique; 599a1542146SAlan Somers 600a1542146SAlan Somers self = pthread_self(); 601a1542146SAlan Somers 602a1542146SAlan Somers expect_lookup(RELPATH, ino); 603a1542146SAlan Somers expect_open(ino, 0, 1); 604a1542146SAlan Somers expect_read(ino, &read_unique); 605a1542146SAlan Somers EXPECT_CALL(*m_mock, process( 606a1542146SAlan Somers ResultOf([&](auto in) { 607a1542146SAlan Somers return (in->header.opcode == FUSE_INTERRUPT && 608a1542146SAlan Somers in->body.interrupt.unique == read_unique); 609a1542146SAlan Somers }, Eq(true)), 610a1542146SAlan Somers _) 611a1542146SAlan Somers ).WillOnce(Invoke([&](auto in __unused, auto &out) { 612a1542146SAlan Somers auto out0 = new mockfs_buf_out; 613a1542146SAlan Somers out0->header.error = -EINTR; 614a1542146SAlan Somers out0->header.unique = read_unique; 615a1542146SAlan Somers out0->header.len = sizeof(out0->header); 616a1542146SAlan Somers out.push_back(out0); 617a1542146SAlan Somers })); 618a1542146SAlan Somers 619a1542146SAlan Somers fd = open(FULLPATH, O_RDONLY); 620a1542146SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 621a1542146SAlan Somers 622a1542146SAlan Somers setup_interruptor(self); 623a1542146SAlan Somers ASSERT_EQ(-1, read(fd, buf, bufsize)); 624a1542146SAlan Somers EXPECT_EQ(EINTR, errno); 625a1542146SAlan Somers 626a1542146SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 627a1542146SAlan Somers } 628a1542146SAlan Somers 6299821f1d3SAlan Somers /* 6309821f1d3SAlan Somers * If the FUSE filesystem receives the FUSE_INTERRUPT operation before 6319821f1d3SAlan Somers * processing the original, then it should wait for "some timeout" for the 6329821f1d3SAlan Somers * original operation to arrive. If not, it should send EAGAIN to the 6339821f1d3SAlan Somers * INTERRUPT operation, and the kernel should requeue the INTERRUPT. 6349821f1d3SAlan Somers * 6359821f1d3SAlan Somers * In this test, we'll pretend that the INTERRUPT arrives too soon, gets 6369821f1d3SAlan Somers * EAGAINed, then the kernel requeues it, and the second time around it 6379821f1d3SAlan Somers * successfully interrupts the original 6389821f1d3SAlan Somers */ 6399821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236530 */ 640723c7768SAlan Somers TEST_F(Interrupt, too_soon) 6419821f1d3SAlan Somers { 6429821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6439821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6449821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 645723c7768SAlan Somers Sequence seq; 6469821f1d3SAlan Somers uint64_t ino = 42; 6479821f1d3SAlan Somers int fd; 6489821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 6499821f1d3SAlan Somers pthread_t self; 6509821f1d3SAlan Somers uint64_t write_unique; 6519821f1d3SAlan Somers 6529821f1d3SAlan Somers self = pthread_self(); 6539821f1d3SAlan Somers 6549821f1d3SAlan Somers expect_lookup(RELPATH, ino); 6559821f1d3SAlan Somers expect_open(ino, 0, 1); 6569821f1d3SAlan Somers expect_write(ino, &write_unique); 6579821f1d3SAlan Somers 6589821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 659723c7768SAlan Somers ResultOf([&](auto in) { 6609821f1d3SAlan Somers return (in->header.opcode == FUSE_INTERRUPT && 6619821f1d3SAlan Somers in->body.interrupt.unique == write_unique); 6629821f1d3SAlan Somers }, Eq(true)), 6639821f1d3SAlan Somers _) 664723c7768SAlan Somers ).InSequence(seq) 665723c7768SAlan Somers .WillOnce(Invoke(ReturnErrno(EAGAIN))); 6669821f1d3SAlan Somers 6679821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 668723c7768SAlan Somers ResultOf([&](auto in) { 6699821f1d3SAlan Somers return (in->header.opcode == FUSE_INTERRUPT && 6709821f1d3SAlan Somers in->body.interrupt.unique == write_unique); 6719821f1d3SAlan Somers }, Eq(true)), 6729821f1d3SAlan Somers _) 673723c7768SAlan Somers ).InSequence(seq) 674723c7768SAlan Somers .WillOnce(Invoke([&](auto in __unused, auto &out __unused) { 6759821f1d3SAlan Somers auto out0 = new mockfs_buf_out; 6769821f1d3SAlan Somers out0->header.error = -EINTR; 6779821f1d3SAlan Somers out0->header.unique = write_unique; 6789821f1d3SAlan Somers out0->header.len = sizeof(out0->header); 6799821f1d3SAlan Somers out.push_back(out0); 6809821f1d3SAlan Somers })); 6819821f1d3SAlan Somers 6829821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 6839821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 6849821f1d3SAlan Somers 6859821f1d3SAlan Somers setup_interruptor(self); 6869821f1d3SAlan Somers ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)); 6879821f1d3SAlan Somers EXPECT_EQ(EINTR, errno); 6889821f1d3SAlan Somers 6899821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6909821f1d3SAlan Somers } 691723c7768SAlan Somers 692723c7768SAlan Somers 693723c7768SAlan Somers // TODO: add a test where write returns EWOULDBLOCK 694