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