19821f1d3SAlan Somers /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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. 291fa8ebfbSAlan Somers * 301fa8ebfbSAlan Somers * $FreeBSD$ 319821f1d3SAlan Somers */ 329821f1d3SAlan Somers 339821f1d3SAlan Somers extern "C" { 349821f1d3SAlan Somers #include <dirent.h> 3527537990SAlan Somers 369821f1d3SAlan Somers #include <fcntl.h> 3727537990SAlan Somers #include <semaphore.h> 389821f1d3SAlan Somers } 399821f1d3SAlan Somers 409821f1d3SAlan Somers #include "mockfs.hh" 419821f1d3SAlan Somers #include "utils.hh" 429821f1d3SAlan Somers 439821f1d3SAlan Somers using namespace testing; 449821f1d3SAlan Somers 459821f1d3SAlan Somers class Opendir: public FuseTest { 469821f1d3SAlan Somers public: 479821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino) 489821f1d3SAlan Somers { 499821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 0, 1); 509821f1d3SAlan Somers } 51363a7416SAlan Somers 52363a7416SAlan Somers void expect_opendir(uint64_t ino, uint32_t flags, ProcessMockerT r) 53363a7416SAlan Somers { 54363a7416SAlan Somers /* opendir(3) calls fstatfs */ 55363a7416SAlan Somers EXPECT_CALL(*m_mock, process( 56363a7416SAlan Somers ResultOf([](auto in) { 5729edc611SAlan Somers return (in.header.opcode == FUSE_STATFS); 58363a7416SAlan Somers }, Eq(true)), 59363a7416SAlan Somers _) 6029edc611SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 61363a7416SAlan Somers SET_OUT_HEADER_LEN(out, statfs); 62363a7416SAlan Somers }))); 63363a7416SAlan Somers 64363a7416SAlan Somers EXPECT_CALL(*m_mock, process( 65363a7416SAlan Somers ResultOf([=](auto in) { 6629edc611SAlan Somers return (in.header.opcode == FUSE_OPENDIR && 6729edc611SAlan Somers in.header.nodeid == ino && 6829edc611SAlan Somers in.body.opendir.flags == flags); 69363a7416SAlan Somers }, Eq(true)), 70363a7416SAlan Somers _) 71363a7416SAlan Somers ).WillOnce(Invoke(r)); 72363a7416SAlan Somers } 73363a7416SAlan Somers 749821f1d3SAlan Somers }; 759821f1d3SAlan Somers 767124d2bcSAlan Somers class OpendirNoOpendirSupport: public Opendir { 777124d2bcSAlan Somers virtual void SetUp() { 787124d2bcSAlan Somers m_init_flags = FUSE_NO_OPENDIR_SUPPORT; 797124d2bcSAlan Somers FuseTest::SetUp(); 807124d2bcSAlan Somers } 817124d2bcSAlan Somers }; 827124d2bcSAlan Somers 839821f1d3SAlan Somers 849821f1d3SAlan Somers /* 859821f1d3SAlan Somers * The fuse daemon fails the request with enoent. This usually indicates a 869821f1d3SAlan Somers * race condition: some other FUSE client removed the file in between when the 879821f1d3SAlan Somers * kernel checked for it with lookup and tried to open it 889821f1d3SAlan Somers */ 899821f1d3SAlan Somers TEST_F(Opendir, enoent) 909821f1d3SAlan Somers { 919821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 929821f1d3SAlan Somers const char RELPATH[] = "some_dir"; 939821f1d3SAlan Somers uint64_t ino = 42; 9427537990SAlan Somers sem_t sem; 9527537990SAlan Somers 9627537990SAlan Somers ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); 979821f1d3SAlan Somers 989821f1d3SAlan Somers expect_lookup(RELPATH, ino); 99363a7416SAlan Somers expect_opendir(ino, O_RDONLY, ReturnErrno(ENOENT)); 10027537990SAlan Somers // Since FUSE_OPENDIR returns ENOENT, the kernel will reclaim the vnode 10127537990SAlan Somers // and send a FUSE_FORGET 10227537990SAlan Somers expect_forget(ino, 1, &sem); 1039821f1d3SAlan Somers 1048e765737SAlan Somers ASSERT_EQ(-1, open(FULLPATH, O_DIRECTORY)); 1059821f1d3SAlan Somers EXPECT_EQ(ENOENT, errno); 10627537990SAlan Somers 10727537990SAlan Somers sem_wait(&sem); 10827537990SAlan Somers sem_destroy(&sem); 1099821f1d3SAlan Somers } 1109821f1d3SAlan Somers 1119821f1d3SAlan Somers /* 1129821f1d3SAlan Somers * The daemon is responsible for checking file permissions (unless the 1139821f1d3SAlan Somers * default_permissions mount option was used) 1149821f1d3SAlan Somers */ 1159821f1d3SAlan Somers TEST_F(Opendir, eperm) 1169821f1d3SAlan Somers { 1179821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 1189821f1d3SAlan Somers const char RELPATH[] = "some_dir"; 1199821f1d3SAlan Somers uint64_t ino = 42; 1209821f1d3SAlan Somers 1219821f1d3SAlan Somers expect_lookup(RELPATH, ino); 122363a7416SAlan Somers expect_opendir(ino, O_RDONLY, ReturnErrno(EPERM)); 1239821f1d3SAlan Somers 1244ca1c0b7SAlan Somers EXPECT_EQ(-1, open(FULLPATH, O_DIRECTORY)); 1259821f1d3SAlan Somers EXPECT_EQ(EPERM, errno); 1269821f1d3SAlan Somers } 1279821f1d3SAlan Somers 1289821f1d3SAlan Somers TEST_F(Opendir, open) 1299821f1d3SAlan Somers { 1309821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 1319821f1d3SAlan Somers const char RELPATH[] = "some_dir"; 1329821f1d3SAlan Somers uint64_t ino = 42; 1338e765737SAlan Somers int fd; 1349821f1d3SAlan Somers 1359821f1d3SAlan Somers expect_lookup(RELPATH, ino); 136363a7416SAlan Somers expect_opendir(ino, O_RDONLY, 13729edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 1389821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, open); 139363a7416SAlan Somers })); 1409821f1d3SAlan Somers 1418e765737SAlan Somers fd = open(FULLPATH, O_DIRECTORY); 142d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1438e765737SAlan Somers 1448e765737SAlan Somers leak(fd); 1459821f1d3SAlan Somers } 1469821f1d3SAlan Somers 147363a7416SAlan Somers /* Directories can be opened O_EXEC for stuff like fchdir(2) */ 148363a7416SAlan Somers TEST_F(Opendir, open_exec) 149363a7416SAlan Somers { 150363a7416SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 151363a7416SAlan Somers const char RELPATH[] = "some_dir"; 152363a7416SAlan Somers uint64_t ino = 42; 153363a7416SAlan Somers int fd; 154363a7416SAlan Somers 155363a7416SAlan Somers expect_lookup(RELPATH, ino); 156363a7416SAlan Somers expect_opendir(ino, O_EXEC, 15729edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 158363a7416SAlan Somers SET_OUT_HEADER_LEN(out, open); 159363a7416SAlan Somers })); 160363a7416SAlan Somers 161363a7416SAlan Somers fd = open(FULLPATH, O_EXEC | O_DIRECTORY); 162363a7416SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1638e765737SAlan Somers 1648e765737SAlan Somers leak(fd); 165363a7416SAlan Somers } 166363a7416SAlan Somers 1679821f1d3SAlan Somers TEST_F(Opendir, opendir) 1689821f1d3SAlan Somers { 1699821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 1709821f1d3SAlan Somers const char RELPATH[] = "some_dir"; 1719821f1d3SAlan Somers uint64_t ino = 42; 1729821f1d3SAlan Somers 1739821f1d3SAlan Somers expect_lookup(RELPATH, ino); 174363a7416SAlan Somers expect_opendir(ino, O_RDONLY, 17529edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 1769821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, open); 177363a7416SAlan Somers })); 1789821f1d3SAlan Somers 1799821f1d3SAlan Somers errno = 0; 1805a0b9a27SAlan Somers EXPECT_NE(nullptr, opendir(FULLPATH)) << strerror(errno); 1819821f1d3SAlan Somers } 1827124d2bcSAlan Somers 1837124d2bcSAlan Somers /* 1847124d2bcSAlan Somers * Without FUSE_NO_OPENDIR_SUPPORT, returning ENOSYS is an error 1857124d2bcSAlan Somers */ 1867124d2bcSAlan Somers TEST_F(Opendir, enosys) 1877124d2bcSAlan Somers { 1887124d2bcSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1897124d2bcSAlan Somers const char RELPATH[] = "some_file.txt"; 1907124d2bcSAlan Somers uint64_t ino = 42; 1917124d2bcSAlan Somers 1927124d2bcSAlan Somers expect_lookup(RELPATH, ino); 1937124d2bcSAlan Somers expect_opendir(ino, O_RDONLY, ReturnErrno(ENOSYS)); 1947124d2bcSAlan Somers 1957124d2bcSAlan Somers EXPECT_EQ(-1, open(FULLPATH, O_DIRECTORY)); 1967124d2bcSAlan Somers EXPECT_EQ(ENOSYS, errno); 1977124d2bcSAlan Somers } 1987124d2bcSAlan Somers 1997124d2bcSAlan Somers /* 2007124d2bcSAlan Somers * If a fuse server sets FUSE_NO_OPENDIR_SUPPORT and returns ENOSYS to a 2017124d2bcSAlan Somers * FUSE_OPENDIR, then it and subsequent FUSE_OPENDIR and FUSE_RELEASEDIR 2027124d2bcSAlan Somers * operations will also succeed automatically without being sent to the server. 2037124d2bcSAlan Somers */ 2047124d2bcSAlan Somers TEST_F(OpendirNoOpendirSupport, enosys) 2057124d2bcSAlan Somers { 2067124d2bcSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2077124d2bcSAlan Somers const char RELPATH[] = "some_file.txt"; 2087124d2bcSAlan Somers uint64_t ino = 42; 2097124d2bcSAlan Somers int fd; 2107124d2bcSAlan Somers 2117124d2bcSAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFDIR | 0755, 0, 2); 2127124d2bcSAlan Somers expect_opendir(ino, O_RDONLY, ReturnErrno(ENOSYS)); 2137124d2bcSAlan Somers 2147124d2bcSAlan Somers fd = open(FULLPATH, O_DIRECTORY); 2157124d2bcSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 2167124d2bcSAlan Somers close(fd); 2177124d2bcSAlan Somers 2187124d2bcSAlan Somers fd = open(FULLPATH, O_DIRECTORY); 2197124d2bcSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 2207124d2bcSAlan Somers 2217124d2bcSAlan Somers leak(fd); 2227124d2bcSAlan Somers } 223