]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/fs/fusefs/mockfs.cc
fusefs: raise protocol level to 7.15
[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
473         out->body.init.max_write = m_maxwrite;
474
475         out->body.init.max_readahead = m_maxreadahead;
476         SET_OUT_HEADER_LEN(*out, init);
477         write(m_fuse_fd, out.get(), out->header.len);
478 }
479
480 void MockFS::kill_daemon() {
481         m_quit = true;
482         if (m_daemon_id != NULL)
483                 pthread_kill(m_daemon_id, SIGUSR1);
484         // Closing the /dev/fuse file descriptor first allows unmount to
485         // succeed even if the daemon doesn't correctly respond to commands
486         // during the unmount sequence.
487         close(m_fuse_fd);
488         m_fuse_fd = -1;
489 }
490
491 void MockFS::loop() {
492         std::vector<std::unique_ptr<mockfs_buf_out>> out;
493
494         std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
495         ASSERT_TRUE(in != NULL);
496         while (!m_quit) {
497                 bzero(in.get(), sizeof(*in));
498                 read_request(*in);
499                 if (m_quit)
500                         break;
501                 if (verbosity > 0)
502                         debug_request(*in);
503                 if (pid_ok((pid_t)in->header.pid)) {
504                         process(*in, out);
505                 } else {
506                         /* 
507                          * Reject any requests from unknown processes.  Because
508                          * we actually do mount a filesystem, plenty of
509                          * unrelated system daemons may try to access it.
510                          */
511                         if (verbosity > 1)
512                                 printf("\tREJECTED (wrong pid %d)\n",
513                                         in->header.pid);
514                         process_default(*in, out);
515                 }
516                 for (auto &it: out)
517                         write_response(*it);
518                 out.clear();
519         }
520 }
521
522 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
523 {
524         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
525
526         out->header.unique = 0; /* 0 means asynchronous notification */
527         out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
528         out->body.inval_entry.parent = parent;
529         out->body.inval_entry.namelen = namelen;
530         strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
531                 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
532         out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
533                 namelen;
534         debug_response(*out);
535         write_response(*out);
536         return 0;
537 }
538
539 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
540 {
541         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
542
543         out->header.unique = 0; /* 0 means asynchronous notification */
544         out->header.error = FUSE_NOTIFY_INVAL_INODE;
545         out->body.inval_inode.ino = ino;
546         out->body.inval_inode.off = off;
547         out->body.inval_inode.len = len;
548         out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
549         debug_response(*out);
550         write_response(*out);
551         return 0;
552 }
553
554 int MockFS::notify_store(ino_t ino, off_t off, void* data, ssize_t size)
555 {
556         std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
557
558         out->header.unique = 0; /* 0 means asynchronous notification */
559         out->header.error = FUSE_NOTIFY_STORE;
560         out->body.store.nodeid = ino;
561         out->body.store.offset = off;
562         out->body.store.size = size;
563         bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
564         out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
565         debug_response(*out);
566         write_response(*out);
567         return 0;
568 }
569
570 bool MockFS::pid_ok(pid_t pid) {
571         if (pid == m_pid) {
572                 return (true);
573         } else if (pid == m_child_pid) {
574                 return (true);
575         } else {
576                 struct kinfo_proc *ki;
577                 bool ok = false;
578
579                 ki = kinfo_getproc(pid);
580                 if (ki == NULL)
581                         return (false);
582                 /* 
583                  * Allow access by the aio daemon processes so that our tests
584                  * can use aio functions
585                  */
586                 if (0 == strncmp("aiod", ki->ki_comm, 4))
587                         ok = true;
588                 free(ki);
589                 return (ok);
590         }
591 }
592
593 void MockFS::process_default(const mockfs_buf_in& in,
594                 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
595 {
596         std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
597         out0->header.unique = in.header.unique;
598         out0->header.error = -EOPNOTSUPP;
599         out0->header.len = sizeof(out0->header);
600         out.push_back(std::move(out0));
601 }
602
603 void MockFS::read_request(mockfs_buf_in &in) {
604         ssize_t res;
605         int nready = 0;
606         fd_set readfds;
607         pollfd fds[1];
608         struct kevent changes[1];
609         struct kevent events[1];
610         struct timespec timeout_ts;
611         struct timeval timeout_tv;
612         const int timeout_ms = 999;
613         int timeout_int, nfds;
614
615         switch (m_pm) {
616         case BLOCKING:
617                 break;
618         case KQ:
619                 timeout_ts.tv_sec = 0;
620                 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
621                 while (nready == 0) {
622                         EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
623                                 0, 0);
624                         nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
625                                 &timeout_ts);
626                         if (m_quit)
627                                 return;
628                 }
629                 ASSERT_LE(0, nready) << strerror(errno);
630                 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
631                 if (events[0].flags & EV_ERROR)
632                         FAIL() << strerror(events[0].data);
633                 else if (events[0].flags & EV_EOF)
634                         FAIL() << strerror(events[0].fflags);
635                 m_nready = events[0].data;
636                 break;
637         case POLL:
638                 timeout_int = timeout_ms;
639                 fds[0].fd = m_fuse_fd;
640                 fds[0].events = POLLIN;
641                 while (nready == 0) {
642                         nready = poll(fds, 1, timeout_int);
643                         if (m_quit)
644                                 return;
645                 }
646                 ASSERT_LE(0, nready) << strerror(errno);
647                 ASSERT_TRUE(fds[0].revents & POLLIN);
648                 break;
649         case SELECT:
650                 timeout_tv.tv_sec = 0;
651                 timeout_tv.tv_usec = timeout_ms * 1'000;
652                 nfds = m_fuse_fd + 1;
653                 while (nready == 0) {
654                         FD_ZERO(&readfds);
655                         FD_SET(m_fuse_fd, &readfds);
656                         nready = select(nfds, &readfds, NULL, NULL,
657                                 &timeout_tv);
658                         if (m_quit)
659                                 return;
660                 }
661                 ASSERT_LE(0, nready) << strerror(errno);
662                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
663                 break;
664         default:
665                 FAIL() << "not yet implemented";
666         }
667         res = read(m_fuse_fd, &in, sizeof(in));
668
669         if (res < 0 && !m_quit) {
670                 FAIL() << "read: " << strerror(errno);
671                 m_quit = true;
672         }
673         ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
674 }
675
676 void MockFS::write_response(const mockfs_buf_out &out) {
677         fd_set writefds;
678         pollfd fds[1];
679         int nready, nfds;
680         ssize_t r;
681
682         switch (m_pm) {
683         case BLOCKING:
684         case KQ:        /* EVFILT_WRITE is not supported */
685                 break;
686         case POLL:
687                 fds[0].fd = m_fuse_fd;
688                 fds[0].events = POLLOUT;
689                 nready = poll(fds, 1, INFTIM);
690                 ASSERT_LE(0, nready) << strerror(errno);
691                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
692                 ASSERT_TRUE(fds[0].revents & POLLOUT);
693                 break;
694         case SELECT:
695                 FD_ZERO(&writefds);
696                 FD_SET(m_fuse_fd, &writefds);
697                 nfds = m_fuse_fd + 1;
698                 nready = select(nfds, NULL, &writefds, NULL, NULL);
699                 ASSERT_LE(0, nready) << strerror(errno);
700                 ASSERT_EQ(1, nready) << "NULL timeout expired?";
701                 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
702                 break;
703         default:
704                 FAIL() << "not yet implemented";
705         }
706         r = write(m_fuse_fd, &out, out.header.len);
707         ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
708 }
709
710 void* MockFS::service(void *pthr_data) {
711         MockFS *mock_fs = (MockFS*)pthr_data;
712
713         mock_fs->loop();
714
715         return (NULL);
716 }
717
718 void MockFS::unmount() {
719         ::unmount("mountpoint", 0);
720 }