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