2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
34 #include <sys/param.h>
36 #include <sys/mount.h>
37 #include <sys/select.h>
50 #include "mntopts.h" // for build_iovec
55 #include <gtest/gtest.h>
59 using namespace testing;
63 const char* opcode2opname(uint32_t opcode)
65 const int NUM_OPS = 39;
66 const char* table[NUM_OPS] = {
86 "Unknown (opcode 19)",
107 if (opcode >= NUM_OPS)
108 return ("Unknown (opcode > max)");
110 return (table[opcode]);
114 ReturnErrno(int error)
116 return([=](auto in, auto &out) {
117 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
118 out0->header.unique = in.header.unique;
119 out0->header.error = -error;
120 out0->header.len = sizeof(out0->header);
121 out.push_back(std::move(out0));
125 /* Helper function used for returning negative cache entries for LOOKUP */
127 ReturnNegativeCache(const struct timespec *entry_valid)
129 return([=](auto in, auto &out) {
130 /* nodeid means ENOENT and cache it */
131 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
132 out0->body.entry.nodeid = 0;
133 out0->header.unique = in.header.unique;
134 out0->header.error = 0;
135 out0->body.entry.entry_valid = entry_valid->tv_sec;
136 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
137 SET_OUT_HEADER_LEN(*out0, entry);
138 out.push_back(std::move(out0));
143 ReturnImmediate(std::function<void(const mockfs_buf_in& in,
144 struct mockfs_buf_out &out)> f)
146 return([=](auto& in, auto &out) {
147 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
148 out0->header.unique = in.header.unique;
150 out.push_back(std::move(out0));
154 void sigint_handler(int __unused sig) {
155 // Don't do anything except interrupt the daemon's read(2) call
158 void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
160 printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
163 printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
165 in.header.uid, in.header.gid, in.header.pid,
166 in.header.unique, in.header.len, buflen);
168 switch (in.header.opcode) {
169 const char *name, *value;
172 printf(" mask=%#x", in.body.access.mask);
175 printf(" block=%" PRIx64 " blocksize=%#x",
176 in.body.bmap.block, in.body.bmap.blocksize);
179 if (m_kernel_minor_version >= 12)
180 name = (const char*)in.body.bytes +
181 sizeof(fuse_create_in);
183 name = (const char*)in.body.bytes +
184 sizeof(fuse_open_in);
185 printf(" flags=%#x name=%s",
186 in.body.open.flags, name);
189 printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
191 in.body.flush.lock_owner);
194 printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
197 printf(" flags=%#x", in.body.fsync.fsync_flags);
200 printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
203 printf(" unique=%" PRIu64, in.body.interrupt.unique);
206 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
209 printf(" size=%" PRIu32, in.body.listxattr.size);
212 printf(" %s", in.body.lookup);
215 name = (const char*)in.body.bytes +
216 sizeof(fuse_mkdir_in);
217 printf(" name=%s mode=%#o umask=%#o", name,
218 in.body.mkdir.mode, in.body.mkdir.umask);
221 if (m_kernel_minor_version >= 12)
222 name = (const char*)in.body.bytes +
223 sizeof(fuse_mknod_in);
225 name = (const char*)in.body.bytes +
226 FUSE_COMPAT_MKNOD_IN_SIZE;
227 printf(" mode=%#o rdev=%x umask=%#o name=%s",
228 in.body.mknod.mode, in.body.mknod.rdev,
229 in.body.mknod.umask, name);
232 printf(" flags=%#x", in.body.open.flags);
235 printf(" flags=%#x", in.body.opendir.flags);
238 printf(" offset=%" PRIu64 " size=%u",
242 printf(" flags=%#x", in.body.read.flags);
245 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
246 in.body.readdir.fh, in.body.readdir.offset,
247 in.body.readdir.size);
250 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
252 in.body.release.flags,
253 in.body.release.lock_owner);
256 if (verbosity <= 1) {
257 printf(" valid=%#x", in.body.setattr.valid);
260 if (in.body.setattr.valid & FATTR_MODE)
261 printf(" mode=%#o", in.body.setattr.mode);
262 if (in.body.setattr.valid & FATTR_UID)
263 printf(" uid=%u", in.body.setattr.uid);
264 if (in.body.setattr.valid & FATTR_GID)
265 printf(" gid=%u", in.body.setattr.gid);
266 if (in.body.setattr.valid & FATTR_SIZE)
267 printf(" size=%" PRIu64, in.body.setattr.size);
268 if (in.body.setattr.valid & FATTR_ATIME)
269 printf(" atime=%" PRIu64 ".%u",
270 in.body.setattr.atime,
271 in.body.setattr.atimensec);
272 if (in.body.setattr.valid & FATTR_MTIME)
273 printf(" mtime=%" PRIu64 ".%u",
274 in.body.setattr.mtime,
275 in.body.setattr.mtimensec);
276 if (in.body.setattr.valid & FATTR_FH)
277 printf(" fh=%" PRIu64 "", in.body.setattr.fh);
280 printf(" fh=%#" PRIx64 " owner=%" PRIu64
282 in.body.setlk.fh, in.body.setlk.owner,
283 in.body.setlk.lk.type,
284 in.body.setlk.lk.pid);
285 if (verbosity >= 2) {
286 printf(" range=[%" PRIu64 "-%" PRIu64 "]",
287 in.body.setlk.lk.start,
288 in.body.setlk.lk.end);
293 * In theory neither the xattr name and value need be
294 * ASCII, but in this test suite they always are.
296 name = (const char*)in.body.bytes +
297 sizeof(fuse_setxattr_in);
298 value = name + strlen(name) + 1;
299 printf(" %s=%s", name, value);
302 printf(" fh=%#" PRIx64 " offset=%" PRIu64
303 " size=%u write_flags=%u",
305 in.body.write.offset, in.body.write.size,
306 in.body.write.write_flags);
308 printf(" flags=%#x", in.body.write.flags);
317 * Debug a FUSE response.
319 * This is mostly useful for asynchronous notifications, which don't correspond
322 void MockFS::debug_response(const mockfs_buf_out &out) {
328 switch (out.header.error) {
329 case FUSE_NOTIFY_INVAL_ENTRY:
330 name = (const char*)out.body.bytes +
331 sizeof(fuse_notify_inval_entry_out);
332 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
333 out.body.inval_entry.parent, name);
335 case FUSE_NOTIFY_INVAL_INODE:
336 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
337 " len=%" PRIi64 "\n",
338 out.body.inval_inode.ino,
339 out.body.inval_inode.off,
340 out.body.inval_inode.len);
342 case FUSE_NOTIFY_STORE:
343 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
344 " size=%" PRIu32 "\n",
345 out.body.store.nodeid,
346 out.body.store.offset,
347 out.body.store.size);
354 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
355 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
356 uint32_t kernel_minor_version, uint32_t max_write, bool async,
357 bool noclusterr, unsigned time_gran, bool nointr)
360 struct iovec *iov = NULL;
363 const bool trueval = true;
366 m_kernel_minor_version = kernel_minor_version;
367 m_maxreadahead = max_readahead;
368 m_maxwrite = max_write;
371 m_time_gran = time_gran;
379 * Kyua sets pwd to a testcase-unique tempdir; no need to use
383 * googletest doesn't allow ASSERT_ in constructors, so we must throw
386 if (mkdir("mountpoint" , 0755) && errno != EEXIST)
387 throw(std::system_error(errno, std::system_category(),
388 "Couldn't make mountpoint directory"));
392 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
395 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
399 throw(std::system_error(errno, std::system_category(),
400 "Couldn't open /dev/fuse"));
405 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
406 build_iovec(&iov, &iovlen, "fspath",
407 __DECONST(void *, "mountpoint"), -1);
408 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
409 sprintf(fdstr, "%d", m_fuse_fd);
410 build_iovec(&iov, &iovlen, "fd", fdstr, -1);
412 build_iovec(&iov, &iovlen, "allow_other",
413 __DECONST(void*, &trueval), sizeof(bool));
415 if (default_permissions) {
416 build_iovec(&iov, &iovlen, "default_permissions",
417 __DECONST(void*, &trueval), sizeof(bool));
419 if (push_symlinks_in) {
420 build_iovec(&iov, &iovlen, "push_symlinks_in",
421 __DECONST(void*, &trueval), sizeof(bool));
424 build_iovec(&iov, &iovlen, "ro",
425 __DECONST(void*, &trueval), sizeof(bool));
428 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
432 build_iovec(&iov, &iovlen, "noclusterr",
433 __DECONST(void*, &trueval), sizeof(bool));
436 build_iovec(&iov, &iovlen, "nointr",
437 __DECONST(void*, &trueval), sizeof(bool));
439 build_iovec(&iov, &iovlen, "intr",
440 __DECONST(void*, &trueval), sizeof(bool));
442 if (nmount(iov, iovlen, 0))
443 throw(std::system_error(errno, std::system_category(),
444 "Couldn't mount filesystem"));
446 // Setup default handler
447 ON_CALL(*this, process(_, _))
448 .WillByDefault(Invoke(this, &MockFS::process_default));
451 bzero(&sa, sizeof(sa));
452 sa.sa_handler = sigint_handler;
453 sa.sa_flags = 0; /* Don't set SA_RESTART! */
454 if (0 != sigaction(SIGUSR1, &sa, NULL))
455 throw(std::system_error(errno, std::system_category(),
456 "Couldn't handle SIGUSR1"));
457 if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
458 throw(std::system_error(errno, std::system_category(),
459 "Couldn't Couldn't start fuse thread"));
464 if (m_daemon_id != NULL) {
465 pthread_join(m_daemon_id, NULL);
468 ::unmount("mountpoint", MNT_FORCE);
474 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
475 uint32_t inlen = in.header.len;
476 size_t fih = sizeof(in.header);
477 switch (in.header.opcode) {
482 EXPECT_GT(inlen, fih) << "Missing request filename";
483 // No redundant information for checking buflen
486 EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
487 EXPECT_EQ((size_t)buflen, inlen);
490 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
491 EXPECT_EQ((size_t)buflen, inlen);
494 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
495 EXPECT_EQ((size_t)buflen, inlen);
498 EXPECT_EQ(inlen, fih) << "Unexpected request body";
499 EXPECT_EQ((size_t)buflen, inlen);
504 if (m_kernel_minor_version >= 12)
505 s = sizeof(in.body.mknod);
507 s = FUSE_COMPAT_MKNOD_IN_SIZE;
508 EXPECT_GE(inlen, fih + s) << "Missing request body";
509 EXPECT_GT(inlen, fih + s) << "Missing request filename";
510 // No redundant information for checking buflen
514 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
515 "Missing request body";
516 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
517 "Missing request filename";
518 // No redundant information for checking buflen
521 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
522 "Missing request body";
523 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
524 "Missing request filename";
525 // No redundant information for checking buflen
528 EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
529 "Missing request body";
530 EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
531 "Missing request filename";
532 // No redundant information for checking buflen
535 EXPECT_EQ(inlen, fih + sizeof(in.body.open));
536 EXPECT_EQ((size_t)buflen, inlen);
539 EXPECT_EQ(inlen, fih + sizeof(in.body.read));
540 EXPECT_EQ((size_t)buflen, inlen);
546 if (m_kernel_minor_version >= 9)
547 s = sizeof(in.body.write);
549 s = FUSE_COMPAT_WRITE_IN_SIZE;
550 // I suppose a 0-byte write should be allowed
551 EXPECT_GE(inlen, fih + s) << "Missing request body";
552 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
557 EXPECT_EQ(inlen, fih);
558 EXPECT_EQ((size_t)buflen, inlen);
561 EXPECT_EQ(inlen, fih + sizeof(in.body.release));
562 EXPECT_EQ((size_t)buflen, inlen);
566 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
567 EXPECT_EQ((size_t)buflen, inlen);
570 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
571 "Missing request body";
572 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
573 "Missing request attribute name";
574 // No redundant information for checking buflen
577 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
578 "Missing request body";
579 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
580 "Missing request attribute name";
581 // No redundant information for checking buflen
584 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
585 EXPECT_EQ((size_t)buflen, inlen);
587 case FUSE_REMOVEXATTR:
588 EXPECT_GT(inlen, fih) << "Missing request attribute name";
589 // No redundant information for checking buflen
592 EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
593 EXPECT_EQ((size_t)buflen, inlen);
596 EXPECT_EQ(inlen, fih + sizeof(in.body.init));
597 EXPECT_EQ((size_t)buflen, inlen);
600 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
601 EXPECT_EQ((size_t)buflen, inlen);
604 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
605 EXPECT_EQ((size_t)buflen, inlen);
607 case FUSE_RELEASEDIR:
608 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
609 EXPECT_EQ((size_t)buflen, inlen);
612 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
613 EXPECT_EQ((size_t)buflen, inlen);
617 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
618 EXPECT_EQ((size_t)buflen, inlen);
621 EXPECT_EQ(inlen, fih + sizeof(in.body.access));
622 EXPECT_EQ((size_t)buflen, inlen);
625 EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
626 "Missing request body";
627 EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
628 "Missing request filename";
629 // No redundant information for checking buflen
632 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
633 EXPECT_EQ((size_t)buflen, inlen);
636 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
637 EXPECT_EQ((size_t)buflen, inlen);
639 case FUSE_NOTIFY_REPLY:
640 case FUSE_BATCH_FORGET:
644 case FUSE_READDIRPLUS:
645 FAIL() << "Unsupported opcode?";
647 FAIL() << "Unknown opcode " << in.header.opcode;
651 void MockFS::init(uint32_t flags) {
654 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
655 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
657 read_request(*in, buflen);
658 audit_request(*in, buflen);
659 ASSERT_EQ(FUSE_INIT, in->header.opcode);
661 out->header.unique = in->header.unique;
662 out->header.error = 0;
663 out->body.init.major = FUSE_KERNEL_VERSION;
664 out->body.init.minor = m_kernel_minor_version;;
665 out->body.init.flags = in->body.init.flags & flags;
666 out->body.init.max_write = m_maxwrite;
667 out->body.init.max_readahead = m_maxreadahead;
669 if (m_kernel_minor_version < 23) {
670 SET_OUT_HEADER_LEN(*out, init_7_22);
672 out->body.init.time_gran = m_time_gran;
673 SET_OUT_HEADER_LEN(*out, init);
676 write(m_fuse_fd, out.get(), out->header.len);
679 void MockFS::kill_daemon() {
681 if (m_daemon_id != NULL)
682 pthread_kill(m_daemon_id, SIGUSR1);
683 // Closing the /dev/fuse file descriptor first allows unmount to
684 // succeed even if the daemon doesn't correctly respond to commands
685 // during the unmount sequence.
690 void MockFS::loop() {
691 std::vector<std::unique_ptr<mockfs_buf_out>> out;
693 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
694 ASSERT_TRUE(in != NULL);
698 bzero(in.get(), sizeof(*in));
699 read_request(*in, buflen);
703 debug_request(*in, buflen);
704 audit_request(*in, buflen);
705 if (pid_ok((pid_t)in->header.pid)) {
709 * Reject any requests from unknown processes. Because
710 * we actually do mount a filesystem, plenty of
711 * unrelated system daemons may try to access it.
714 printf("\tREJECTED (wrong pid %d)\n",
716 process_default(*in, out);
724 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
726 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
728 out->header.unique = 0; /* 0 means asynchronous notification */
729 out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
730 out->body.inval_entry.parent = parent;
731 out->body.inval_entry.namelen = namelen;
732 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
733 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
734 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
736 debug_response(*out);
737 write_response(*out);
741 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
743 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
745 out->header.unique = 0; /* 0 means asynchronous notification */
746 out->header.error = FUSE_NOTIFY_INVAL_INODE;
747 out->body.inval_inode.ino = ino;
748 out->body.inval_inode.off = off;
749 out->body.inval_inode.len = len;
750 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
751 debug_response(*out);
752 write_response(*out);
756 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
758 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
760 out->header.unique = 0; /* 0 means asynchronous notification */
761 out->header.error = FUSE_NOTIFY_STORE;
762 out->body.store.nodeid = ino;
763 out->body.store.offset = off;
764 out->body.store.size = size;
765 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
766 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
767 debug_response(*out);
768 write_response(*out);
772 bool MockFS::pid_ok(pid_t pid) {
775 } else if (pid == m_child_pid) {
778 struct kinfo_proc *ki;
781 ki = kinfo_getproc(pid);
785 * Allow access by the aio daemon processes so that our tests
786 * can use aio functions
788 if (0 == strncmp("aiod", ki->ki_comm, 4))
795 void MockFS::process_default(const mockfs_buf_in& in,
796 std::vector<std::unique_ptr<mockfs_buf_out>> &out)
798 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
799 out0->header.unique = in.header.unique;
800 out0->header.error = -EOPNOTSUPP;
801 out0->header.len = sizeof(out0->header);
802 out.push_back(std::move(out0));
805 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
809 struct kevent changes[1];
810 struct kevent events[1];
811 struct timespec timeout_ts;
812 struct timeval timeout_tv;
813 const int timeout_ms = 999;
814 int timeout_int, nfds;
820 timeout_ts.tv_sec = 0;
821 timeout_ts.tv_nsec = timeout_ms * 1'000'000;
822 while (nready == 0) {
823 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
825 nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
830 ASSERT_LE(0, nready) << strerror(errno);
831 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
832 if (events[0].flags & EV_ERROR)
833 FAIL() << strerror(events[0].data);
834 else if (events[0].flags & EV_EOF)
835 FAIL() << strerror(events[0].fflags);
836 m_nready = events[0].data;
839 timeout_int = timeout_ms;
840 fds[0].fd = m_fuse_fd;
841 fds[0].events = POLLIN;
842 while (nready == 0) {
843 nready = poll(fds, 1, timeout_int);
847 ASSERT_LE(0, nready) << strerror(errno);
848 ASSERT_TRUE(fds[0].revents & POLLIN);
851 timeout_tv.tv_sec = 0;
852 timeout_tv.tv_usec = timeout_ms * 1'000;
853 nfds = m_fuse_fd + 1;
854 while (nready == 0) {
856 FD_SET(m_fuse_fd, &readfds);
857 nready = select(nfds, &readfds, NULL, NULL,
862 ASSERT_LE(0, nready) << strerror(errno);
863 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
866 FAIL() << "not yet implemented";
868 res = read(m_fuse_fd, &in, sizeof(in));
870 if (res < 0 && !m_quit) {
872 FAIL() << "read: " << strerror(errno);
874 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
876 * Inconsistently, fuse_in_header.len is the size of the entire
877 * request,including header, even though fuse_out_header.len excludes
878 * the size of the header.
880 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
883 void MockFS::write_response(const mockfs_buf_out &out) {
891 case KQ: /* EVFILT_WRITE is not supported */
894 fds[0].fd = m_fuse_fd;
895 fds[0].events = POLLOUT;
896 nready = poll(fds, 1, INFTIM);
897 ASSERT_LE(0, nready) << strerror(errno);
898 ASSERT_EQ(1, nready) << "NULL timeout expired?";
899 ASSERT_TRUE(fds[0].revents & POLLOUT);
903 FD_SET(m_fuse_fd, &writefds);
904 nfds = m_fuse_fd + 1;
905 nready = select(nfds, NULL, &writefds, NULL, NULL);
906 ASSERT_LE(0, nready) << strerror(errno);
907 ASSERT_EQ(1, nready) << "NULL timeout expired?";
908 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
911 FAIL() << "not yet implemented";
913 r = write(m_fuse_fd, &out, out.header.len);
914 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
917 void* MockFS::service(void *pthr_data) {
918 MockFS *mock_fs = (MockFS*)pthr_data;
925 void MockFS::unmount() {
926 ::unmount("mountpoint", 0);