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