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: 449f10f423SAlan Somers void 459f10f423SAlan 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) { 4929edc611SAlan Somers return (in.header.opcode == FUSE_FLUSH && 5029edc611SAlan Somers in.header.nodeid == ino && 5129edc611SAlan Somers in.body.flush.lock_owner == (uint64_t)lo && 5229edc611SAlan 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 599f10f423SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, int times) 609821f1d3SAlan Somers { 619f10f423SAlan 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) { 7229edc611SAlan 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 869f10f423SAlan Somers /* 879f10f423SAlan Somers * If multiple file descriptors refer to the same file handle, closing each 889f10f423SAlan Somers * should send FUSE_FLUSH 899f10f423SAlan Somers */ 909f10f423SAlan 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 979f10f423SAlan Somers expect_lookup(RELPATH, ino, 2); 989821f1d3SAlan Somers expect_open(ino, 0, 1); 99f067b609SAlan Somers expect_flush(ino, 2, getpid(), ReturnErrno(0)); 1009821f1d3SAlan Somers expect_release(); 1019821f1d3SAlan Somers 1029821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 1039821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1049821f1d3SAlan Somers 1059f10f423SAlan Somers fd2 = open(FULLPATH, O_WRONLY); 1069f10f423SAlan Somers EXPECT_LE(0, fd2) << strerror(errno); 1079821f1d3SAlan Somers 1089f10f423SAlan Somers EXPECT_EQ(0, close(fd2)) << strerror(errno); 1099f10f423SAlan Somers EXPECT_EQ(0, close(fd)) << strerror(errno); 1109821f1d3SAlan Somers } 1119821f1d3SAlan Somers 1129821f1d3SAlan Somers /* 1139821f1d3SAlan Somers * Some FUSE filesystem cache data internally and flush it on release. Such 1149821f1d3SAlan Somers * filesystems may generate errors during release. On Linux, these get 1159821f1d3SAlan Somers * returned by close(2). However, POSIX does not require close(2) to return 1169821f1d3SAlan Somers * this error. FreeBSD's fuse(4) should return EIO if it returns an error at 1179821f1d3SAlan Somers * all. 1189821f1d3SAlan Somers */ 1199821f1d3SAlan Somers /* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ 1209f10f423SAlan Somers TEST_F(Flush, eio) 1219821f1d3SAlan Somers { 1229821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1239821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1249821f1d3SAlan Somers uint64_t ino = 42; 1259821f1d3SAlan Somers int fd; 1269821f1d3SAlan Somers 1279f10f423SAlan Somers expect_lookup(RELPATH, ino, 1); 1289821f1d3SAlan Somers expect_open(ino, 0, 1); 129f067b609SAlan Somers expect_flush(ino, 1, getpid(), ReturnErrno(EIO)); 1309821f1d3SAlan Somers expect_release(); 1319821f1d3SAlan Somers 1329821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 1339821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1349821f1d3SAlan Somers 1359821f1d3SAlan Somers ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno); 1369821f1d3SAlan Somers } 1379821f1d3SAlan Somers 1389821f1d3SAlan Somers /* 1399821f1d3SAlan Somers * If the filesystem returns ENOSYS, it will be treated as success and 1409821f1d3SAlan Somers * no more FUSE_FLUSH operations will be sent to the daemon 1419821f1d3SAlan Somers */ 1429f10f423SAlan Somers TEST_F(Flush, enosys) 1439821f1d3SAlan Somers { 1449f10f423SAlan Somers const char FULLPATH0[] = "mountpoint/some_file.txt"; 1459f10f423SAlan Somers const char RELPATH0[] = "some_file.txt"; 1469f10f423SAlan Somers const char FULLPATH1[] = "mountpoint/other_file.txt"; 1479f10f423SAlan Somers const char RELPATH1[] = "other_file.txt"; 1489f10f423SAlan Somers uint64_t ino0 = 42; 1499f10f423SAlan Somers uint64_t ino1 = 43; 1509f10f423SAlan Somers int fd0, fd1; 1519821f1d3SAlan Somers 1529f10f423SAlan Somers expect_lookup(RELPATH0, ino0, 1); 1539f10f423SAlan Somers expect_open(ino0, 0, 1); 1549821f1d3SAlan Somers /* On the 2nd close, FUSE_FLUSH won't be sent at all */ 155f067b609SAlan Somers expect_flush(ino0, 1, getpid(), ReturnErrno(ENOSYS)); 1569821f1d3SAlan Somers expect_release(); 1579821f1d3SAlan Somers 1589f10f423SAlan Somers expect_lookup(RELPATH1, ino1, 1); 1599f10f423SAlan Somers expect_open(ino1, 0, 1); 1609f10f423SAlan Somers /* On the 2nd close, FUSE_FLUSH won't be sent at all */ 1619f10f423SAlan Somers expect_release(); 1629821f1d3SAlan Somers 1639f10f423SAlan Somers fd0 = open(FULLPATH0, O_WRONLY); 1649f10f423SAlan Somers ASSERT_LE(0, fd0) << strerror(errno); 1659821f1d3SAlan Somers 1669f10f423SAlan Somers fd1 = open(FULLPATH1, O_WRONLY); 1679f10f423SAlan Somers ASSERT_LE(0, fd1) << strerror(errno); 1689f10f423SAlan Somers 1699f10f423SAlan Somers EXPECT_EQ(0, close(fd0)) << strerror(errno); 1709f10f423SAlan Somers EXPECT_EQ(0, close(fd1)) << strerror(errno); 1719821f1d3SAlan Somers } 1729821f1d3SAlan Somers 1739821f1d3SAlan Somers /* A FUSE_FLUSH should be sent on close(2) */ 1749f10f423SAlan Somers TEST_F(Flush, flush) 1759821f1d3SAlan Somers { 1769821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1779821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1789821f1d3SAlan Somers uint64_t ino = 42; 1799821f1d3SAlan Somers int fd; 1809821f1d3SAlan Somers 1819f10f423SAlan Somers expect_lookup(RELPATH, ino, 1); 1829821f1d3SAlan Somers expect_open(ino, 0, 1); 183f067b609SAlan Somers expect_flush(ino, 1, getpid(), ReturnErrno(0)); 1849821f1d3SAlan Somers expect_release(); 1859821f1d3SAlan Somers 1869821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 1879821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1889821f1d3SAlan Somers 1899821f1d3SAlan Somers ASSERT_TRUE(0 == close(fd)) << strerror(errno); 1909821f1d3SAlan Somers } 1919821f1d3SAlan Somers 1929821f1d3SAlan Somers /* 1939821f1d3SAlan Somers * When closing a file with a POSIX file lock, flush should release the lock, 1949821f1d3SAlan Somers * _even_if_ it's not the process's last file descriptor for this file. 1959821f1d3SAlan Somers */ 196f067b609SAlan Somers TEST_F(FlushWithLocks, unlock_on_close) 1979821f1d3SAlan Somers { 1989821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1999821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2009821f1d3SAlan Somers uint64_t ino = 42; 2019821f1d3SAlan Somers int fd, fd2; 2029821f1d3SAlan Somers struct flock fl; 2039821f1d3SAlan Somers pid_t pid = getpid(); 2049821f1d3SAlan Somers 205f067b609SAlan Somers expect_lookup(RELPATH, ino, 2); 2069821f1d3SAlan Somers expect_open(ino, 0, 1); 2079821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 2089821f1d3SAlan Somers ResultOf([=](auto in) { 20929edc611SAlan Somers return (in.header.opcode == FUSE_SETLK && 21029edc611SAlan Somers in.header.nodeid == ino && 21129edc611SAlan Somers in.body.setlk.fh == FH); 2129821f1d3SAlan Somers }, Eq(true)), 2139821f1d3SAlan Somers _) 214f067b609SAlan Somers ).WillOnce(Invoke(ReturnErrno(0))); 2159821f1d3SAlan Somers expect_flush(ino, 1, pid, ReturnErrno(0)); 2169821f1d3SAlan Somers 2179821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 2189821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 2199821f1d3SAlan Somers fl.l_start = 0; 2209821f1d3SAlan Somers fl.l_len = 0; 2219821f1d3SAlan Somers fl.l_pid = pid; 2229821f1d3SAlan Somers fl.l_type = F_RDLCK; 2239821f1d3SAlan Somers fl.l_whence = SEEK_SET; 2249821f1d3SAlan Somers fl.l_sysid = 0; 2259821f1d3SAlan Somers ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); 2269821f1d3SAlan Somers 227f067b609SAlan Somers fd2 = open(FULLPATH, O_WRONLY); 228f067b609SAlan Somers ASSERT_LE(0, fd2) << strerror(errno); 2299821f1d3SAlan Somers ASSERT_EQ(0, close(fd2)) << strerror(errno); 230*7fc0921dSAlan Somers leak(fd); 231*7fc0921dSAlan Somers leak(fd2); 2329821f1d3SAlan Somers } 233