xref: /freebsd/tests/sys/fs/fusefs/statfs.cc (revision b3e76948)
19821f1d3SAlan Somers /*-
24d846d26SWarner 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.
299821f1d3SAlan Somers  */
309821f1d3SAlan Somers 
319821f1d3SAlan Somers extern "C" {
329821f1d3SAlan Somers #include <sys/param.h>
339821f1d3SAlan Somers #include <sys/mount.h>
347e0aac24SAlan Somers #include <semaphore.h>
359821f1d3SAlan Somers }
369821f1d3SAlan Somers 
379821f1d3SAlan Somers #include "mockfs.hh"
389821f1d3SAlan Somers #include "utils.hh"
399821f1d3SAlan Somers 
409821f1d3SAlan Somers using namespace testing;
419821f1d3SAlan Somers 
429821f1d3SAlan Somers class Statfs: public FuseTest {};
439821f1d3SAlan Somers 
TEST_F(Statfs,eio)449821f1d3SAlan Somers TEST_F(Statfs, eio)
459821f1d3SAlan Somers {
469821f1d3SAlan Somers 	struct statfs statbuf;
479821f1d3SAlan Somers 
489821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
499821f1d3SAlan Somers 		ResultOf([](auto in) {
5029edc611SAlan Somers 			return (in.header.opcode == FUSE_STATFS);
519821f1d3SAlan Somers 		}, Eq(true)),
529821f1d3SAlan Somers 		_)
539821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnErrno(EIO)));
549821f1d3SAlan Somers 
559821f1d3SAlan Somers 	ASSERT_NE(0, statfs("mountpoint", &statbuf));
569821f1d3SAlan Somers 	ASSERT_EQ(EIO, errno);
579821f1d3SAlan Somers }
589821f1d3SAlan Somers 
599821f1d3SAlan Somers /*
609821f1d3SAlan Somers  * When the daemon is dead but the filesystem is still mounted, fuse(4) fakes
619821f1d3SAlan Somers  * the statfs(2) response, which is necessary for unmounting.
629821f1d3SAlan Somers  */
TEST_F(Statfs,enotconn)639821f1d3SAlan Somers TEST_F(Statfs, enotconn)
649821f1d3SAlan Somers {
659821f1d3SAlan Somers 	struct statfs statbuf;
669821f1d3SAlan Somers 	char mp[PATH_MAX];
679821f1d3SAlan Somers 
689821f1d3SAlan Somers 	m_mock->kill_daemon();
699821f1d3SAlan Somers 
705a0b9a27SAlan Somers 	ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno);
719821f1d3SAlan Somers 	strlcat(mp, "/mountpoint", PATH_MAX);
729821f1d3SAlan Somers 	ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
739821f1d3SAlan Somers 
749821f1d3SAlan Somers 	EXPECT_EQ(getuid(), statbuf.f_owner);
759821f1d3SAlan Somers 	EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
769821f1d3SAlan Somers 	EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
779821f1d3SAlan Somers 	EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname));
789821f1d3SAlan Somers }
799821f1d3SAlan Somers 
statfs_th(void * arg)807e0aac24SAlan Somers static void* statfs_th(void* arg) {
817e0aac24SAlan Somers 	ssize_t r;
827e0aac24SAlan Somers 	struct statfs *sb = (struct statfs*)arg;
837e0aac24SAlan Somers 
847e0aac24SAlan Somers 	r = statfs("mountpoint", sb);
857e0aac24SAlan Somers 	if (r >= 0)
867e0aac24SAlan Somers 		return 0;
877e0aac24SAlan Somers 	else
887e0aac24SAlan Somers 		return (void*)(intptr_t)errno;
897e0aac24SAlan Somers }
907e0aac24SAlan Somers 
917e0aac24SAlan Somers /*
927e0aac24SAlan Somers  * Like the enotconn test, but in this case the daemon dies after we send the
937e0aac24SAlan Somers  * FUSE_STATFS operation but before we get a response.
947e0aac24SAlan Somers  */
TEST_F(Statfs,enotconn_while_blocked)957e0aac24SAlan Somers TEST_F(Statfs, enotconn_while_blocked)
967e0aac24SAlan Somers {
977e0aac24SAlan Somers 	struct statfs statbuf;
987e0aac24SAlan Somers 	void *thr0_value;
997e0aac24SAlan Somers 	pthread_t th0;
1007e0aac24SAlan Somers 	char mp[PATH_MAX];
1017e0aac24SAlan Somers 	sem_t sem;
1027e0aac24SAlan Somers 
1037e0aac24SAlan Somers 	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
1047e0aac24SAlan Somers 
1057e0aac24SAlan Somers 	EXPECT_CALL(*m_mock, process(
1067e0aac24SAlan Somers 		ResultOf([](auto in) {
10729edc611SAlan Somers 			return (in.header.opcode == FUSE_STATFS);
1087e0aac24SAlan Somers 		}, Eq(true)),
1097e0aac24SAlan Somers 		_)
1107e0aac24SAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
1117e0aac24SAlan Somers 		sem_post(&sem);
1127e0aac24SAlan Somers 		/* Just block until the daemon dies */
1137e0aac24SAlan Somers 	}));
1147e0aac24SAlan Somers 
1155a0b9a27SAlan Somers 	ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno);
1167e0aac24SAlan Somers 	strlcat(mp, "/mountpoint", PATH_MAX);
1177e0aac24SAlan Somers 	ASSERT_EQ(0, pthread_create(&th0, NULL, statfs_th, (void*)&statbuf))
1187e0aac24SAlan Somers 		<< strerror(errno);
1197e0aac24SAlan Somers 
1207e0aac24SAlan Somers 	ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
1217e0aac24SAlan Somers 	m_mock->kill_daemon();
1227e0aac24SAlan Somers 
1237e0aac24SAlan Somers 	pthread_join(th0, &thr0_value);
1247e0aac24SAlan Somers 	ASSERT_EQ(0, (intptr_t)thr0_value);
1257e0aac24SAlan Somers 
1267e0aac24SAlan Somers 	EXPECT_EQ(getuid(), statbuf.f_owner);
1277e0aac24SAlan Somers 	EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
1287e0aac24SAlan Somers 	EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
1297e0aac24SAlan Somers 	EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname));
1307e0aac24SAlan Somers }
1317e0aac24SAlan Somers 
TEST_F(Statfs,ok)1329821f1d3SAlan Somers TEST_F(Statfs, ok)
1339821f1d3SAlan Somers {
1349821f1d3SAlan Somers 	struct statfs statbuf;
1359821f1d3SAlan Somers 	char mp[PATH_MAX];
1369821f1d3SAlan Somers 
1379821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
1389821f1d3SAlan Somers 		ResultOf([](auto in) {
13929edc611SAlan Somers 			return (in.header.opcode == FUSE_STATFS);
1409821f1d3SAlan Somers 		}, Eq(true)),
1419821f1d3SAlan Somers 		_)
14229edc611SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1439821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, statfs);
14429edc611SAlan Somers 		out.body.statfs.st.blocks = 1000;
14529edc611SAlan Somers 		out.body.statfs.st.bfree = 100;
14629edc611SAlan Somers 		out.body.statfs.st.bavail = 200;
14729edc611SAlan Somers 		out.body.statfs.st.files = 5;
14829edc611SAlan Somers 		out.body.statfs.st.ffree = 6;
14929edc611SAlan Somers 		out.body.statfs.st.namelen = 128;
15029edc611SAlan Somers 		out.body.statfs.st.frsize = 1024;
1519821f1d3SAlan Somers 	})));
1529821f1d3SAlan Somers 
1535a0b9a27SAlan Somers 	ASSERT_NE(nullptr, getcwd(mp, PATH_MAX)) << strerror(errno);
1549821f1d3SAlan Somers 	strlcat(mp, "/mountpoint", PATH_MAX);
1559821f1d3SAlan Somers 	ASSERT_EQ(0, statfs("mountpoint", &statbuf)) << strerror(errno);
1569821f1d3SAlan Somers 	EXPECT_EQ(1024ul, statbuf.f_bsize);
1579821f1d3SAlan Somers 	/*
1589821f1d3SAlan Somers 	 * fuse(4) ignores the filesystem's reported optimal transfer size, and
1599821f1d3SAlan Somers 	 * chooses a size that works well with the rest of the system instead
1609821f1d3SAlan Somers 	 */
1619821f1d3SAlan Somers 	EXPECT_EQ(1000ul, statbuf.f_blocks);
1629821f1d3SAlan Somers 	EXPECT_EQ(100ul, statbuf.f_bfree);
1639821f1d3SAlan Somers 	EXPECT_EQ(200l, statbuf.f_bavail);
1649821f1d3SAlan Somers 	EXPECT_EQ(5ul, statbuf.f_files);
1659821f1d3SAlan Somers 	EXPECT_EQ(6l, statbuf.f_ffree);
1669821f1d3SAlan Somers 	EXPECT_EQ(128u, statbuf.f_namemax);
1679821f1d3SAlan Somers 	EXPECT_EQ(getuid(), statbuf.f_owner);
1689821f1d3SAlan Somers 	EXPECT_EQ(0, strcmp("fusefs", statbuf.f_fstypename));
1699821f1d3SAlan Somers 	EXPECT_EQ(0, strcmp("/dev/fuse", statbuf.f_mntfromname));
1709821f1d3SAlan Somers 	EXPECT_EQ(0, strcmp(mp, statbuf.f_mntonname));
1719821f1d3SAlan Somers }
172