]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/mockfs.cc
fusefs: raise protocol level to 7.23
[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=%#lx blocksize=%#x", in.body.bmap.block,
173                                 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)
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_quit = false;
366         if (m_pm == KQ)
367                 m_kq = kqueue();
368         else
369                 m_kq = -1;
370
371         /*
372          * Kyua sets pwd to a testcase-unique tempdir; no need to use
373          * mkdtemp
374          */
375         /*
376          * googletest doesn't allow ASSERT_ in constructors, so we must throw
377          * instead.
378          */
379         if (mkdir("mountpoint" , 0755) && errno != EEXIST)
380                 throw(std::system_error(errno, std::system_category(),
381                         "Couldn't make mountpoint directory"));
382
383         switch (m_pm) {
384         case BLOCKING:
385                 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
386                 break;
387         default:
388                 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
389                 break;
390         }
391         if (m_fuse_fd < 0)
392                 throw(std::system_error(errno, std::system_category(),
393                         "Couldn't open /dev/fuse"));
394
395         m_pid = getpid();
396         m_child_pid = -1;
397
398         build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
399         build_iovec(&iov, &iovlen, "fspath",
400                     __DECONST(void *, "mountpoint"), -1);
401         build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
402         sprintf(fdstr, "%d", m_fuse_fd);
403         build_iovec(&iov, &iovlen, "fd", fdstr, -1);
404         if (allow_other) {
405                 build_iovec(&iov, &iovlen, "allow_other",
406                         __DECONST(void*, &trueval), sizeof(bool));
407         }
408         if (default_permissions) {
409                 build_iovec(&iov, &iovlen, "default_permissions",
410                         __DECONST(void*, &trueval), sizeof(bool));
411         }
412         if (push_symlinks_in) {
413                 build_iovec(&iov, &iovlen, "push_symlinks_in",
414                         __DECONST(void*, &trueval), sizeof(bool));
415         }
416         if (ro) {
417                 build_iovec(&iov, &iovlen, "ro",
418                         __DECONST(void*, &trueval), sizeof(bool));
419         }
420         if (async) {
421                 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
422                         sizeof(bool));
423         }
424         if (noclusterr) {
425                 build_iovec(&iov, &iovlen, "noclusterr",
426                         __DECONST(void*, &trueval), sizeof(bool));
427         }
428         if (nmount(iov, iovlen, 0))
429                 throw(std::system_error(errno, std::system_category(),
430                         "Couldn't mount filesystem"));
431
432         // Setup default handler
433         ON_CALL(*this, process(_, _))
434                 .WillByDefault(Invoke(this, &MockFS::process_default));
435
436         init(flags);
437         bzero(&sa, sizeof(sa));
438         sa.sa_handler = sigint_handler;
439         sa.sa_flags = 0;        /* Don't set SA_RESTART! */
440         if (0 != sigaction(SIGUSR1, &sa, NULL))
441                 throw(std::system_error(errno, std::system_category(),
442                         "Couldn't handle SIGUSR1"));
443         if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
444                 throw(std::system_error(errno, std::system_category(),
445                         "Couldn't Couldn't start fuse thread"));
446 }
447
448 MockFS::~MockFS() {
449         kill_daemon();
450         if (m_daemon_id != NULL) {
451                 pthread_join(m_daemon_id, NULL);
452                 m_daemon_id = NULL;
453         }
454         ::unmount("mountpoint", MNT_FORCE);
455         rmdir("mountpoint");
456         if (m_kq >= 0)
457                 close(m_kq);
458 }
459
460 void MockFS::init(uint32_t flags) {
461         std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
462         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
463
464         read_request(*in);
465         ASSERT_EQ(FUSE_INIT, in->header.opcode);
466
467         out->header.unique = in->header.unique;
468         out->header.error = 0;
469         out->body.init.major = FUSE_KERNEL_VERSION;
470         out->body.init.minor = m_kernel_minor_version;;
471         out->body.init.flags = in->body.init.flags & flags;
472         out->body.init.max_write = m_maxwrite;
473         out->body.init.max_readahead = m_maxreadahead;
474
475         if (m_kernel_minor_version < 23) {
476                 SET_OUT_HEADER_LEN(*out, init_7_22);
477         } else {
478                 SET_OUT_HEADER_LEN(*out, init);
479         }
480
481         write(m_fuse_fd, out.get(), out->header.len);
482 }
483
484 void MockFS::kill_daemon() {
485         m_quit = true;
486         if (m_daemon_id != NULL)
487                 pthread_kill(m_daemon_id, SIGUSR1);
488         // Closing the /dev/fuse file descriptor first allows unmount to
489         // succeed even if the daemon doesn't correctly respond to commands
490         // during the unmount sequence.
491         close(m_fuse_fd);
492         m_fuse_fd = -1;
493 }
494
495 void MockFS::loop() {
496         std::vector<std::unique_ptr<mockfs_buf_out>> out;
497
498         std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
499         ASSERT_TRUE(in != NULL);
500         while (!m_quit) {
501                 bzero(in.get(), sizeof(*in));
502                 read_request(*in);
503                 if (m_quit)
504                         break;
505                 if (verbosity > 0)
506                         debug_request(*in);
507                 if (pid_ok((pid_t)in->header.pid)) {
508                         process(*in, out);
509                 } else {
510                         /* 
511                          * Reject any requests from unknown processes.  Because
512                          * we actually do mount a filesystem, plenty of
513                          * unrelated system daemons may try to access it.
514                          */
515                         if (verbosity > 1)
516                                 printf("\tREJECTED (wrong pid %d)\n",
517                                         in->header.pid);
518                         process_default(*in, out);
519                 }
520                 for (auto &it: out)
521                         write_response(*it);
522                 out.clear();
523         }
524 }
525
526 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
527 {
528         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
529
530         out->header.unique = 0; /* 0 means asynchronous notification */
531         out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
532         out->body.inval_entry.parent = parent;
533         out->body.inval_entry.namelen = namelen;
534         strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
535                 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
536         out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
537                 namelen;
538         debug_response(*out);
539         write_response(*out);
540         return 0;
541 }
542
543 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
544 {
545         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
546
547         out->header.unique = 0; /* 0 means asynchronous notification */
548         out->header.error = FUSE_NOTIFY_INVAL_INODE;
549         out->body.inval_inode.ino = ino;
550         out->body.inval_inode.off = off;
551         out->body.inval_inode.len = len;
552         out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
553         debug_response(*out);
554         write_response(*out);
555         return 0;
556 }
557
558 int MockFS::notify_store(ino_t ino, off_t off, void* data, ssize_t size)
559 {
560         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
561
562         out->header.unique = 0; /* 0 means asynchronous notification */
563         out->header.error = FUSE_NOTIFY_STORE;
564         out->body.store.nodeid = ino;
565         out->body.store.offset = off;
566         out->body.store.size = size;
567         bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
568         out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
569         debug_response(*out);
570         write_response(*out);
571         return 0;
572 }
573
574 bool MockFS::pid_ok(pid_t pid) {
575         if (pid == m_pid) {
576                 return (true);
577         } else if (pid == m_child_pid) {
578                 return (true);
579         } else {
580                 struct kinfo_proc *ki;
581                 bool ok = false;
582
583                 ki = kinfo_getproc(pid);
584                 if (ki == NULL)
585                         return (false);
586                 /* 
587                  * Allow access by the aio daemon processes so that our tests
588                  * can use aio functions
589                  */
590                 if (0 == strncmp("aiod", ki->ki_comm, 4))
591                         ok = true;
592                 free(ki);
593                 return (ok);
594         }
595 }
596
597 void MockFS::process_default(const mockfs_buf_in& in,
598                 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
599 {
600         std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
601         out0->header.unique = in.header.unique;
602         out0->header.error = -EOPNOTSUPP;
603         out0->header.len = sizeof(out0->header);
604         out.push_back(std::move(out0));
605 }
606
607 void MockFS::read_request(mockfs_buf_in &in) {
608         ssize_t res;
609         int nready = 0;
610         fd_set readfds;
611         pollfd fds[1];
612         struct kevent changes[1];
613         struct kevent events[1];
614         struct timespec timeout_ts;
615         struct timeval timeout_tv;
616         const int timeout_ms = 999;
617         int timeout_int, nfds;
618
619         switch (m_pm) {
620         case BLOCKING:
621                 break;
622         case KQ:
623                 timeout_ts.tv_sec = 0;
624                 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
625                 while (nready == 0) {
626                         EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
627                                 0, 0);
628                         nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
629                                 &timeout_ts);
630                         if (m_quit)
631                                 return;
632                 }
633                 ASSERT_LE(0, nready) << strerror(errno);
634                 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
635                 if (events[0].flags & EV_ERROR)
636                         FAIL() << strerror(events[0].data);
637                 else if (events[0].flags & EV_EOF)
638                         FAIL() << strerror(events[0].fflags);
639                 m_nready = events[0].data;
640                 break;
641         case POLL:
642                 timeout_int = timeout_ms;
643                 fds[0].fd = m_fuse_fd;
644                 fds[0].events = POLLIN;
645                 while (nready == 0) {
646                         nready = poll(fds, 1, timeout_int);
647                         if (m_quit)
648                                 return;
649                 }
650                 ASSERT_LE(0, nready) << strerror(errno);
651                 ASSERT_TRUE(fds[0].revents & POLLIN);
652                 break;
653         case SELECT:
654                 timeout_tv.tv_sec = 0;
655                 timeout_tv.tv_usec = timeout_ms * 1'000;
656                 nfds = m_fuse_fd + 1;
657                 while (nready == 0) {
658                         FD_ZERO(&readfds);
659                         FD_SET(m_fuse_fd, &readfds);
660                         nready = select(nfds, &readfds, NULL, NULL,
661                                 &timeout_tv);
662                         if (m_quit)
663                                 return;
664                 }
665                 ASSERT_LE(0, nready) << strerror(errno);
666                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
667                 break;
668         default:
669                 FAIL() << "not yet implemented";
670         }
671         res = read(m_fuse_fd, &in, sizeof(in));
672
673         if (res < 0 && !m_quit) {
674                 FAIL() << "read: " << strerror(errno);
675                 m_quit = true;
676         }
677         ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
678 }
679
680 void MockFS::write_response(const mockfs_buf_out &out) {
681         fd_set writefds;
682         pollfd fds[1];
683         int nready, nfds;
684         ssize_t r;
685
686         switch (m_pm) {
687         case BLOCKING:
688         case KQ:        /* EVFILT_WRITE is not supported */
689                 break;
690         case POLL:
691                 fds[0].fd = m_fuse_fd;
692                 fds[0].events = POLLOUT;
693                 nready = poll(fds, 1, INFTIM);
694                 ASSERT_LE(0, nready) << strerror(errno);
695                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
696                 ASSERT_TRUE(fds[0].revents & POLLOUT);
697                 break;
698         case SELECT:
699                 FD_ZERO(&writefds);
700                 FD_SET(m_fuse_fd, &writefds);
701                 nfds = m_fuse_fd + 1;
702                 nready = select(nfds, NULL, &writefds, NULL, NULL);
703                 ASSERT_LE(0, nready) << strerror(errno);
704                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
705                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
706                 break;
707         default:
708                 FAIL() << "not yet implemented";
709         }
710         r = write(m_fuse_fd, &out, out.header.len);
711         ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
712 }
713
714 void* MockFS::service(void *pthr_data) {
715         MockFS *mock_fs = (MockFS*)pthr_data;
716
717         mock_fs->loop();
718
719         return (NULL);
720 }
721
722 void MockFS::unmount() {
723         ::unmount("mountpoint", 0);
724 }