]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/mockfs.cc
fusefs: Report the number of available ops in kevent(2)
[FreeBSD/FreeBSD.git] / tests / sys / fs / fusefs / mockfs.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
34 #include <sys/mount.h>
35 #include <sys/select.h>
36 #include <sys/stat.h>
37 #include <sys/uio.h>
38 #include <sys/user.h>
39
40 #include <fcntl.h>
41 #include <libutil.h>
42 #include <poll.h>
43 #include <pthread.h>
44 #include <signal.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47
48 #include "mntopts.h"    // for build_iovec
49 }
50
51 #include <gtest/gtest.h>
52
53 #include "mockfs.hh"
54
55 using namespace testing;
56
57 int verbosity = 0;
58
59 const char* opcode2opname(uint32_t opcode)
60 {
61         const int NUM_OPS = 39;
62         const char* table[NUM_OPS] = {
63                 "Unknown (opcode 0)",
64                 "LOOKUP",
65                 "FORGET",
66                 "GETATTR",
67                 "SETATTR",
68                 "READLINK",
69                 "SYMLINK",
70                 "Unknown (opcode 7)",
71                 "MKNOD",
72                 "MKDIR",
73                 "UNLINK",
74                 "RMDIR",
75                 "RENAME",
76                 "LINK",
77                 "OPEN",
78                 "READ",
79                 "WRITE",
80                 "STATFS",
81                 "RELEASE",
82                 "Unknown (opcode 19)",
83                 "FSYNC",
84                 "SETXATTR",
85                 "GETXATTR",
86                 "LISTXATTR",
87                 "REMOVEXATTR",
88                 "FLUSH",
89                 "INIT",
90                 "OPENDIR",
91                 "READDIR",
92                 "RELEASEDIR",
93                 "FSYNCDIR",
94                 "GETLK",
95                 "SETLK",
96                 "SETLKW",
97                 "ACCESS",
98                 "CREATE",
99                 "INTERRUPT",
100                 "BMAP",
101                 "DESTROY"
102         };
103         if (opcode >= NUM_OPS)
104                 return ("Unknown (opcode > max)");
105         else
106                 return (table[opcode]);
107 }
108
109 ProcessMockerT
110 ReturnErrno(int error)
111 {
112         return([=](auto in, auto &out) {
113                 auto out0 = new mockfs_buf_out;
114                 out0->header.unique = in->header.unique;
115                 out0->header.error = -error;
116                 out0->header.len = sizeof(out0->header);
117                 out.push_back(out0);
118         });
119 }
120
121 /* Helper function used for returning negative cache entries for LOOKUP */
122 ProcessMockerT
123 ReturnNegativeCache(const struct timespec *entry_valid)
124 {
125         return([=](auto in, auto &out) {
126                 /* nodeid means ENOENT and cache it */
127                 auto out0 = new mockfs_buf_out;
128                 out0->body.entry.nodeid = 0;
129                 out0->header.unique = in->header.unique;
130                 out0->header.error = 0;
131                 out0->body.entry.entry_valid = entry_valid->tv_sec;
132                 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
133                 SET_OUT_HEADER_LEN(out0, entry);
134                 out.push_back(out0);
135         });
136 }
137
138 ProcessMockerT
139 ReturnImmediate(std::function<void(const struct mockfs_buf_in *in,
140                                    struct mockfs_buf_out *out)> f)
141 {
142         return([=](auto in, auto &out) {
143                 auto out0 = new mockfs_buf_out;
144                 out0->header.unique = in->header.unique;
145                 f(in, out0);
146                 out.push_back(out0);
147         });
148 }
149
150 void sigint_handler(int __unused sig) {
151         // Don't do anything except interrupt the daemon's read(2) call
152 }
153
154 void debug_fuseop(const mockfs_buf_in *in)
155 {
156         printf("%-11s ino=%2lu", opcode2opname(in->header.opcode),
157                 in->header.nodeid);
158         if (verbosity > 1) {
159                 printf(" uid=%5u gid=%5u pid=%5u unique=%lu len=%u",
160                         in->header.uid, in->header.gid, in->header.pid,
161                         in->header.unique, in->header.len);
162         }
163         switch (in->header.opcode) {
164                 const char *name, *value;
165
166                 case FUSE_ACCESS:
167                         printf(" mask=%#x", in->body.access.mask);
168                         break;
169                 case FUSE_CREATE:
170                         name = (const char*)in->body.bytes +
171                                 sizeof(fuse_open_in);
172                         printf(" flags=%#x name=%s",
173                                 in->body.open.flags, name);
174                         break;
175                 case FUSE_FLUSH:
176                         printf(" fh=%#lx lock_owner=%lu", in->body.flush.fh,
177                                 in->body.flush.lock_owner);
178                         break;
179                 case FUSE_FORGET:
180                         printf(" nlookup=%lu", in->body.forget.nlookup);
181                         break;
182                 case FUSE_FSYNC:
183                         printf(" flags=%#x", in->body.fsync.fsync_flags);
184                         break;
185                 case FUSE_FSYNCDIR:
186                         printf(" flags=%#x", in->body.fsyncdir.fsync_flags);
187                         break;
188                 case FUSE_INTERRUPT:
189                         printf(" unique=%lu", in->body.interrupt.unique);
190                         break;
191                 case FUSE_LINK:
192                         printf(" oldnodeid=%lu", in->body.link.oldnodeid);
193                         break;
194                 case FUSE_LOOKUP:
195                         printf(" %s", in->body.lookup);
196                         break;
197                 case FUSE_MKDIR:
198                         name = (const char*)in->body.bytes +
199                                 sizeof(fuse_mkdir_in);
200                         printf(" name=%s mode=%#o", name, in->body.mkdir.mode);
201                         break;
202                 case FUSE_MKNOD:
203                         printf(" mode=%#o rdev=%x", in->body.mknod.mode,
204                                 in->body.mknod.rdev);
205                         break;
206                 case FUSE_OPEN:
207                         printf(" flags=%#x mode=%#o",
208                                 in->body.open.flags, in->body.open.mode);
209                         break;
210                 case FUSE_OPENDIR:
211                         printf(" flags=%#x mode=%#o",
212                                 in->body.opendir.flags, in->body.opendir.mode);
213                         break;
214                 case FUSE_READ:
215                         printf(" offset=%lu size=%u", in->body.read.offset,
216                                 in->body.read.size);
217                         break;
218                 case FUSE_READDIR:
219                         printf(" fh=%#lx offset=%lu size=%u",
220                                 in->body.readdir.fh, in->body.readdir.offset,
221                                 in->body.readdir.size);
222                         break;
223                 case FUSE_RELEASE:
224                         printf(" fh=%#lx flags=%#x lock_owner=%lu",
225                                 in->body.release.fh,
226                                 in->body.release.flags,
227                                 in->body.release.lock_owner);
228                         break;
229                 case FUSE_SETATTR:
230                         if (verbosity <= 1) {
231                                 printf(" valid=%#x", in->body.setattr.valid);
232                                 break;
233                         }
234                         if (in->body.setattr.valid & FATTR_MODE)
235                                 printf(" mode=%#o", in->body.setattr.mode);
236                         if (in->body.setattr.valid & FATTR_UID)
237                                 printf(" uid=%u", in->body.setattr.uid);
238                         if (in->body.setattr.valid & FATTR_GID)
239                                 printf(" gid=%u", in->body.setattr.gid);
240                         if (in->body.setattr.valid & FATTR_SIZE)
241                                 printf(" size=%zu", in->body.setattr.size);
242                         if (in->body.setattr.valid & FATTR_ATIME)
243                                 printf(" atime=%zu.%u",
244                                         in->body.setattr.atime,
245                                         in->body.setattr.atimensec);
246                         if (in->body.setattr.valid & FATTR_MTIME)
247                                 printf(" mtime=%zu.%u",
248                                         in->body.setattr.mtime,
249                                         in->body.setattr.mtimensec);
250                         if (in->body.setattr.valid & FATTR_FH)
251                                 printf(" fh=%zu", in->body.setattr.fh);
252                         break;
253                 case FUSE_SETLK:
254                         printf(" fh=%#lx owner=%lu type=%u pid=%u",
255                                 in->body.setlk.fh, in->body.setlk.owner,
256                                 in->body.setlk.lk.type,
257                                 in->body.setlk.lk.pid);
258                         if (verbosity >= 2) {
259                                 printf(" range=[%lu-%lu]",
260                                         in->body.setlk.lk.start,
261                                         in->body.setlk.lk.end);
262                         }
263                         break;
264                 case FUSE_SETXATTR:
265                         /* 
266                          * In theory neither the xattr name and value need be
267                          * ASCII, but in this test suite they always are.
268                          */
269                         name = (const char*)in->body.bytes +
270                                 sizeof(fuse_setxattr_in);
271                         value = name + strlen(name) + 1;
272                         printf(" %s=%s", name, value);
273                         break;
274                 case FUSE_WRITE:
275                         printf(" fh=%#lx offset=%lu size=%u flags=%u",
276                                 in->body.write.fh,
277                                 in->body.write.offset, in->body.write.size,
278                                 in->body.write.write_flags);
279                         break;
280                 default:
281                         break;
282         }
283         printf("\n");
284 }
285
286 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
287         bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags)
288 {
289         struct sigaction sa;
290         struct iovec *iov = NULL;
291         int iovlen = 0;
292         char fdstr[15];
293         const bool trueval = true;
294
295         m_daemon_id = NULL;
296         m_maxreadahead = max_readahead;
297         m_nready = -1;
298         m_pm = pm;
299         m_quit = false;
300         if (m_pm == KQ)
301                 m_kq = kqueue();
302         else
303                 m_kq = -1;
304
305         /*
306          * Kyua sets pwd to a testcase-unique tempdir; no need to use
307          * mkdtemp
308          */
309         /*
310          * googletest doesn't allow ASSERT_ in constructors, so we must throw
311          * instead.
312          */
313         if (mkdir("mountpoint" , 0755) && errno != EEXIST)
314                 throw(std::system_error(errno, std::system_category(),
315                         "Couldn't make mountpoint directory"));
316
317         switch (m_pm) {
318         case BLOCKING:
319                 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
320                 break;
321         default:
322                 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
323                 break;
324         }
325         if (m_fuse_fd < 0)
326                 throw(std::system_error(errno, std::system_category(),
327                         "Couldn't open /dev/fuse"));
328
329         m_pid = getpid();
330         m_child_pid = -1;
331
332         build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
333         build_iovec(&iov, &iovlen, "fspath",
334                     __DECONST(void *, "mountpoint"), -1);
335         build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
336         sprintf(fdstr, "%d", m_fuse_fd);
337         build_iovec(&iov, &iovlen, "fd", fdstr, -1);
338         if (allow_other) {
339                 build_iovec(&iov, &iovlen, "allow_other",
340                         __DECONST(void*, &trueval), sizeof(bool));
341         }
342         if (default_permissions) {
343                 build_iovec(&iov, &iovlen, "default_permissions",
344                         __DECONST(void*, &trueval), sizeof(bool));
345         }
346         if (push_symlinks_in) {
347                 build_iovec(&iov, &iovlen, "push_symlinks_in",
348                         __DECONST(void*, &trueval), sizeof(bool));
349         }
350         if (ro) {
351                 build_iovec(&iov, &iovlen, "ro",
352                         __DECONST(void*, &trueval), sizeof(bool));
353         }
354         if (nmount(iov, iovlen, 0))
355                 throw(std::system_error(errno, std::system_category(),
356                         "Couldn't mount filesystem"));
357
358         // Setup default handler
359         ON_CALL(*this, process(_, _))
360                 .WillByDefault(Invoke(this, &MockFS::process_default));
361
362         init(flags);
363         bzero(&sa, sizeof(sa));
364         sa.sa_handler = sigint_handler;
365         sa.sa_flags = 0;        /* Don't set SA_RESTART! */
366         if (0 != sigaction(SIGUSR1, &sa, NULL))
367                 throw(std::system_error(errno, std::system_category(),
368                         "Couldn't handle SIGUSR1"));
369         if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
370                 throw(std::system_error(errno, std::system_category(),
371                         "Couldn't Couldn't start fuse thread"));
372 }
373
374 MockFS::~MockFS() {
375         kill_daemon();
376         if (m_daemon_id != NULL) {
377                 pthread_join(m_daemon_id, NULL);
378                 m_daemon_id = NULL;
379         }
380         ::unmount("mountpoint", MNT_FORCE);
381         rmdir("mountpoint");
382         if (m_kq >= 0)
383                 close(m_kq);
384 }
385
386 void MockFS::init(uint32_t flags) {
387         mockfs_buf_in *in;
388         mockfs_buf_out *out;
389
390         in = (mockfs_buf_in*) malloc(sizeof(*in));
391         ASSERT_TRUE(in != NULL);
392         out = (mockfs_buf_out*) malloc(sizeof(*out));
393         ASSERT_TRUE(out != NULL);
394
395         read_request(in);
396         ASSERT_EQ(FUSE_INIT, in->header.opcode);
397
398         memset(out, 0, sizeof(*out));
399         out->header.unique = in->header.unique;
400         out->header.error = 0;
401         out->body.init.major = FUSE_KERNEL_VERSION;
402         out->body.init.minor = FUSE_KERNEL_MINOR_VERSION;
403         out->body.init.flags = in->body.init.flags & flags;
404
405         /*
406          * The default max_write is set to this formula in libfuse, though
407          * individual filesystems can lower it.  The "- 4096" was added in
408          * commit 154ffe2, with the commit message "fix".
409          */
410         uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096;
411         /* For testing purposes, it should be distinct from MAXPHYS */
412         m_max_write = MIN(default_max_write, MAXPHYS / 2);
413         out->body.init.max_write = m_max_write;
414
415         out->body.init.max_readahead = m_maxreadahead;
416         SET_OUT_HEADER_LEN(out, init);
417         write(m_fuse_fd, out, out->header.len);
418
419         free(in);
420 }
421
422 void MockFS::kill_daemon() {
423         m_quit = true;
424         if (m_daemon_id != NULL)
425                 pthread_kill(m_daemon_id, SIGUSR1);
426         // Closing the /dev/fuse file descriptor first allows unmount to
427         // succeed even if the daemon doesn't correctly respond to commands
428         // during the unmount sequence.
429         close(m_fuse_fd);
430         m_fuse_fd = -1;
431 }
432
433 void MockFS::loop() {
434         mockfs_buf_in *in;
435         std::vector<mockfs_buf_out*> out;
436
437         in = (mockfs_buf_in*) malloc(sizeof(*in));
438         ASSERT_TRUE(in != NULL);
439         while (!m_quit) {
440                 bzero(in, sizeof(*in));
441                 read_request(in);
442                 if (m_quit)
443                         break;
444                 if (verbosity > 0)
445                         debug_fuseop(in);
446                 if (pid_ok((pid_t)in->header.pid)) {
447                         process(in, out);
448                 } else {
449                         /* 
450                          * Reject any requests from unknown processes.  Because
451                          * we actually do mount a filesystem, plenty of
452                          * unrelated system daemons may try to access it.
453                          */
454                         if (verbosity > 1)
455                                 printf("\tREJECTED (wrong pid %d)\n",
456                                         in->header.pid);
457                         process_default(in, out);
458                 }
459                 for (auto &it: out) {
460                         write_response(it);
461                         delete it;
462                 }
463                 out.clear();
464         }
465         free(in);
466 }
467
468 bool MockFS::pid_ok(pid_t pid) {
469         if (pid == m_pid) {
470                 return (true);
471         } else if (pid == m_child_pid) {
472                 return (true);
473         } else {
474                 struct kinfo_proc *ki;
475                 bool ok = false;
476
477                 ki = kinfo_getproc(pid);
478                 if (ki == NULL)
479                         return (false);
480                 /* 
481                  * Allow access by the aio daemon processes so that our tests
482                  * can use aio functions
483                  */
484                 if (0 == strncmp("aiod", ki->ki_comm, 4))
485                         ok = true;
486                 free(ki);
487                 return (ok);
488         }
489 }
490
491 void MockFS::process_default(const mockfs_buf_in *in,
492                 std::vector<mockfs_buf_out*> &out)
493 {
494         auto out0 = new mockfs_buf_out;
495         out0->header.unique = in->header.unique;
496         out0->header.error = -EOPNOTSUPP;
497         out0->header.len = sizeof(out0->header);
498         out.push_back(out0);
499 }
500
501 void MockFS::read_request(mockfs_buf_in *in) {
502         ssize_t res;
503         int nready;
504         fd_set readfds;
505         pollfd fds[1];
506         struct kevent changes[1];
507         struct kevent events[1];
508         int nfds;
509
510         switch (m_pm) {
511         case BLOCKING:
512                 break;
513         case KQ:
514                 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0, 0, 0);
515                 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, NULL);
516                 if (m_quit)
517                         return;
518                 ASSERT_LE(0, nready) << strerror(errno);
519                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
520                 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
521                 if (events[0].flags & EV_ERROR)
522                         FAIL() << strerror(events[0].data);
523                 else if (events[0].flags & EV_EOF)
524                         FAIL() << strerror(events[0].fflags);
525                 m_nready = events[0].data;
526                 break;
527         case POLL:
528                 fds[0].fd = m_fuse_fd;
529                 fds[0].events = POLLIN;
530                 nready = poll(fds, 1, INFTIM);
531                 if (m_quit)
532                         return;
533                 ASSERT_LE(0, nready) << strerror(errno);
534                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
535                 ASSERT_TRUE(fds[0].revents & POLLIN);
536                 break;
537         case SELECT:
538                 FD_ZERO(&readfds);
539                 FD_SET(m_fuse_fd, &readfds);
540                 nfds = m_fuse_fd + 1;
541                 nready = select(nfds, &readfds, NULL, NULL, NULL);
542                 if (m_quit)
543                         return;
544                 ASSERT_LE(0, nready) << strerror(errno);
545                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
546                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
547                 break;
548         default:
549                 FAIL() << "not yet implemented";
550         }
551         res = read(m_fuse_fd, in, sizeof(*in));
552
553         if (res < 0 && !m_quit)
554                 perror("read");
555         ASSERT_TRUE(res >= (ssize_t)sizeof(in->header) || m_quit);
556 }
557
558 void MockFS::write_response(mockfs_buf_out *out) {
559         fd_set writefds;
560         pollfd fds[1];
561         int nready, nfds;
562         ssize_t r;
563
564         switch (m_pm) {
565         case BLOCKING:
566         case KQ:        /* EVFILT_WRITE is not supported */
567                 break;
568         case POLL:
569                 fds[0].fd = m_fuse_fd;
570                 fds[0].events = POLLOUT;
571                 nready = poll(fds, 1, INFTIM);
572                 ASSERT_LE(0, nready) << strerror(errno);
573                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
574                 ASSERT_TRUE(fds[0].revents & POLLOUT);
575                 break;
576         case SELECT:
577                 FD_ZERO(&writefds);
578                 FD_SET(m_fuse_fd, &writefds);
579                 nfds = m_fuse_fd + 1;
580                 nready = select(nfds, NULL, &writefds, NULL, NULL);
581                 ASSERT_LE(0, nready) << strerror(errno);
582                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
583                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
584                 break;
585         default:
586                 FAIL() << "not yet implemented";
587         }
588         r = write(m_fuse_fd, out, out->header.len);
589         ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
590 }
591
592 void* MockFS::service(void *pthr_data) {
593         MockFS *mock_fs = (MockFS*)pthr_data;
594
595         mock_fs->loop();
596
597         return (NULL);
598 }
599
600 void MockFS::unmount() {
601         ::unmount("mountpoint", 0);
602 }