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