]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/utils.cc
fusefs: adapt the tests to the fuse => fusefs rename
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / utils.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 #include <sys/param.h>
32 #include <sys/module.h>
33 #include <sys/sysctl.h>
34
35 #include <gtest/gtest.h>
36 #include <unistd.h>
37
38 #include "mockfs.hh"
39 #include "utils.hh"
40
41 using namespace testing;
42
43 /* Check that fusefs(4) is accessible and the current user can mount(2) */
44 void check_environment()
45 {
46         const char *mod_name = "fusefs";
47         const char *devnode = "/dev/fuse";
48         const char *usermount_node = "vfs.usermount";
49         int usermount_val = 0;
50         size_t usermount_size = sizeof(usermount_val);
51         if (modfind(mod_name) == -1) {
52                 GTEST_SKIP() << "Module " << mod_name <<
53                         " could not be resolved";
54         }
55         if (eaccess(devnode, R_OK | W_OK)) {
56                 if (errno == ENOENT) {
57                         GTEST_SKIP() << devnode << " does not exist";
58                 } else if (errno == EACCES) {
59                         GTEST_SKIP() << devnode <<
60                             " is not accessible by the current user";
61                 } else {
62                         GTEST_SKIP() << strerror(errno);
63                 }
64         }
65         sysctlbyname(usermount_node, &usermount_val, &usermount_size,
66                      NULL, 0);
67         if (geteuid() != 0 && !usermount_val)
68                 GTEST_SKIP() << "current user is not allowed to mount";
69 }
70
71 class FuseEnv: public Environment {
72         virtual void SetUp() {
73         }
74 };
75
76 void FuseTest::SetUp() {
77         const char *node = "vfs.maxbcachebuf";
78         int val = 0;
79         size_t size = sizeof(val);
80
81         /*
82          * XXX check_environment should be called from FuseEnv::SetUp, but
83          * can't due to https://github.com/google/googletest/issues/2189
84          */
85         check_environment();
86         if (IsSkipped())
87                 return;
88
89         ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
90                 << strerror(errno);
91         m_maxbcachebuf = val;
92
93         try {
94                 m_mock = new MockFS(m_maxreadahead, m_push_symlinks_in,
95                         m_default_permissions, m_init_flags);
96         } catch (std::system_error err) {
97                 FAIL() << err.what();
98         }
99 }
100
101 void FuseTest::expect_getattr(uint64_t ino, uint64_t size)
102 {
103         /* Until the attr cache is working, we may send an additional GETATTR */
104         EXPECT_CALL(*m_mock, process(
105                 ResultOf([=](auto in) {
106                         return (in->header.opcode == FUSE_GETATTR &&
107                                 in->header.nodeid == ino);
108                 }, Eq(true)),
109                 _)
110         ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
111                 SET_OUT_HEADER_LEN(out, attr);
112                 out->body.attr.attr.ino = ino;  // Must match nodeid
113                 out->body.attr.attr.mode = S_IFREG | 0644;
114                 out->body.attr.attr.size = size;
115                 out->body.attr.attr_valid = UINT64_MAX;
116         })));
117 }
118
119 void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
120         uint64_t size, int times)
121 {
122         EXPECT_LOOKUP(1, relpath)
123         .Times(times)
124         .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
125                 SET_OUT_HEADER_LEN(out, entry);
126                 out->body.entry.attr.mode = mode;
127                 out->body.entry.nodeid = ino;
128                 out->body.entry.attr.nlink = 1;
129                 out->body.entry.attr_valid = UINT64_MAX;
130                 out->body.entry.attr.size = size;
131         })));
132 }
133
134 void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
135 {
136         EXPECT_CALL(*m_mock, process(
137                 ResultOf([=](auto in) {
138                         return (in->header.opcode == FUSE_OPEN &&
139                                 in->header.nodeid == ino);
140                 }, Eq(true)),
141                 _)
142         ).Times(times)
143         .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
144                 out->header.len = sizeof(out->header);
145                 SET_OUT_HEADER_LEN(out, open);
146                 out->body.open.fh = FH;
147                 out->body.open.open_flags = flags;
148         })));
149 }
150
151 void FuseTest::expect_opendir(uint64_t ino)
152 {
153         EXPECT_CALL(*m_mock, process(
154                 ResultOf([](auto in) {
155                         return (in->header.opcode == FUSE_STATFS);
156                 }, Eq(true)),
157                 _)
158         ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
159                 SET_OUT_HEADER_LEN(out, statfs);
160         })));
161
162         EXPECT_CALL(*m_mock, process(
163                 ResultOf([=](auto in) {
164                         return (in->header.opcode == FUSE_OPENDIR &&
165                                 in->header.nodeid == ino);
166                 }, Eq(true)),
167                 _)
168         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
169                 out->header.len = sizeof(out->header);
170                 SET_OUT_HEADER_LEN(out, open);
171                 out->body.open.fh = FH;
172         })));
173 }
174
175 void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
176         uint64_t osize, const void *contents)
177 {
178         EXPECT_CALL(*m_mock, process(
179                 ResultOf([=](auto in) {
180                         return (in->header.opcode == FUSE_READ &&
181                                 in->header.nodeid == ino &&
182                                 in->body.read.fh == FH &&
183                                 in->body.read.offset == offset &&
184                                 in->body.read.size == isize);
185                 }, Eq(true)),
186                 _)
187         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
188                 out->header.len = sizeof(struct fuse_out_header) + osize;
189                 memmove(out->body.bytes, contents, osize);
190         }))).RetiresOnSaturation();
191 }
192
193 void FuseTest::expect_release(uint64_t ino, int times, uint64_t lock_owner,
194         int error)
195 {
196         EXPECT_CALL(*m_mock, process(
197                 ResultOf([=](auto in) {
198                         return (in->header.opcode == FUSE_RELEASE &&
199                                 in->header.nodeid == ino &&
200                                 in->body.release.lock_owner == lock_owner &&
201                                 in->body.release.fh == FH);
202                 }, Eq(true)),
203                 _)
204         ).Times(times)
205         .WillRepeatedly(Invoke(ReturnErrno(error)));
206 }
207 void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
208         uint64_t osize, uint32_t flags, const void *contents)
209 {
210         EXPECT_CALL(*m_mock, process(
211                 ResultOf([=](auto in) {
212                         const char *buf = (const char*)in->body.bytes +
213                                 sizeof(struct fuse_write_in);
214                         bool pid_ok;
215
216                         if (in->body.write.write_flags & FUSE_WRITE_CACHE)
217                                 pid_ok = true;
218                         else
219                                 pid_ok = (pid_t)in->header.pid == getpid();
220
221                         return (in->header.opcode == FUSE_WRITE &&
222                                 in->header.nodeid == ino &&
223                                 in->body.write.fh == FH &&
224                                 in->body.write.offset == offset  &&
225                                 in->body.write.size == isize &&
226                                 pid_ok &&
227                                 in->body.write.write_flags == flags &&
228                                 0 == bcmp(buf, contents, isize));
229                 }, Eq(true)),
230                 _)
231         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
232                 SET_OUT_HEADER_LEN(out, write);
233                 out->body.write.size = osize;
234         })));
235 }
236
237 static void usage(char* progname) {
238         fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname);
239         exit(2);
240 }
241
242 int main(int argc, char **argv) {
243         int ch;
244         FuseEnv *fuse_env = new FuseEnv;
245
246         InitGoogleTest(&argc, argv);
247         AddGlobalTestEnvironment(fuse_env);
248
249         while ((ch = getopt(argc, argv, "v")) != -1) {
250                 switch (ch) {
251                         case 'v':
252                                 verbosity++;
253                                 break;
254                         default:
255                                 usage(argv[0]);
256                                 break;
257                 }
258         }
259
260         return (RUN_ALL_TESTS());
261 }