]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/utils.cc
fusefs: add a test case for the allow_other mount option
[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_allow_other,
95                         m_default_permissions, m_push_symlinks_in,
96                         m_init_flags);
97         } catch (std::system_error err) {
98                 FAIL() << err.what();
99         }
100 }
101
102 void
103 FuseTest::expect_access(uint64_t ino, mode_t access_mode, int error)
104 {
105         EXPECT_CALL(*m_mock, process(
106                 ResultOf([=](auto in) {
107                         return (in->header.opcode == FUSE_ACCESS &&
108                                 in->header.nodeid == ino &&
109                                 in->body.access.mask == access_mode);
110                 }, Eq(true)),
111                 _)
112         ).WillOnce(Invoke(ReturnErrno(error)));
113 }
114
115 void FuseTest::expect_getattr(uint64_t ino, uint64_t size)
116 {
117         /* Until the attr cache is working, we may send an additional GETATTR */
118         EXPECT_CALL(*m_mock, process(
119                 ResultOf([=](auto in) {
120                         return (in->header.opcode == FUSE_GETATTR &&
121                                 in->header.nodeid == ino);
122                 }, Eq(true)),
123                 _)
124         ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
125                 SET_OUT_HEADER_LEN(out, attr);
126                 out->body.attr.attr.ino = ino;  // Must match nodeid
127                 out->body.attr.attr.mode = S_IFREG | 0644;
128                 out->body.attr.attr.size = size;
129                 out->body.attr.attr_valid = UINT64_MAX;
130         })));
131 }
132
133 void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
134         uint64_t size, int times)
135 {
136         EXPECT_LOOKUP(1, relpath)
137         .Times(times)
138         .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
139                 SET_OUT_HEADER_LEN(out, entry);
140                 out->body.entry.attr.mode = mode;
141                 out->body.entry.nodeid = ino;
142                 out->body.entry.attr.nlink = 1;
143                 out->body.entry.attr_valid = UINT64_MAX;
144                 out->body.entry.attr.size = size;
145         })));
146 }
147
148 void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
149 {
150         EXPECT_CALL(*m_mock, process(
151                 ResultOf([=](auto in) {
152                         return (in->header.opcode == FUSE_OPEN &&
153                                 in->header.nodeid == ino);
154                 }, Eq(true)),
155                 _)
156         ).Times(times)
157         .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
158                 out->header.len = sizeof(out->header);
159                 SET_OUT_HEADER_LEN(out, open);
160                 out->body.open.fh = FH;
161                 out->body.open.open_flags = flags;
162         })));
163 }
164
165 void FuseTest::expect_opendir(uint64_t ino)
166 {
167         EXPECT_CALL(*m_mock, process(
168                 ResultOf([](auto in) {
169                         return (in->header.opcode == FUSE_STATFS);
170                 }, Eq(true)),
171                 _)
172         ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
173                 SET_OUT_HEADER_LEN(out, statfs);
174         })));
175
176         EXPECT_CALL(*m_mock, process(
177                 ResultOf([=](auto in) {
178                         return (in->header.opcode == FUSE_OPENDIR &&
179                                 in->header.nodeid == ino);
180                 }, Eq(true)),
181                 _)
182         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
183                 out->header.len = sizeof(out->header);
184                 SET_OUT_HEADER_LEN(out, open);
185                 out->body.open.fh = FH;
186         })));
187 }
188
189 void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
190         uint64_t osize, const void *contents)
191 {
192         EXPECT_CALL(*m_mock, process(
193                 ResultOf([=](auto in) {
194                         return (in->header.opcode == FUSE_READ &&
195                                 in->header.nodeid == ino &&
196                                 in->body.read.fh == FH &&
197                                 in->body.read.offset == offset &&
198                                 in->body.read.size == isize);
199                 }, Eq(true)),
200                 _)
201         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
202                 out->header.len = sizeof(struct fuse_out_header) + osize;
203                 memmove(out->body.bytes, contents, osize);
204         }))).RetiresOnSaturation();
205 }
206
207 void FuseTest::expect_release(uint64_t ino, int times, uint64_t lock_owner,
208         int error)
209 {
210         EXPECT_CALL(*m_mock, process(
211                 ResultOf([=](auto in) {
212                         return (in->header.opcode == FUSE_RELEASE &&
213                                 in->header.nodeid == ino &&
214                                 in->body.release.lock_owner == lock_owner &&
215                                 in->body.release.fh == FH);
216                 }, Eq(true)),
217                 _)
218         ).Times(times)
219         .WillRepeatedly(Invoke(ReturnErrno(error)));
220 }
221 void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
222         uint64_t osize, uint32_t flags, const void *contents)
223 {
224         EXPECT_CALL(*m_mock, process(
225                 ResultOf([=](auto in) {
226                         const char *buf = (const char*)in->body.bytes +
227                                 sizeof(struct fuse_write_in);
228                         bool pid_ok;
229
230                         if (in->body.write.write_flags & FUSE_WRITE_CACHE)
231                                 pid_ok = true;
232                         else
233                                 pid_ok = (pid_t)in->header.pid == getpid();
234
235                         return (in->header.opcode == FUSE_WRITE &&
236                                 in->header.nodeid == ino &&
237                                 in->body.write.fh == FH &&
238                                 in->body.write.offset == offset  &&
239                                 in->body.write.size == isize &&
240                                 pid_ok &&
241                                 in->body.write.write_flags == flags &&
242                                 0 == bcmp(buf, contents, isize));
243                 }, Eq(true)),
244                 _)
245         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
246                 SET_OUT_HEADER_LEN(out, write);
247                 out->body.write.size = osize;
248         })));
249 }
250
251 static void usage(char* progname) {
252         fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname);
253         exit(2);
254 }
255
256 int main(int argc, char **argv) {
257         int ch;
258         FuseEnv *fuse_env = new FuseEnv;
259
260         InitGoogleTest(&argc, argv);
261         AddGlobalTestEnvironment(fuse_env);
262
263         while ((ch = getopt(argc, argv, "v")) != -1) {
264                 switch (ch) {
265                         case 'v':
266                                 verbosity++;
267                                 break;
268                         default:
269                                 usage(argv[0]);
270                                 break;
271                 }
272         }
273
274         return (RUN_ALL_TESTS());
275 }