]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/statfs.cc
fusefs: automatically update mtime and ctime on write
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / statfs.cc
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 <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(NULL, 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(NULL, 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(NULL, 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 }