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