1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 <sys/param.h> 33 #include <sys/mount.h> 34 #include <semaphore.h> 35 } 36 37 #include "mockfs.hh" 38 #include "utils.hh" 39 40 using namespace testing; 41 42 class Statfs: public FuseTest {}; 43 44 TEST_F(Statfs, eio) 45 { 46 struct statfs statbuf; 47 48 EXPECT_CALL(*m_mock, process( 49 ResultOf([](auto in) { 50 return (in.header.opcode == FUSE_STATFS); 51 }, Eq(true)), 52 _) 53 ).WillOnce(Invoke(ReturnErrno(EIO))); 54 55 ASSERT_NE(0, statfs("mountpoint", &statbuf)); 56 ASSERT_EQ(EIO, errno); 57 } 58 59 /* 60 * When the daemon is dead but the filesystem is still mounted, fuse(4) fakes 61 * the statfs(2) response, which is necessary for unmounting. 62 */ 63 TEST_F(Statfs, enotconn) 64 { 65 struct statfs statbuf; 66 char mp[PATH_MAX]; 67 68 m_mock->kill_daemon(); 69 70 ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno); 71 strlcat(mp, "/mountpoint", PATH_MAX); 72 ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno); 73 74 EXPECT_EQ(getuid(), statbuf.f_owner); 75 EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename)); 76 EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname)); 77 EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); 78 } 79 80 static void* statfs_th(void* arg) { 81 ssize_t r; 82 struct statfs *sb = (struct statfs*)arg; 83 84 r = statfs("mountpoint", sb); 85 if (r >= 0) 86 return 0; 87 else 88 return (void*)(intptr_t)errno; 89 } 90 91 /* 92 * Like the enotconn test, but in this case the daemon dies after we send the 93 * FUSE_STATFS operation but before we get a response. 94 */ 95 TEST_F(Statfs, enotconn_while_blocked) 96 { 97 struct statfs statbuf; 98 void *thr0_value; 99 pthread_t th0; 100 char mp[PATH_MAX]; 101 sem_t sem; 102 103 ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); 104 105 EXPECT_CALL(*m_mock, process( 106 ResultOf([](auto in) { 107 return (in.header.opcode == FUSE_STATFS); 108 }, Eq(true)), 109 _) 110 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { 111 sem_post(&sem); 112 /* Just block until the daemon dies */ 113 })); 114 115 ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno); 116 strlcat(mp, "/mountpoint", PATH_MAX); 117 ASSERT_EQ(0, pthread_create(&th0, NULL, statfs_th, (void*)&statbuf)) 118 << strerror(errno); 119 120 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno); 121 m_mock->kill_daemon(); 122 123 pthread_join(th0, &thr0_value); 124 ASSERT_EQ(0, (intptr_t)thr0_value); 125 126 EXPECT_EQ(getuid(), statbuf.f_owner); 127 EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename)); 128 EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname)); 129 EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); 130 } 131 132 TEST_F(Statfs, ok) 133 { 134 struct statfs statbuf; 135 char mp[PATH_MAX]; 136 137 EXPECT_CALL(*m_mock, process( 138 ResultOf([](auto in) { 139 return (in.header.opcode == FUSE_STATFS); 140 }, Eq(true)), 141 _) 142 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 143 SET_OUT_HEADER_LEN(out, statfs); 144 out.body.statfs.st.blocks = 1000; 145 out.body.statfs.st.bfree = 100; 146 out.body.statfs.st.bavail = 200; 147 out.body.statfs.st.files = 5; 148 out.body.statfs.st.ffree = 6; 149 out.body.statfs.st.namelen = 128; 150 out.body.statfs.st.frsize = 1024; 151 }))); 152 153 ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno); 154 strlcat(mp, "/mountpoint", PATH_MAX); 155 ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno); 156 EXPECT_EQ(1024ul, statbuf.f_bsize); 157 /* 158 * fuse(4) ignores the filesystem's reported optimal transfer size, and 159 * chooses a size that works well with the rest of the system instead 160 */ 161 EXPECT_EQ(1000ul, statbuf.f_blocks); 162 EXPECT_EQ(100ul, statbuf.f_bfree); 163 EXPECT_EQ(200l, statbuf.f_bavail); 164 EXPECT_EQ(5ul, statbuf.f_files); 165 EXPECT_EQ(6l, statbuf.f_ffree); 166 EXPECT_EQ(128u, statbuf.f_namemax); 167 EXPECT_EQ(getuid(), statbuf.f_owner); 168 EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename)); 169 EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname)); 170 EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname)); 171 } 172