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