]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/utils.cc
fusefs: fix an alignment issue in the tests on arm
[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 extern "C" {
32 #include <sys/param.h>
33 #include <sys/mman.h>
34 #include <sys/module.h>
35 #include <sys/sysctl.h>
36 #include <sys/wait.h>
37
38 #include <dirent.h>
39 #include <grp.h>
40 #include <pwd.h>
41 #include <semaphore.h>
42 #include <unistd.h>
43 }
44
45 #include <gtest/gtest.h>
46
47 #include "mockfs.hh"
48 #include "utils.hh"
49
50 using namespace testing;
51
52 /* Check that fusefs(4) is accessible and the current user can mount(2) */
53 void check_environment()
54 {
55         const char *devnode = "/dev/fuse";
56         const char *usermount_node = "vfs.usermount";
57         int usermount_val = 0;
58         size_t usermount_size = sizeof(usermount_val);
59         if (eaccess(devnode, R_OK | W_OK)) {
60                 if (errno == ENOENT) {
61                         GTEST_SKIP() << devnode << " does not exist";
62                 } else if (errno == EACCES) {
63                         GTEST_SKIP() << devnode <<
64                             " is not accessible by the current user";
65                 } else {
66                         GTEST_SKIP() << strerror(errno);
67                 }
68         }
69         sysctlbyname(usermount_node, &usermount_val, &usermount_size,
70                      NULL, 0);
71         if (geteuid() != 0 && !usermount_val)
72                 GTEST_SKIP() << "current user is not allowed to mount";
73 }
74
75 class FuseEnv: public Environment {
76         virtual void SetUp() {
77         }
78 };
79
80 void FuseTest::SetUp() {
81         const char *node = "vfs.maxbcachebuf";
82         int val = 0;
83         size_t size = sizeof(val);
84
85         /*
86          * XXX check_environment should be called from FuseEnv::SetUp, but
87          * can't due to https://github.com/google/googletest/issues/2189
88          */
89         check_environment();
90         if (IsSkipped())
91                 return;
92
93         ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
94                 << strerror(errno);
95         m_maxbcachebuf = val;
96
97         try {
98                 m_mock = new MockFS(m_maxreadahead, m_allow_other,
99                         m_default_permissions, m_push_symlinks_in, m_ro,
100                         m_pm, m_init_flags, m_kernel_minor_version);
101                 /* 
102                  * FUSE_ACCESS is called almost universally.  Expecting it in
103                  * each test case would be super-annoying.  Instead, set a
104                  * default expectation for FUSE_ACCESS and return ENOSYS.
105                  *
106                  * Individual test cases can override this expectation since
107                  * googlemock evaluates expectations in LIFO order.
108                  */
109                 EXPECT_CALL(*m_mock, process(
110                         ResultOf([=](auto in) {
111                                 return (in.header.opcode == FUSE_ACCESS);
112                         }, Eq(true)),
113                         _)
114                 ).Times(AnyNumber())
115                 .WillRepeatedly(Invoke(ReturnErrno(ENOSYS)));
116         } catch (std::system_error err) {
117                 FAIL() << err.what();
118         }
119 }
120
121 void
122 FuseTest::expect_access(uint64_t ino, mode_t access_mode, int error)
123 {
124         EXPECT_CALL(*m_mock, process(
125                 ResultOf([=](auto in) {
126                         return (in.header.opcode == FUSE_ACCESS &&
127                                 in.header.nodeid == ino &&
128                                 in.body.access.mask == access_mode);
129                 }, Eq(true)),
130                 _)
131         ).WillOnce(Invoke(ReturnErrno(error)));
132 }
133
134 void
135 FuseTest::expect_destroy(int error)
136 {
137         EXPECT_CALL(*m_mock, process(
138                 ResultOf([=](auto in) {
139                         return (in.header.opcode == FUSE_DESTROY);
140                 }, Eq(true)),
141                 _)
142         ).WillOnce(Invoke( ReturnImmediate([&](auto in, auto& out) {
143                 m_mock->m_quit = true;
144                 out.header.len = sizeof(out.header);
145                 out.header.unique = in.header.unique;
146                 out.header.error = -error;
147         })));
148 }
149
150 void
151 FuseTest::expect_flush(uint64_t ino, int times, ProcessMockerT r)
152 {
153         EXPECT_CALL(*m_mock, process(
154                 ResultOf([=](auto in) {
155                         return (in.header.opcode == FUSE_FLUSH &&
156                                 in.header.nodeid == ino);
157                 }, Eq(true)),
158                 _)
159         ).Times(times)
160         .WillRepeatedly(Invoke(r));
161 }
162
163 void
164 FuseTest::expect_forget(uint64_t ino, uint64_t nlookup)
165 {
166         EXPECT_CALL(*m_mock, process(
167                 ResultOf([=](auto in) {
168                         return (in.header.opcode == FUSE_FORGET &&
169                                 in.header.nodeid == ino &&
170                                 in.body.forget.nlookup == nlookup);
171                 }, Eq(true)),
172                 _)
173         ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
174                 /* FUSE_FORGET has no response! */
175         }));
176 }
177
178 void FuseTest::expect_getattr(uint64_t ino, uint64_t size)
179 {
180         EXPECT_CALL(*m_mock, process(
181                 ResultOf([=](auto in) {
182                         return (in.header.opcode == FUSE_GETATTR &&
183                                 in.header.nodeid == ino);
184                 }, Eq(true)),
185                 _)
186         ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
187                 SET_OUT_HEADER_LEN(out, attr);
188                 out.body.attr.attr.ino = ino;   // Must match nodeid
189                 out.body.attr.attr.mode = S_IFREG | 0644;
190                 out.body.attr.attr.size = size;
191                 out.body.attr.attr_valid = UINT64_MAX;
192         })));
193 }
194
195 void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
196         uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
197 {
198         EXPECT_LOOKUP(1, relpath)
199         .Times(times)
200         .WillRepeatedly(Invoke(
201                 ReturnImmediate([=](auto in __unused, auto& out) {
202                 SET_OUT_HEADER_LEN(out, entry);
203                 out.body.entry.attr.mode = mode;
204                 out.body.entry.nodeid = ino;
205                 out.body.entry.attr.nlink = 1;
206                 out.body.entry.attr_valid = attr_valid;
207                 out.body.entry.attr.size = size;
208                 out.body.entry.attr.uid = uid;
209                 out.body.entry.attr.gid = gid;
210         })));
211 }
212
213 void FuseTest::expect_lookup_7_8(const char *relpath, uint64_t ino, mode_t mode,
214         uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid)
215 {
216         EXPECT_LOOKUP(1, relpath)
217         .Times(times)
218         .WillRepeatedly(Invoke(
219                 ReturnImmediate([=](auto in __unused, auto& out) {
220                 SET_OUT_HEADER_LEN(out, entry_7_8);
221                 out.body.entry.attr.mode = mode;
222                 out.body.entry.nodeid = ino;
223                 out.body.entry.attr.nlink = 1;
224                 out.body.entry.attr_valid = attr_valid;
225                 out.body.entry.attr.size = size;
226                 out.body.entry.attr.uid = uid;
227                 out.body.entry.attr.gid = gid;
228         })));
229 }
230
231 void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
232 {
233         EXPECT_CALL(*m_mock, process(
234                 ResultOf([=](auto in) {
235                         return (in.header.opcode == FUSE_OPEN &&
236                                 in.header.nodeid == ino);
237                 }, Eq(true)),
238                 _)
239         ).Times(times)
240         .WillRepeatedly(Invoke(
241                 ReturnImmediate([=](auto in __unused, auto& out) {
242                 out.header.len = sizeof(out.header);
243                 SET_OUT_HEADER_LEN(out, open);
244                 out.body.open.fh = FH;
245                 out.body.open.open_flags = flags;
246         })));
247 }
248
249 void FuseTest::expect_opendir(uint64_t ino)
250 {
251         /* opendir(3) calls fstatfs */
252         EXPECT_CALL(*m_mock, process(
253                 ResultOf([](auto in) {
254                         return (in.header.opcode == FUSE_STATFS);
255                 }, Eq(true)),
256                 _)
257         ).WillRepeatedly(Invoke(
258         ReturnImmediate([=](auto i __unused, auto& out) {
259                 SET_OUT_HEADER_LEN(out, statfs);
260         })));
261
262         EXPECT_CALL(*m_mock, process(
263                 ResultOf([=](auto in) {
264                         return (in.header.opcode == FUSE_OPENDIR &&
265                                 in.header.nodeid == ino);
266                 }, Eq(true)),
267                 _)
268         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
269                 out.header.len = sizeof(out.header);
270                 SET_OUT_HEADER_LEN(out, open);
271                 out.body.open.fh = FH;
272         })));
273 }
274
275 void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
276         uint64_t osize, const void *contents)
277 {
278         EXPECT_CALL(*m_mock, process(
279                 ResultOf([=](auto in) {
280                         return (in.header.opcode == FUSE_READ &&
281                                 in.header.nodeid == ino &&
282                                 in.body.read.fh == FH &&
283                                 in.body.read.offset == offset &&
284                                 in.body.read.size == isize);
285                 }, Eq(true)),
286                 _)
287         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
288                 out.header.len = sizeof(struct fuse_out_header) + osize;
289                 memmove(out.body.bytes, contents, osize);
290         }))).RetiresOnSaturation();
291 }
292
293 void FuseTest::expect_readdir(uint64_t ino, uint64_t off,
294         std::vector<struct dirent> &ents)
295 {
296         EXPECT_CALL(*m_mock, process(
297                 ResultOf([=](auto in) {
298                         return (in.header.opcode == FUSE_READDIR &&
299                                 in.header.nodeid == ino &&
300                                 in.body.readdir.fh == FH &&
301                                 in.body.readdir.offset == off);
302                 }, Eq(true)),
303                 _)
304         ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) {
305                 struct fuse_dirent *fde = (struct fuse_dirent*)&(out.body);
306                 int i = 0;
307
308                 out.header.error = 0;
309                 out.header.len = 0;
310
311                 for (const auto& it: ents) {
312                         size_t entlen, entsize;
313
314                         fde->ino = it.d_fileno;
315                         fde->off = it.d_off;
316                         fde->type = it.d_type;
317                         fde->namelen = it.d_namlen;
318                         strncpy(fde->name, it.d_name, it.d_namlen);
319                         entlen = FUSE_NAME_OFFSET + fde->namelen;
320                         entsize = FUSE_DIRENT_SIZE(fde);
321                         /* 
322                          * The FUSE protocol does not require zeroing out the
323                          * unused portion of the name.  But it's a good
324                          * practice to prevent information disclosure to the
325                          * FUSE client, even though the client is usually the
326                          * kernel
327                          */
328                         memset(fde->name + fde->namelen, 0, entsize - entlen);
329                         if (out.header.len + entsize > in.body.read.size) {
330                                 printf("Overflow in readdir expectation: i=%d\n"
331                                         , i);
332                                 break;
333                         }
334                         out.header.len += entsize;
335                         fde = (struct fuse_dirent*)
336                                 ((intmax_t*)fde + entsize / sizeof(intmax_t));
337                         i++;
338                 }
339                 out.header.len += sizeof(out.header);
340         })));
341
342 }
343 void FuseTest::expect_release(uint64_t ino, uint64_t fh)
344 {
345         EXPECT_CALL(*m_mock, process(
346                 ResultOf([=](auto in) {
347                         return (in.header.opcode == FUSE_RELEASE &&
348                                 in.header.nodeid == ino &&
349                                 in.body.release.fh == fh);
350                 }, Eq(true)),
351                 _)
352         ).WillOnce(Invoke(ReturnErrno(0)));
353 }
354
355 void FuseTest::expect_releasedir(uint64_t ino, ProcessMockerT r)
356 {
357         EXPECT_CALL(*m_mock, process(
358                 ResultOf([=](auto in) {
359                         return (in.header.opcode == FUSE_RELEASEDIR &&
360                                 in.header.nodeid == ino &&
361                                 in.body.release.fh == FH);
362                 }, Eq(true)),
363                 _)
364         ).WillOnce(Invoke(r));
365 }
366
367 void FuseTest::expect_unlink(uint64_t parent, const char *path, int error)
368 {
369         EXPECT_CALL(*m_mock, process(
370                 ResultOf([=](auto in) {
371                         return (in.header.opcode == FUSE_UNLINK &&
372                                 0 == strcmp(path, in.body.unlink) &&
373                                 in.header.nodeid == parent);
374                 }, Eq(true)),
375                 _)
376         ).WillOnce(Invoke(ReturnErrno(error)));
377 }
378
379 void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
380         uint64_t osize, uint32_t flags_set, uint32_t flags_unset,
381         const void *contents)
382 {
383         EXPECT_CALL(*m_mock, process(
384                 ResultOf([=](auto in) {
385                         const char *buf = (const char*)in.body.bytes +
386                                 sizeof(struct fuse_write_in);
387                         bool pid_ok;
388                         uint32_t wf = in.body.write.write_flags;
389
390                         if (wf & FUSE_WRITE_CACHE)
391                                 pid_ok = true;
392                         else
393                                 pid_ok = (pid_t)in.header.pid == getpid();
394
395                         return (in.header.opcode == FUSE_WRITE &&
396                                 in.header.nodeid == ino &&
397                                 in.body.write.fh == FH &&
398                                 in.body.write.offset == offset  &&
399                                 in.body.write.size == isize &&
400                                 pid_ok &&
401                                 (wf & flags_set) == flags_set &&
402                                 (wf & flags_unset) == 0 &&
403                                 0 == bcmp(buf, contents, isize));
404                 }, Eq(true)),
405                 _)
406         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
407                 SET_OUT_HEADER_LEN(out, write);
408                 out.body.write.size = osize;
409         })));
410 }
411
412 void FuseTest::expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize,
413         uint64_t osize, const void *contents)
414 {
415         EXPECT_CALL(*m_mock, process(
416                 ResultOf([=](auto in) {
417                         const char *buf = (const char*)in.body.bytes +
418                                 FUSE_COMPAT_WRITE_IN_SIZE;
419                         bool pid_ok = (pid_t)in.header.pid == getpid();
420                         return (in.header.opcode == FUSE_WRITE &&
421                                 in.header.nodeid == ino &&
422                                 in.body.write.fh == FH &&
423                                 in.body.write.offset == offset  &&
424                                 in.body.write.size == isize &&
425                                 pid_ok &&
426                                 0 == bcmp(buf, contents, isize));
427                 }, Eq(true)),
428                 _)
429         ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
430                 SET_OUT_HEADER_LEN(out, write);
431                 out.body.write.size = osize;
432         })));
433 }
434
435 void
436 get_unprivileged_id(uid_t *uid, gid_t *gid)
437 {
438         struct passwd *pw;
439         struct group *gr;
440
441         /* 
442          * First try "tests", Kyua's default unprivileged user.  XXX after
443          * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API
444          */
445         pw = getpwnam("tests");
446         if (pw == NULL) {
447                 /* Fall back to "nobody" */
448                 pw = getpwnam("nobody");
449         }
450         if (pw == NULL)
451                 GTEST_SKIP() << "Test requires an unprivileged user";
452         /* Use group "nobody", which is Kyua's default unprivileged group */
453         gr = getgrnam("nobody");
454         if (gr == NULL)
455                 GTEST_SKIP() << "Test requires an unprivileged group";
456         *uid = pw->pw_uid;
457         *gid = gr->gr_gid;
458 }
459
460 void
461 FuseTest::fork(bool drop_privs, int *child_status,
462         std::function<void()> parent_func,
463         std::function<int()> child_func)
464 {
465         sem_t *sem;
466         int mprot = PROT_READ | PROT_WRITE;
467         int mflags = MAP_ANON | MAP_SHARED;
468         pid_t child;
469         uid_t uid;
470         gid_t gid;
471         
472         if (drop_privs) {
473                 get_unprivileged_id(&uid, &gid);
474                 if (IsSkipped())
475                         return;
476         }
477
478         sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0);
479         ASSERT_NE(MAP_FAILED, sem) << strerror(errno);
480         ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno);
481
482         if ((child = ::fork()) == 0) {
483                 /* In child */
484                 int err = 0;
485
486                 if (sem_wait(sem)) {
487                         perror("sem_wait");
488                         err = 1;
489                         goto out;
490                 }
491
492                 if (drop_privs && 0 != setegid(gid)) {
493                         perror("setegid");
494                         err = 1;
495                         goto out;
496                 }
497                 if (drop_privs && 0 != setreuid(-1, uid)) {
498                         perror("setreuid");
499                         err = 1;
500                         goto out;
501                 }
502                 err = child_func();
503
504 out:
505                 sem_destroy(sem);
506                 _exit(err);
507         } else if (child > 0) {
508                 /* 
509                  * In parent.  Cleanup must happen here, because it's still
510                  * privileged.
511                  */
512                 m_mock->m_child_pid = child;
513                 ASSERT_NO_FATAL_FAILURE(parent_func());
514
515                 /* Signal the child process to go */
516                 ASSERT_EQ(0, sem_post(sem)) << strerror(errno);
517
518                 ASSERT_LE(0, wait(child_status)) << strerror(errno);
519         } else {
520                 FAIL() << strerror(errno);
521         }
522         munmap(sem, sizeof(*sem));
523         return;
524 }
525
526 static void usage(char* progname) {
527         fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname);
528         exit(2);
529 }
530
531 int main(int argc, char **argv) {
532         int ch;
533         FuseEnv *fuse_env = new FuseEnv;
534
535         InitGoogleTest(&argc, argv);
536         AddGlobalTestEnvironment(fuse_env);
537
538         while ((ch = getopt(argc, argv, "v")) != -1) {
539                 switch (ch) {
540                         case 'v':
541                                 verbosity++;
542                                 break;
543                         default:
544                                 usage(argv[0]);
545                                 break;
546                 }
547         }
548
549         return (RUN_ALL_TESTS());
550 }