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