]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/mockfs.cc
fusefs: fix warnings in the tests reported by GCC
[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 MockFS::debug_request(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_BMAP:
172                         printf(" block=%" PRIx64 " blocksize=%#x",
173                                 in.body.bmap.block, in.body.bmap.blocksize);
174                         break;
175                 case FUSE_CREATE:
176                         if (m_kernel_minor_version >= 12)
177                                 name = (const char*)in.body.bytes +
178                                         sizeof(fuse_create_in);
179                         else
180                                 name = (const char*)in.body.bytes +
181                                         sizeof(fuse_open_in);
182                         printf(" flags=%#x name=%s",
183                                 in.body.open.flags, name);
184                         break;
185                 case FUSE_FLUSH:
186                         printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
187                                 in.body.flush.fh,
188                                 in.body.flush.lock_owner);
189                         break;
190                 case FUSE_FORGET:
191                         printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
192                         break;
193                 case FUSE_FSYNC:
194                         printf(" flags=%#x", in.body.fsync.fsync_flags);
195                         break;
196                 case FUSE_FSYNCDIR:
197                         printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
198                         break;
199                 case FUSE_INTERRUPT:
200                         printf(" unique=%" PRIu64, in.body.interrupt.unique);
201                         break;
202                 case FUSE_LINK:
203                         printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
204                         break;
205                 case FUSE_LOOKUP:
206                         printf(" %s", in.body.lookup);
207                         break;
208                 case FUSE_MKDIR:
209                         name = (const char*)in.body.bytes +
210                                 sizeof(fuse_mkdir_in);
211                         printf(" name=%s mode=%#o umask=%#o", name,
212                                 in.body.mkdir.mode, in.body.mkdir.umask);
213                         break;
214                 case FUSE_MKNOD:
215                         if (m_kernel_minor_version >= 12)
216                                 name = (const char*)in.body.bytes +
217                                         sizeof(fuse_mknod_in);
218                         else
219                                 name = (const char*)in.body.bytes +
220                                         FUSE_COMPAT_MKNOD_IN_SIZE;
221                         printf(" mode=%#o rdev=%x umask=%#o name=%s",
222                                 in.body.mknod.mode, in.body.mknod.rdev,
223                                 in.body.mknod.umask, name);
224                         break;
225                 case FUSE_OPEN:
226                         printf(" flags=%#x", in.body.open.flags);
227                         break;
228                 case FUSE_OPENDIR:
229                         printf(" flags=%#x", in.body.opendir.flags);
230                         break;
231                 case FUSE_READ:
232                         printf(" offset=%" PRIu64 " size=%u",
233                                 in.body.read.offset,
234                                 in.body.read.size);
235                         if (verbosity > 1)
236                                 printf(" flags=%#x", in.body.read.flags);
237                         break;
238                 case FUSE_READDIR:
239                         printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
240                                 in.body.readdir.fh, in.body.readdir.offset,
241                                 in.body.readdir.size);
242                         break;
243                 case FUSE_RELEASE:
244                         printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
245                                 in.body.release.fh,
246                                 in.body.release.flags,
247                                 in.body.release.lock_owner);
248                         break;
249                 case FUSE_SETATTR:
250                         if (verbosity <= 1) {
251                                 printf(" valid=%#x", in.body.setattr.valid);
252                                 break;
253                         }
254                         if (in.body.setattr.valid & FATTR_MODE)
255                                 printf(" mode=%#o", in.body.setattr.mode);
256                         if (in.body.setattr.valid & FATTR_UID)
257                                 printf(" uid=%u", in.body.setattr.uid);
258                         if (in.body.setattr.valid & FATTR_GID)
259                                 printf(" gid=%u", in.body.setattr.gid);
260                         if (in.body.setattr.valid & FATTR_SIZE)
261                                 printf(" size=%" PRIu64, in.body.setattr.size);
262                         if (in.body.setattr.valid & FATTR_ATIME)
263                                 printf(" atime=%" PRIu64 ".%u",
264                                         in.body.setattr.atime,
265                                         in.body.setattr.atimensec);
266                         if (in.body.setattr.valid & FATTR_MTIME)
267                                 printf(" mtime=%" PRIu64 ".%u",
268                                         in.body.setattr.mtime,
269                                         in.body.setattr.mtimensec);
270                         if (in.body.setattr.valid & FATTR_FH)
271                                 printf(" fh=%" PRIu64 "", in.body.setattr.fh);
272                         break;
273                 case FUSE_SETLK:
274                         printf(" fh=%#" PRIx64 " owner=%" PRIu64
275                                 " type=%u pid=%u",
276                                 in.body.setlk.fh, in.body.setlk.owner,
277                                 in.body.setlk.lk.type,
278                                 in.body.setlk.lk.pid);
279                         if (verbosity >= 2) {
280                                 printf(" range=[%" PRIu64 "-%" PRIu64 "]",
281                                         in.body.setlk.lk.start,
282                                         in.body.setlk.lk.end);
283                         }
284                         break;
285                 case FUSE_SETXATTR:
286                         /* 
287                          * In theory neither the xattr name and value need be
288                          * ASCII, but in this test suite they always are.
289                          */
290                         name = (const char*)in.body.bytes +
291                                 sizeof(fuse_setxattr_in);
292                         value = name + strlen(name) + 1;
293                         printf(" %s=%s", name, value);
294                         break;
295                 case FUSE_WRITE:
296                         printf(" fh=%#" PRIx64 " offset=%" PRIu64
297                                 " size=%u write_flags=%u",
298                                 in.body.write.fh,
299                                 in.body.write.offset, in.body.write.size,
300                                 in.body.write.write_flags);
301                         if (verbosity > 1)
302                                 printf(" flags=%#x", in.body.write.flags);
303                         break;
304                 default:
305                         break;
306         }
307         printf("\n");
308 }
309
310 /* 
311  * Debug a FUSE response.
312  *
313  * This is mostly useful for asynchronous notifications, which don't correspond
314  * to any request
315  */
316 void MockFS::debug_response(const mockfs_buf_out &out) {
317         const char *name;
318
319         if (verbosity == 0)
320                 return;
321
322         switch (out.header.error) {
323                 case FUSE_NOTIFY_INVAL_ENTRY:
324                         name = (const char*)out.body.bytes +
325                                 sizeof(fuse_notify_inval_entry_out);
326                         printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
327                                 out.body.inval_entry.parent, name);
328                         break;
329                 case FUSE_NOTIFY_INVAL_INODE:
330                         printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
331                                 " len=%" PRIi64 "\n",
332                                 out.body.inval_inode.ino,
333                                 out.body.inval_inode.off,
334                                 out.body.inval_inode.len);
335                         break;
336                 case FUSE_NOTIFY_STORE:
337                         printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
338                                 " size=%" PRIu32 "\n",
339                                 out.body.store.nodeid,
340                                 out.body.store.offset,
341                                 out.body.store.size);
342                         break;
343                 default:
344                         break;
345         }
346 }
347
348 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
349         bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
350         uint32_t kernel_minor_version, uint32_t max_write, bool async,
351         bool noclusterr, unsigned time_gran, bool nointr)
352 {
353         struct sigaction sa;
354         struct iovec *iov = NULL;
355         int iovlen = 0;
356         char fdstr[15];
357         const bool trueval = true;
358
359         m_daemon_id = NULL;
360         m_kernel_minor_version = kernel_minor_version;
361         m_maxreadahead = max_readahead;
362         m_maxwrite = max_write;
363         m_nready = -1;
364         m_pm = pm;
365         m_time_gran = time_gran;
366         m_quit = false;
367         if (m_pm == KQ)
368                 m_kq = kqueue();
369         else
370                 m_kq = -1;
371
372         /*
373          * Kyua sets pwd to a testcase-unique tempdir; no need to use
374          * mkdtemp
375          */
376         /*
377          * googletest doesn't allow ASSERT_ in constructors, so we must throw
378          * instead.
379          */
380         if (mkdir("mountpoint" , 0755) && errno != EEXIST)
381                 throw(std::system_error(errno, std::system_category(),
382                         "Couldn't make mountpoint directory"));
383
384         switch (m_pm) {
385         case BLOCKING:
386                 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
387                 break;
388         default:
389                 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
390                 break;
391         }
392         if (m_fuse_fd < 0)
393                 throw(std::system_error(errno, std::system_category(),
394                         "Couldn't open /dev/fuse"));
395
396         m_pid = getpid();
397         m_child_pid = -1;
398
399         build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
400         build_iovec(&iov, &iovlen, "fspath",
401                     __DECONST(void *, "mountpoint"), -1);
402         build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
403         sprintf(fdstr, "%d", m_fuse_fd);
404         build_iovec(&iov, &iovlen, "fd", fdstr, -1);
405         if (allow_other) {
406                 build_iovec(&iov, &iovlen, "allow_other",
407                         __DECONST(void*, &trueval), sizeof(bool));
408         }
409         if (default_permissions) {
410                 build_iovec(&iov, &iovlen, "default_permissions",
411                         __DECONST(void*, &trueval), sizeof(bool));
412         }
413         if (push_symlinks_in) {
414                 build_iovec(&iov, &iovlen, "push_symlinks_in",
415                         __DECONST(void*, &trueval), sizeof(bool));
416         }
417         if (ro) {
418                 build_iovec(&iov, &iovlen, "ro",
419                         __DECONST(void*, &trueval), sizeof(bool));
420         }
421         if (async) {
422                 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
423                         sizeof(bool));
424         }
425         if (noclusterr) {
426                 build_iovec(&iov, &iovlen, "noclusterr",
427                         __DECONST(void*, &trueval), sizeof(bool));
428         }
429         if (nointr) {
430                 build_iovec(&iov, &iovlen, "nointr",
431                         __DECONST(void*, &trueval), sizeof(bool));
432         } else {
433                 build_iovec(&iov, &iovlen, "intr",
434                         __DECONST(void*, &trueval), sizeof(bool));
435         }
436         if (nmount(iov, iovlen, 0))
437                 throw(std::system_error(errno, std::system_category(),
438                         "Couldn't mount filesystem"));
439
440         // Setup default handler
441         ON_CALL(*this, process(_, _))
442                 .WillByDefault(Invoke(this, &MockFS::process_default));
443
444         init(flags);
445         bzero(&sa, sizeof(sa));
446         sa.sa_handler = sigint_handler;
447         sa.sa_flags = 0;        /* Don't set SA_RESTART! */
448         if (0 != sigaction(SIGUSR1, &sa, NULL))
449                 throw(std::system_error(errno, std::system_category(),
450                         "Couldn't handle SIGUSR1"));
451         if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
452                 throw(std::system_error(errno, std::system_category(),
453                         "Couldn't Couldn't start fuse thread"));
454 }
455
456 MockFS::~MockFS() {
457         kill_daemon();
458         if (m_daemon_id != NULL) {
459                 pthread_join(m_daemon_id, NULL);
460                 m_daemon_id = NULL;
461         }
462         ::unmount("mountpoint", MNT_FORCE);
463         rmdir("mountpoint");
464         if (m_kq >= 0)
465                 close(m_kq);
466 }
467
468 void MockFS::init(uint32_t flags) {
469         std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
470         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
471
472         read_request(*in);
473         ASSERT_EQ(FUSE_INIT, in->header.opcode);
474
475         out->header.unique = in->header.unique;
476         out->header.error = 0;
477         out->body.init.major = FUSE_KERNEL_VERSION;
478         out->body.init.minor = m_kernel_minor_version;;
479         out->body.init.flags = in->body.init.flags & flags;
480         out->body.init.max_write = m_maxwrite;
481         out->body.init.max_readahead = m_maxreadahead;
482
483         if (m_kernel_minor_version < 23) {
484                 SET_OUT_HEADER_LEN(*out, init_7_22);
485         } else {
486                 out->body.init.time_gran = m_time_gran;
487                 SET_OUT_HEADER_LEN(*out, init);
488         }
489
490         write(m_fuse_fd, out.get(), out->header.len);
491 }
492
493 void MockFS::kill_daemon() {
494         m_quit = true;
495         if (m_daemon_id != NULL)
496                 pthread_kill(m_daemon_id, SIGUSR1);
497         // Closing the /dev/fuse file descriptor first allows unmount to
498         // succeed even if the daemon doesn't correctly respond to commands
499         // during the unmount sequence.
500         close(m_fuse_fd);
501         m_fuse_fd = -1;
502 }
503
504 void MockFS::loop() {
505         std::vector<std::unique_ptr<mockfs_buf_out>> out;
506
507         std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
508         ASSERT_TRUE(in != NULL);
509         while (!m_quit) {
510                 bzero(in.get(), sizeof(*in));
511                 read_request(*in);
512                 if (m_quit)
513                         break;
514                 if (verbosity > 0)
515                         debug_request(*in);
516                 if (pid_ok((pid_t)in->header.pid)) {
517                         process(*in, out);
518                 } else {
519                         /* 
520                          * Reject any requests from unknown processes.  Because
521                          * we actually do mount a filesystem, plenty of
522                          * unrelated system daemons may try to access it.
523                          */
524                         if (verbosity > 1)
525                                 printf("\tREJECTED (wrong pid %d)\n",
526                                         in->header.pid);
527                         process_default(*in, out);
528                 }
529                 for (auto &it: out)
530                         write_response(*it);
531                 out.clear();
532         }
533 }
534
535 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
536 {
537         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
538
539         out->header.unique = 0; /* 0 means asynchronous notification */
540         out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
541         out->body.inval_entry.parent = parent;
542         out->body.inval_entry.namelen = namelen;
543         strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
544                 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
545         out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
546                 namelen;
547         debug_response(*out);
548         write_response(*out);
549         return 0;
550 }
551
552 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
553 {
554         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
555
556         out->header.unique = 0; /* 0 means asynchronous notification */
557         out->header.error = FUSE_NOTIFY_INVAL_INODE;
558         out->body.inval_inode.ino = ino;
559         out->body.inval_inode.off = off;
560         out->body.inval_inode.len = len;
561         out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
562         debug_response(*out);
563         write_response(*out);
564         return 0;
565 }
566
567 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
568 {
569         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
570
571         out->header.unique = 0; /* 0 means asynchronous notification */
572         out->header.error = FUSE_NOTIFY_STORE;
573         out->body.store.nodeid = ino;
574         out->body.store.offset = off;
575         out->body.store.size = size;
576         bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
577         out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
578         debug_response(*out);
579         write_response(*out);
580         return 0;
581 }
582
583 bool MockFS::pid_ok(pid_t pid) {
584         if (pid == m_pid) {
585                 return (true);
586         } else if (pid == m_child_pid) {
587                 return (true);
588         } else {
589                 struct kinfo_proc *ki;
590                 bool ok = false;
591
592                 ki = kinfo_getproc(pid);
593                 if (ki == NULL)
594                         return (false);
595                 /* 
596                  * Allow access by the aio daemon processes so that our tests
597                  * can use aio functions
598                  */
599                 if (0 == strncmp("aiod", ki->ki_comm, 4))
600                         ok = true;
601                 free(ki);
602                 return (ok);
603         }
604 }
605
606 void MockFS::process_default(const mockfs_buf_in& in,
607                 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
608 {
609         std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
610         out0->header.unique = in.header.unique;
611         out0->header.error = -EOPNOTSUPP;
612         out0->header.len = sizeof(out0->header);
613         out.push_back(std::move(out0));
614 }
615
616 void MockFS::read_request(mockfs_buf_in &in) {
617         ssize_t res;
618         int nready = 0;
619         fd_set readfds;
620         pollfd fds[1];
621         struct kevent changes[1];
622         struct kevent events[1];
623         struct timespec timeout_ts;
624         struct timeval timeout_tv;
625         const int timeout_ms = 999;
626         int timeout_int, nfds;
627
628         switch (m_pm) {
629         case BLOCKING:
630                 break;
631         case KQ:
632                 timeout_ts.tv_sec = 0;
633                 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
634                 while (nready == 0) {
635                         EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
636                                 0, 0);
637                         nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
638                                 &timeout_ts);
639                         if (m_quit)
640                                 return;
641                 }
642                 ASSERT_LE(0, nready) << strerror(errno);
643                 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
644                 if (events[0].flags & EV_ERROR)
645                         FAIL() << strerror(events[0].data);
646                 else if (events[0].flags & EV_EOF)
647                         FAIL() << strerror(events[0].fflags);
648                 m_nready = events[0].data;
649                 break;
650         case POLL:
651                 timeout_int = timeout_ms;
652                 fds[0].fd = m_fuse_fd;
653                 fds[0].events = POLLIN;
654                 while (nready == 0) {
655                         nready = poll(fds, 1, timeout_int);
656                         if (m_quit)
657                                 return;
658                 }
659                 ASSERT_LE(0, nready) << strerror(errno);
660                 ASSERT_TRUE(fds[0].revents & POLLIN);
661                 break;
662         case SELECT:
663                 timeout_tv.tv_sec = 0;
664                 timeout_tv.tv_usec = timeout_ms * 1'000;
665                 nfds = m_fuse_fd + 1;
666                 while (nready == 0) {
667                         FD_ZERO(&readfds);
668                         FD_SET(m_fuse_fd, &readfds);
669                         nready = select(nfds, &readfds, NULL, NULL,
670                                 &timeout_tv);
671                         if (m_quit)
672                                 return;
673                 }
674                 ASSERT_LE(0, nready) << strerror(errno);
675                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
676                 break;
677         default:
678                 FAIL() << "not yet implemented";
679         }
680         res = read(m_fuse_fd, &in, sizeof(in));
681
682         if (res < 0 && !m_quit) {
683                 FAIL() << "read: " << strerror(errno);
684                 m_quit = true;
685         }
686         ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
687 }
688
689 void MockFS::write_response(const mockfs_buf_out &out) {
690         fd_set writefds;
691         pollfd fds[1];
692         int nready, nfds;
693         ssize_t r;
694
695         switch (m_pm) {
696         case BLOCKING:
697         case KQ:        /* EVFILT_WRITE is not supported */
698                 break;
699         case POLL:
700                 fds[0].fd = m_fuse_fd;
701                 fds[0].events = POLLOUT;
702                 nready = poll(fds, 1, INFTIM);
703                 ASSERT_LE(0, nready) << strerror(errno);
704                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
705                 ASSERT_TRUE(fds[0].revents & POLLOUT);
706                 break;
707         case SELECT:
708                 FD_ZERO(&writefds);
709                 FD_SET(m_fuse_fd, &writefds);
710                 nfds = m_fuse_fd + 1;
711                 nready = select(nfds, NULL, &writefds, NULL, NULL);
712                 ASSERT_LE(0, nready) << strerror(errno);
713                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
714                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
715                 break;
716         default:
717                 FAIL() << "not yet implemented";
718         }
719         r = write(m_fuse_fd, &out, out.header.len);
720         ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
721 }
722
723 void* MockFS::service(void *pthr_data) {
724         MockFS *mock_fs = (MockFS*)pthr_data;
725
726         mock_fs->loop();
727
728         return (NULL);
729 }
730
731 void MockFS::unmount() {
732         ::unmount("mountpoint", 0);
733 }