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" { 329821f1d3SAlan Somers #include <fcntl.h> 339821f1d3SAlan Somers #include <unistd.h> 349821f1d3SAlan Somers } 359821f1d3SAlan Somers 369821f1d3SAlan Somers #include "mockfs.hh" 379821f1d3SAlan Somers #include "utils.hh" 389821f1d3SAlan Somers 399821f1d3SAlan Somers using namespace testing; 409821f1d3SAlan Somers 419821f1d3SAlan Somers class Flush: public FuseTest { 429821f1d3SAlan Somers 439821f1d3SAlan Somers public: 44*9f10f423SAlan Somers void 45*9f10f423SAlan Somers expect_flush(uint64_t ino, int times, pid_t lo, ProcessMockerT r) 469821f1d3SAlan Somers { 479821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 489821f1d3SAlan Somers ResultOf([=](auto in) { 499821f1d3SAlan Somers return (in->header.opcode == FUSE_FLUSH && 509821f1d3SAlan Somers in->header.nodeid == ino && 519821f1d3SAlan Somers in->body.flush.lock_owner == (uint64_t)lo && 529821f1d3SAlan Somers in->body.flush.fh == FH); 539821f1d3SAlan Somers }, Eq(true)), 549821f1d3SAlan Somers _) 559821f1d3SAlan Somers ).Times(times) 569821f1d3SAlan Somers .WillRepeatedly(Invoke(r)); 579821f1d3SAlan Somers } 589821f1d3SAlan Somers 59*9f10f423SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, int times) 609821f1d3SAlan Somers { 61*9f10f423SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times); 629821f1d3SAlan Somers } 639821f1d3SAlan Somers 649821f1d3SAlan Somers /* 659821f1d3SAlan Somers * When testing FUSE_FLUSH, the FUSE_RELEASE calls are uninteresting. This 669821f1d3SAlan Somers * expectation will silence googlemock warnings 679821f1d3SAlan Somers */ 689821f1d3SAlan Somers void expect_release() 699821f1d3SAlan Somers { 709821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 719821f1d3SAlan Somers ResultOf([=](auto in) { 729821f1d3SAlan Somers return (in->header.opcode == FUSE_RELEASE); 739821f1d3SAlan Somers }, Eq(true)), 749821f1d3SAlan Somers _) 759821f1d3SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(0))); 769821f1d3SAlan Somers } 779821f1d3SAlan Somers }; 789821f1d3SAlan Somers 799821f1d3SAlan Somers class FlushWithLocks: public Flush { 809821f1d3SAlan Somers virtual void SetUp() { 819821f1d3SAlan Somers m_init_flags = FUSE_POSIX_LOCKS; 829821f1d3SAlan Somers Flush::SetUp(); 839821f1d3SAlan Somers } 849821f1d3SAlan Somers }; 859821f1d3SAlan Somers 86*9f10f423SAlan Somers /* 87*9f10f423SAlan Somers * If multiple file descriptors refer to the same file handle, closing each 88*9f10f423SAlan Somers * should send FUSE_FLUSH 89*9f10f423SAlan Somers */ 90*9f10f423SAlan Somers TEST_F(Flush, open_twice) 919821f1d3SAlan Somers { 929821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 939821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 949821f1d3SAlan Somers uint64_t ino = 42; 959821f1d3SAlan Somers int fd, fd2; 969821f1d3SAlan Somers 97*9f10f423SAlan Somers expect_lookup(RELPATH, ino, 2); 989821f1d3SAlan Somers expect_open(ino, 0, 1); 999821f1d3SAlan Somers expect_getattr(ino, 0); 1009821f1d3SAlan Somers expect_flush(ino, 2, 0, ReturnErrno(0)); 1019821f1d3SAlan Somers expect_release(); 1029821f1d3SAlan Somers 1039821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 1049821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1059821f1d3SAlan Somers 106*9f10f423SAlan Somers fd2 = open(FULLPATH, O_WRONLY); 107*9f10f423SAlan Somers EXPECT_LE(0, fd2) << strerror(errno); 1089821f1d3SAlan Somers 109*9f10f423SAlan Somers EXPECT_EQ(0, close(fd2)) << strerror(errno); 110*9f10f423SAlan Somers EXPECT_EQ(0, close(fd)) << strerror(errno); 1119821f1d3SAlan Somers } 1129821f1d3SAlan Somers 1139821f1d3SAlan Somers /* 1149821f1d3SAlan Somers * Some FUSE filesystem cache data internally and flush it on release. Such 1159821f1d3SAlan Somers * filesystems may generate errors during release. On Linux, these get 1169821f1d3SAlan Somers * returned by close(2). However, POSIX does not require close(2) to return 1179821f1d3SAlan Somers * this error. FreeBSD's fuse(4) should return EIO if it returns an error at 1189821f1d3SAlan Somers * all. 1199821f1d3SAlan Somers */ 1209821f1d3SAlan Somers /* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ 121*9f10f423SAlan Somers TEST_F(Flush, eio) 1229821f1d3SAlan Somers { 1239821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1249821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1259821f1d3SAlan Somers uint64_t ino = 42; 1269821f1d3SAlan Somers int fd; 1279821f1d3SAlan Somers 128*9f10f423SAlan Somers expect_lookup(RELPATH, ino, 1); 1299821f1d3SAlan Somers expect_open(ino, 0, 1); 1309821f1d3SAlan Somers expect_getattr(ino, 0); 1319821f1d3SAlan Somers expect_flush(ino, 1, 0, ReturnErrno(EIO)); 1329821f1d3SAlan Somers expect_release(); 1339821f1d3SAlan Somers 1349821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 1359821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1369821f1d3SAlan Somers 1379821f1d3SAlan Somers ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno); 1389821f1d3SAlan Somers } 1399821f1d3SAlan Somers 1409821f1d3SAlan Somers /* 1419821f1d3SAlan Somers * If the filesystem returns ENOSYS, it will be treated as success and 1429821f1d3SAlan Somers * no more FUSE_FLUSH operations will be sent to the daemon 1439821f1d3SAlan Somers */ 144*9f10f423SAlan Somers TEST_F(Flush, enosys) 1459821f1d3SAlan Somers { 146*9f10f423SAlan Somers const char FULLPATH0[] = "mountpoint/some_file.txt"; 147*9f10f423SAlan Somers const char RELPATH0[] = "some_file.txt"; 148*9f10f423SAlan Somers const char FULLPATH1[] = "mountpoint/other_file.txt"; 149*9f10f423SAlan Somers const char RELPATH1[] = "other_file.txt"; 150*9f10f423SAlan Somers uint64_t ino0 = 42; 151*9f10f423SAlan Somers uint64_t ino1 = 43; 152*9f10f423SAlan Somers int fd0, fd1; 1539821f1d3SAlan Somers 154*9f10f423SAlan Somers expect_lookup(RELPATH0, ino0, 1); 155*9f10f423SAlan Somers expect_open(ino0, 0, 1); 156*9f10f423SAlan Somers expect_getattr(ino0, 0); 1579821f1d3SAlan Somers /* On the 2nd close, FUSE_FLUSH won't be sent at all */ 158*9f10f423SAlan Somers expect_flush(ino0, 1, 0, ReturnErrno(ENOSYS)); 1599821f1d3SAlan Somers expect_release(); 1609821f1d3SAlan Somers 161*9f10f423SAlan Somers expect_lookup(RELPATH1, ino1, 1); 162*9f10f423SAlan Somers expect_open(ino1, 0, 1); 163*9f10f423SAlan Somers expect_getattr(ino1, 0); 164*9f10f423SAlan Somers /* On the 2nd close, FUSE_FLUSH won't be sent at all */ 165*9f10f423SAlan Somers expect_release(); 1669821f1d3SAlan Somers 167*9f10f423SAlan Somers fd0 = open(FULLPATH0, O_WRONLY); 168*9f10f423SAlan Somers ASSERT_LE(0, fd0) << strerror(errno); 1699821f1d3SAlan Somers 170*9f10f423SAlan Somers fd1 = open(FULLPATH1, O_WRONLY); 171*9f10f423SAlan Somers ASSERT_LE(0, fd1) << strerror(errno); 172*9f10f423SAlan Somers 173*9f10f423SAlan Somers EXPECT_EQ(0, close(fd0)) << strerror(errno); 174*9f10f423SAlan Somers EXPECT_EQ(0, close(fd1)) << strerror(errno); 1759821f1d3SAlan Somers } 1769821f1d3SAlan Somers 1779821f1d3SAlan Somers /* A FUSE_FLUSH should be sent on close(2) */ 178*9f10f423SAlan Somers TEST_F(Flush, flush) 1799821f1d3SAlan Somers { 1809821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1819821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1829821f1d3SAlan Somers uint64_t ino = 42; 1839821f1d3SAlan Somers int fd; 1849821f1d3SAlan Somers 185*9f10f423SAlan Somers expect_lookup(RELPATH, ino, 1); 1869821f1d3SAlan Somers expect_open(ino, 0, 1); 1879821f1d3SAlan Somers expect_getattr(ino, 0); 1889821f1d3SAlan Somers expect_flush(ino, 1, 0, ReturnErrno(0)); 1899821f1d3SAlan Somers expect_release(); 1909821f1d3SAlan Somers 1919821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 1929821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1939821f1d3SAlan Somers 1949821f1d3SAlan Somers ASSERT_TRUE(0 == close(fd)) << strerror(errno); 1959821f1d3SAlan Somers } 1969821f1d3SAlan Somers 1979821f1d3SAlan Somers /* 1989821f1d3SAlan Somers * When closing a file with a POSIX file lock, flush should release the lock, 1999821f1d3SAlan Somers * _even_if_ it's not the process's last file descriptor for this file. 2009821f1d3SAlan Somers */ 2019821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ 2029821f1d3SAlan Somers TEST_F(FlushWithLocks, DISABLED_unlock_on_close) 2039821f1d3SAlan Somers { 2049821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2059821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2069821f1d3SAlan Somers uint64_t ino = 42; 2079821f1d3SAlan Somers int fd, fd2; 2089821f1d3SAlan Somers struct flock fl; 2099821f1d3SAlan Somers pid_t pid = getpid(); 2109821f1d3SAlan Somers 211*9f10f423SAlan Somers expect_lookup(RELPATH, ino, 1); 2129821f1d3SAlan Somers expect_open(ino, 0, 1); 2139821f1d3SAlan Somers expect_getattr(ino, 0); 2149821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 2159821f1d3SAlan Somers ResultOf([=](auto in) { 2169821f1d3SAlan Somers return (in->header.opcode == FUSE_SETLK && 2179821f1d3SAlan Somers in->header.nodeid == ino && 2189821f1d3SAlan Somers in->body.setlk.fh == FH); 2199821f1d3SAlan Somers }, Eq(true)), 2209821f1d3SAlan Somers _) 2219821f1d3SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { 2229821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, setlk); 2239821f1d3SAlan Somers out->body.setlk.lk = in->body.setlk.lk; 2249821f1d3SAlan Somers }))); 2259821f1d3SAlan Somers expect_flush(ino, 1, pid, ReturnErrno(0)); 2269821f1d3SAlan Somers 2279821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 2289821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 2299821f1d3SAlan Somers fl.l_start = 0; 2309821f1d3SAlan Somers fl.l_len = 0; 2319821f1d3SAlan Somers fl.l_pid = pid; 2329821f1d3SAlan Somers fl.l_type = F_RDLCK; 2339821f1d3SAlan Somers fl.l_whence = SEEK_SET; 2349821f1d3SAlan Somers fl.l_sysid = 0; 2359821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); 2369821f1d3SAlan Somers 2379821f1d3SAlan Somers fd2 = dup(fd); 2389821f1d3SAlan Somers ASSERT_EQ(0, close(fd2)) << strerror(errno); 2399821f1d3SAlan Somers /* Deliberately leak fd */ 2409821f1d3SAlan Somers } 241