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 Release: public FuseTest { 429821f1d3SAlan Somers 439821f1d3SAlan Somers public: 449821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, int times) 459821f1d3SAlan Somers { 469821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, times); 479821f1d3SAlan Somers } 48*e0bec057SAlan Somers 49*e0bec057SAlan Somers void expect_release(uint64_t ino, uint64_t lock_owner, 50*e0bec057SAlan Somers uint32_t flags, int error) 51*e0bec057SAlan Somers { 52*e0bec057SAlan Somers EXPECT_CALL(*m_mock, process( 53*e0bec057SAlan Somers ResultOf([=](auto in) { 54*e0bec057SAlan Somers return (in->header.opcode == FUSE_RELEASE && 55*e0bec057SAlan Somers in->header.nodeid == ino && 56*e0bec057SAlan Somers in->body.release.lock_owner == lock_owner && 57*e0bec057SAlan Somers in->body.release.fh == FH && 58*e0bec057SAlan Somers in->body.release.flags == flags); 59*e0bec057SAlan Somers }, Eq(true)), 60*e0bec057SAlan Somers _) 61*e0bec057SAlan Somers ).WillOnce(Invoke(ReturnErrno(error))) 62*e0bec057SAlan Somers .RetiresOnSaturation(); 63*e0bec057SAlan Somers } 649821f1d3SAlan Somers }; 659821f1d3SAlan Somers 669821f1d3SAlan Somers class ReleaseWithLocks: public Release { 679821f1d3SAlan Somers virtual void SetUp() { 689821f1d3SAlan Somers m_init_flags = FUSE_POSIX_LOCKS; 699821f1d3SAlan Somers Release::SetUp(); 709821f1d3SAlan Somers } 719821f1d3SAlan Somers }; 729821f1d3SAlan Somers 739821f1d3SAlan Somers 749821f1d3SAlan Somers /* If a file descriptor is duplicated, only the last close causes RELEASE */ 759821f1d3SAlan Somers TEST_F(Release, dup) 769821f1d3SAlan Somers { 779821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 789821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 799821f1d3SAlan Somers uint64_t ino = 42; 809821f1d3SAlan Somers int fd, fd2; 819821f1d3SAlan Somers 829821f1d3SAlan Somers expect_lookup(RELPATH, ino, 1); 839821f1d3SAlan Somers expect_open(ino, 0, 1); 849821f1d3SAlan Somers expect_getattr(ino, 0); 85*e0bec057SAlan Somers expect_release(ino, 0, O_RDONLY, 0); 869821f1d3SAlan Somers 879821f1d3SAlan Somers fd = open(FULLPATH, O_RDONLY); 889821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 899821f1d3SAlan Somers 909821f1d3SAlan Somers fd2 = dup(fd); 919821f1d3SAlan Somers 929821f1d3SAlan Somers ASSERT_EQ(0, close(fd2)) << strerror(errno); 939821f1d3SAlan Somers ASSERT_EQ(0, close(fd)) << strerror(errno); 949821f1d3SAlan Somers } 959821f1d3SAlan Somers 969821f1d3SAlan Somers /* 979821f1d3SAlan Somers * Some FUSE filesystem cache data internally and flush it on release. Such 989821f1d3SAlan Somers * filesystems may generate errors during release. On Linux, these get 999821f1d3SAlan Somers * returned by close(2). However, POSIX does not require close(2) to return 1009821f1d3SAlan Somers * this error. FreeBSD's fuse(4) should return EIO if it returns an error at 1019821f1d3SAlan Somers * all. 1029821f1d3SAlan Somers */ 1039821f1d3SAlan Somers /* http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html */ 1049821f1d3SAlan Somers TEST_F(Release, eio) 1059821f1d3SAlan Somers { 1069821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1079821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1089821f1d3SAlan Somers uint64_t ino = 42; 1099821f1d3SAlan Somers int fd; 1109821f1d3SAlan Somers 1119821f1d3SAlan Somers expect_lookup(RELPATH, ino, 1); 1129821f1d3SAlan Somers expect_open(ino, 0, 1); 1139821f1d3SAlan Somers expect_getattr(ino, 0); 114*e0bec057SAlan Somers expect_release(ino, 0, O_WRONLY, EIO); 1159821f1d3SAlan Somers 1169821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 1179821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1189821f1d3SAlan Somers 1199821f1d3SAlan Somers ASSERT_TRUE(0 == close(fd) || errno == EIO) << strerror(errno); 1209821f1d3SAlan Somers } 1219821f1d3SAlan Somers 1229821f1d3SAlan Somers /* 123*e0bec057SAlan Somers * FUSE_RELEASE should contain the same flags used for FUSE_OPEN 124*e0bec057SAlan Somers */ 125*e0bec057SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236340 */ 126*e0bec057SAlan Somers TEST_F(Release, DISABLED_flags) 127*e0bec057SAlan Somers { 128*e0bec057SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 129*e0bec057SAlan Somers const char RELPATH[] = "some_file.txt"; 130*e0bec057SAlan Somers uint64_t ino = 42; 131*e0bec057SAlan Somers int fd; 132*e0bec057SAlan Somers 133*e0bec057SAlan Somers expect_lookup(RELPATH, ino, 1); 134*e0bec057SAlan Somers expect_open(ino, 0, 1); 135*e0bec057SAlan Somers expect_getattr(ino, 0); 136*e0bec057SAlan Somers expect_release(ino, 0, O_RDWR | O_APPEND, 0); 137*e0bec057SAlan Somers 138*e0bec057SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND); 139*e0bec057SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 140*e0bec057SAlan Somers 141*e0bec057SAlan Somers ASSERT_EQ(0, close(fd)) << strerror(errno); 142*e0bec057SAlan Somers } 143*e0bec057SAlan Somers 144*e0bec057SAlan Somers /* 1459821f1d3SAlan Somers * fuse(4) will issue multiple FUSE_OPEN operations for the same file if it's 1469821f1d3SAlan Somers * opened with different modes. Each FUSE_OPEN should get its own 1479821f1d3SAlan Somers * FUSE_RELEASE. 1489821f1d3SAlan Somers */ 1499821f1d3SAlan Somers TEST_F(Release, multiple_opens) 1509821f1d3SAlan Somers { 1519821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1529821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1539821f1d3SAlan Somers uint64_t ino = 42; 1549821f1d3SAlan Somers int fd, fd2; 1559821f1d3SAlan Somers 1569821f1d3SAlan Somers expect_lookup(RELPATH, ino, 2); 1579821f1d3SAlan Somers expect_open(ino, 0, 2); 1589821f1d3SAlan Somers expect_getattr(ino, 0); 159*e0bec057SAlan Somers expect_release(ino, 0, O_RDONLY, 0); 1609821f1d3SAlan Somers 1619821f1d3SAlan Somers fd = open(FULLPATH, O_RDONLY); 1629821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1639821f1d3SAlan Somers 164*e0bec057SAlan Somers expect_release(ino, 0, O_WRONLY, 0); 1659821f1d3SAlan Somers fd2 = open(FULLPATH, O_WRONLY); 1669821f1d3SAlan Somers EXPECT_LE(0, fd2) << strerror(errno); 1679821f1d3SAlan Somers 1689821f1d3SAlan Somers ASSERT_EQ(0, close(fd2)) << strerror(errno); 1699821f1d3SAlan Somers ASSERT_EQ(0, close(fd)) << strerror(errno); 1709821f1d3SAlan Somers } 1719821f1d3SAlan Somers 1729821f1d3SAlan Somers TEST_F(Release, ok) 1739821f1d3SAlan Somers { 1749821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1759821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1769821f1d3SAlan Somers uint64_t ino = 42; 1779821f1d3SAlan Somers int fd; 1789821f1d3SAlan Somers 1799821f1d3SAlan Somers expect_lookup(RELPATH, ino, 1); 1809821f1d3SAlan Somers expect_open(ino, 0, 1); 1819821f1d3SAlan Somers expect_getattr(ino, 0); 182*e0bec057SAlan Somers expect_release(ino, 0, O_RDONLY, 0); 1839821f1d3SAlan Somers 1849821f1d3SAlan Somers fd = open(FULLPATH, O_RDONLY); 1859821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1869821f1d3SAlan Somers 1879821f1d3SAlan Somers ASSERT_EQ(0, close(fd)) << strerror(errno); 1889821f1d3SAlan Somers } 1899821f1d3SAlan Somers 1909821f1d3SAlan Somers /* When closing a file with a POSIX file lock, release should release the lock*/ 1919821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=234581 */ 1929821f1d3SAlan Somers TEST_F(ReleaseWithLocks, DISABLED_unlock_on_close) 1939821f1d3SAlan Somers { 1949821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1959821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 1969821f1d3SAlan Somers uint64_t ino = 42; 1979821f1d3SAlan Somers int fd; 1989821f1d3SAlan Somers struct flock fl; 1999821f1d3SAlan Somers pid_t pid = getpid(); 2009821f1d3SAlan Somers 2019821f1d3SAlan Somers expect_lookup(RELPATH, ino, 1); 2029821f1d3SAlan Somers expect_open(ino, 0, 1); 2039821f1d3SAlan Somers expect_getattr(ino, 0); 2049821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 2059821f1d3SAlan Somers ResultOf([=](auto in) { 2069821f1d3SAlan Somers return (in->header.opcode == FUSE_SETLK && 2079821f1d3SAlan Somers in->header.nodeid == ino && 2089821f1d3SAlan Somers in->body.setlk.fh == FH); 2099821f1d3SAlan Somers }, Eq(true)), 2109821f1d3SAlan Somers _) 2119821f1d3SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto out) { 2129821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, setlk); 2139821f1d3SAlan Somers out->body.setlk.lk = in->body.setlk.lk; 2149821f1d3SAlan Somers }))); 215*e0bec057SAlan Somers expect_release(ino, (uint64_t)pid, O_RDWR, 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 2279821f1d3SAlan Somers ASSERT_EQ(0, close(fd)) << strerror(errno); 2289821f1d3SAlan Somers } 229