2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/param.h>
32 #include <sys/module.h>
33 #include <sys/sysctl.h>
35 #include <gtest/gtest.h>
41 using namespace testing;
43 /* Check that fusefs(4) is accessible and the current user can mount(2) */
44 void check_environment()
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";
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";
62 GTEST_SKIP() << strerror(errno);
65 sysctlbyname(usermount_node, &usermount_val, &usermount_size,
67 if (geteuid() != 0 && !usermount_val)
68 GTEST_SKIP() << "current user is not allowed to mount";
71 class FuseEnv: public Environment {
72 virtual void SetUp() {
76 void FuseTest::SetUp() {
77 const char *node = "vfs.maxbcachebuf";
79 size_t size = sizeof(val);
82 * XXX check_environment should be called from FuseEnv::SetUp, but
83 * can't due to https://github.com/google/googletest/issues/2189
89 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
94 m_mock = new MockFS(m_maxreadahead, m_push_symlinks_in,
95 m_default_permissions, m_init_flags);
96 } catch (std::system_error err) {
101 void FuseTest::expect_getattr(uint64_t ino, uint64_t size)
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);
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;
119 void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
120 uint64_t size, int times)
122 EXPECT_LOOKUP(1, relpath)
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;
134 void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
136 EXPECT_CALL(*m_mock, process(
137 ResultOf([=](auto in) {
138 return (in->header.opcode == FUSE_OPEN &&
139 in->header.nodeid == ino);
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;
151 void FuseTest::expect_opendir(uint64_t ino)
153 EXPECT_CALL(*m_mock, process(
154 ResultOf([](auto in) {
155 return (in->header.opcode == FUSE_STATFS);
158 ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
159 SET_OUT_HEADER_LEN(out, statfs);
162 EXPECT_CALL(*m_mock, process(
163 ResultOf([=](auto in) {
164 return (in->header.opcode == FUSE_OPENDIR &&
165 in->header.nodeid == ino);
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;
175 void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
176 uint64_t osize, const void *contents)
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);
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();
193 void FuseTest::expect_release(uint64_t ino, int times, uint64_t lock_owner,
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);
205 .WillRepeatedly(Invoke(ReturnErrno(error)));
207 void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
208 uint64_t osize, uint32_t flags, const void *contents)
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);
216 if (in->body.write.write_flags & FUSE_WRITE_CACHE)
219 pid_ok = (pid_t)in->header.pid == getpid();
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 &&
227 in->body.write.write_flags == flags &&
228 0 == bcmp(buf, contents, isize));
231 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
232 SET_OUT_HEADER_LEN(out, write);
233 out->body.write.size = osize;
237 static void usage(char* progname) {
238 fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname);
242 int main(int argc, char **argv) {
244 FuseEnv *fuse_env = new FuseEnv;
246 InitGoogleTest(&argc, argv);
247 AddGlobalTestEnvironment(fuse_env);
249 while ((ch = getopt(argc, argv, "v")) != -1) {
260 return (RUN_ALL_TESTS());