1*9821f1d3SAlan Somers /*- 2*9821f1d3SAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*9821f1d3SAlan Somers * 4*9821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 5*9821f1d3SAlan Somers * 6*9821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 7*9821f1d3SAlan Somers * from the FreeBSD Foundation. 8*9821f1d3SAlan Somers * 9*9821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without 10*9821f1d3SAlan Somers * modification, are permitted provided that the following conditions 11*9821f1d3SAlan Somers * are met: 12*9821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright 13*9821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer. 14*9821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 15*9821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the 16*9821f1d3SAlan Somers * documentation and/or other materials provided with the distribution. 17*9821f1d3SAlan Somers * 18*9821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19*9821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*9821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*9821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22*9821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23*9821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24*9821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25*9821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26*9821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27*9821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28*9821f1d3SAlan Somers * SUCH DAMAGE. 29*9821f1d3SAlan Somers */ 30*9821f1d3SAlan Somers 31*9821f1d3SAlan Somers extern "C" { 32*9821f1d3SAlan Somers #include <fcntl.h> 33*9821f1d3SAlan Somers #include <unistd.h> 34*9821f1d3SAlan Somers } 35*9821f1d3SAlan Somers 36*9821f1d3SAlan Somers #include "mockfs.hh" 37*9821f1d3SAlan Somers #include "utils.hh" 38*9821f1d3SAlan Somers 39*9821f1d3SAlan Somers using namespace testing; 40*9821f1d3SAlan Somers 41*9821f1d3SAlan Somers class Flush: public FuseTest { 42*9821f1d3SAlan Somers 43*9821f1d3SAlan Somers public: 44*9821f1d3SAlan Somers void expect_flush(uint64_t ino, int times, pid_t lo, ProcessMockerT r) 45*9821f1d3SAlan Somers { 46*9821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 47*9821f1d3SAlan Somers ResultOf([=](auto in) { 48*9821f1d3SAlan Somers return (in->header.opcode == FUSE_FLUSH && 49*9821f1d3SAlan Somers in->header.nodeid == ino && 50*9821f1d3SAlan Somers in->body.flush.lock_owner == (uint64_t)lo && 51*9821f1d3SAlan Somers in->body.flush.fh == FH); 52*9821f1d3SAlan Somers }, Eq(true)), 53*9821f1d3SAlan Somers _) 54*9821f1d3SAlan Somers ).Times(times) 55*9821f1d3SAlan Somers .WillRepeatedly(Invoke(r)); 56*9821f1d3SAlan Somers } 57*9821f1d3SAlan Somers 58*9821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino) 59*9821f1d3SAlan Somers { 60*9821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); 61*9821f1d3SAlan Somers } 62*9821f1d3SAlan Somers 63*9821f1d3SAlan Somers /* 64*9821f1d3SAlan Somers * When testing FUSE_FLUSH, the FUSE_RELEASE calls are uninteresting. This 65*9821f1d3SAlan Somers * expectation will silence googlemock warnings 66*9821f1d3SAlan Somers */ 67*9821f1d3SAlan Somers void expect_release() 68*9821f1d3SAlan Somers { 69*9821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 70*9821f1d3SAlan Somers ResultOf([=](auto in) { 71*9821f1d3SAlan Somers return (in->header.opcode == FUSE_RELEASE); 72*9821f1d3SAlan Somers }, Eq(true)), 73*9821f1d3SAlan Somers _) 74*9821f1d3SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(0))); 75*9821f1d3SAlan Somers } 76*9821f1d3SAlan Somers }; 77*9821f1d3SAlan Somers 78*9821f1d3SAlan Somers class FlushWithLocks: public Flush { 79*9821f1d3SAlan Somers virtual void SetUp() { 80*9821f1d3SAlan Somers m_init_flags = FUSE_POSIX_LOCKS; 81*9821f1d3SAlan Somers Flush::SetUp(); 82*9821f1d3SAlan Somers } 83*9821f1d3SAlan Somers }; 84*9821f1d3SAlan Somers 85*9821f1d3SAlan Somers /* If a file descriptor is duplicated, every close causes FLUSH */ 86*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ 87*9821f1d3SAlan Somers TEST_F(Flush, DISABLED_dup) 88*9821f1d3SAlan Somers { 89*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 90*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 91*9821f1d3SAlan Somers uint64_t ino = 42; 92*9821f1d3SAlan Somers int fd, fd2; 93*9821f1d3SAlan Somers 94*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 95*9821f1d3SAlan Somers expect_open(ino, 0, 1); 96*9821f1d3SAlan Somers expect_getattr(ino, 0); 97*9821f1d3SAlan Somers expect_flush(ino, 2, 0, ReturnErrno(0)); 98*9821f1d3SAlan Somers expect_release(); 99*9821f1d3SAlan Somers 100*9821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 101*9821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 102*9821f1d3SAlan Somers 103*9821f1d3SAlan Somers fd2 = dup(fd); 104*9821f1d3SAlan Somers 105*9821f1d3SAlan Somers ASSERT_EQ(0, close(fd2)) << strerror(errno); 106*9821f1d3SAlan Somers ASSERT_EQ(0, close(fd)) << strerror(errno); 107*9821f1d3SAlan Somers } 108*9821f1d3SAlan Somers 109*9821f1d3SAlan Somers /* 110*9821f1d3SAlan Somers * Some FUSE filesystem cache data internally and flush it on release. Such 111*9821f1d3SAlan Somers * filesystems may generate errors during release. On Linux, these get 112*9821f1d3SAlan Somers * returned by close(2). However, POSIX does not require close(2) to return 113*9821f1d3SAlan Somers * this error. FreeBSD's fuse(4) should return EIO if it returns an error at 114*9821f1d3SAlan Somers * all. 115*9821f1d3SAlan Somers */ 116*9821f1d3SAlan Somers /* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ 117*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ 118*9821f1d3SAlan Somers TEST_F(Flush, DISABLED_eio) 119*9821f1d3SAlan Somers { 120*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 121*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 122*9821f1d3SAlan Somers uint64_t ino = 42; 123*9821f1d3SAlan Somers int fd; 124*9821f1d3SAlan Somers 125*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 126*9821f1d3SAlan Somers expect_open(ino, 0, 1); 127*9821f1d3SAlan Somers expect_getattr(ino, 0); 128*9821f1d3SAlan Somers expect_flush(ino, 1, 0, ReturnErrno(EIO)); 129*9821f1d3SAlan Somers expect_release(); 130*9821f1d3SAlan Somers 131*9821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 132*9821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 133*9821f1d3SAlan Somers 134*9821f1d3SAlan Somers ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno); 135*9821f1d3SAlan Somers } 136*9821f1d3SAlan Somers 137*9821f1d3SAlan Somers /* 138*9821f1d3SAlan Somers * If the filesystem returns ENOSYS, it will be treated as success and 139*9821f1d3SAlan Somers * no more FUSE_FLUSH operations will be sent to the daemon 140*9821f1d3SAlan Somers */ 141*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ 142*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ 143*9821f1d3SAlan Somers TEST_F(Flush, DISABLED_enosys) 144*9821f1d3SAlan Somers { 145*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 146*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 147*9821f1d3SAlan Somers uint64_t ino = 42; 148*9821f1d3SAlan Somers int fd, fd2; 149*9821f1d3SAlan Somers 150*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 151*9821f1d3SAlan Somers expect_open(ino, 0, 1); 152*9821f1d3SAlan Somers expect_getattr(ino, 0); 153*9821f1d3SAlan Somers /* On the 2nd close, FUSE_FLUSH won't be sent at all */ 154*9821f1d3SAlan Somers expect_flush(ino, 1, 0, ReturnErrno(ENOSYS)); 155*9821f1d3SAlan Somers expect_release(); 156*9821f1d3SAlan Somers 157*9821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 158*9821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 159*9821f1d3SAlan Somers 160*9821f1d3SAlan Somers fd2 = dup(fd); 161*9821f1d3SAlan Somers 162*9821f1d3SAlan Somers EXPECT_EQ(0, close(fd2)) << strerror(errno); 163*9821f1d3SAlan Somers EXPECT_EQ(0, close(fd)) << strerror(errno); 164*9821f1d3SAlan Somers } 165*9821f1d3SAlan Somers 166*9821f1d3SAlan Somers /* A FUSE_FLUSH should be sent on close(2) */ 167*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ 168*9821f1d3SAlan Somers TEST_F(Flush, DISABLED_flush) 169*9821f1d3SAlan Somers { 170*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 171*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 172*9821f1d3SAlan Somers uint64_t ino = 42; 173*9821f1d3SAlan Somers int fd; 174*9821f1d3SAlan Somers 175*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 176*9821f1d3SAlan Somers expect_open(ino, 0, 1); 177*9821f1d3SAlan Somers expect_getattr(ino, 0); 178*9821f1d3SAlan Somers expect_flush(ino, 1, 0, ReturnErrno(0)); 179*9821f1d3SAlan Somers expect_release(); 180*9821f1d3SAlan Somers 181*9821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 182*9821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 183*9821f1d3SAlan Somers 184*9821f1d3SAlan Somers ASSERT_TRUE(0 == close(fd)) << strerror(errno); 185*9821f1d3SAlan Somers } 186*9821f1d3SAlan Somers 187*9821f1d3SAlan Somers /* 188*9821f1d3SAlan Somers * When closing a file with a POSIX file lock, flush should release the lock, 189*9821f1d3SAlan Somers * _even_if_ it's not the process's last file descriptor for this file. 190*9821f1d3SAlan Somers */ 191*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236405 */ 192*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ 193*9821f1d3SAlan Somers TEST_F(FlushWithLocks, DISABLED_unlock_on_close) 194*9821f1d3SAlan Somers { 195*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 196*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 197*9821f1d3SAlan Somers uint64_t ino = 42; 198*9821f1d3SAlan Somers int fd, fd2; 199*9821f1d3SAlan Somers struct flock fl; 200*9821f1d3SAlan Somers pid_t pid = getpid(); 201*9821f1d3SAlan Somers 202*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 203*9821f1d3SAlan Somers expect_open(ino, 0, 1); 204*9821f1d3SAlan Somers expect_getattr(ino, 0); 205*9821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 206*9821f1d3SAlan Somers ResultOf([=](auto in) { 207*9821f1d3SAlan Somers return (in->header.opcode == FUSE_SETLK && 208*9821f1d3SAlan Somers in->header.nodeid == ino && 209*9821f1d3SAlan Somers in->body.setlk.fh == FH); 210*9821f1d3SAlan Somers }, Eq(true)), 211*9821f1d3SAlan Somers _) 212*9821f1d3SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { 213*9821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, setlk); 214*9821f1d3SAlan Somers out->body.setlk.lk = in->body.setlk.lk; 215*9821f1d3SAlan Somers }))); 216*9821f1d3SAlan Somers expect_flush(ino, 1, pid, ReturnErrno(0)); 217*9821f1d3SAlan Somers 218*9821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 219*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 220*9821f1d3SAlan Somers fl.l_start = 0; 221*9821f1d3SAlan Somers fl.l_len = 0; 222*9821f1d3SAlan Somers fl.l_pid = pid; 223*9821f1d3SAlan Somers fl.l_type = F_RDLCK; 224*9821f1d3SAlan Somers fl.l_whence = SEEK_SET; 225*9821f1d3SAlan Somers fl.l_sysid = 0; 226*9821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); 227*9821f1d3SAlan Somers 228*9821f1d3SAlan Somers fd2 = dup(fd); 229*9821f1d3SAlan Somers ASSERT_EQ(0, close(fd2)) << strerror(errno); 230*9821f1d3SAlan Somers /* Deliberately leak fd */ 231*9821f1d3SAlan Somers } 232