1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 The FreeBSD Foundation 5 * 6 * This software was developed by BFF Storage Systems, LLC under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 extern "C" { 32 #include <sys/param.h> 33 34 #include <sys/mount.h> 35 #include <sys/select.h> 36 #include <sys/stat.h> 37 #include <sys/uio.h> 38 #include <sys/user.h> 39 40 #include <fcntl.h> 41 #include <libutil.h> 42 #include <poll.h> 43 #include <pthread.h> 44 #include <signal.h> 45 #include <stdlib.h> 46 #include <unistd.h> 47 48 #include "mntopts.h" // for build_iovec 49 } 50 51 #include <cinttypes> 52 53 #include <gtest/gtest.h> 54 55 #include "mockfs.hh" 56 57 using namespace testing; 58 59 int verbosity = 0; 60 61 const char* opcode2opname(uint32_t opcode) 62 { 63 const int NUM_OPS = 39; 64 const char* table[NUM_OPS] = { 65 "Unknown (opcode 0)", 66 "LOOKUP", 67 "FORGET", 68 "GETATTR", 69 "SETATTR", 70 "READLINK", 71 "SYMLINK", 72 "Unknown (opcode 7)", 73 "MKNOD", 74 "MKDIR", 75 "UNLINK", 76 "RMDIR", 77 "RENAME", 78 "LINK", 79 "OPEN", 80 "READ", 81 "WRITE", 82 "STATFS", 83 "RELEASE", 84 "Unknown (opcode 19)", 85 "FSYNC", 86 "SETXATTR", 87 "GETXATTR", 88 "LISTXATTR", 89 "REMOVEXATTR", 90 "FLUSH", 91 "INIT", 92 "OPENDIR", 93 "READDIR", 94 "RELEASEDIR", 95 "FSYNCDIR", 96 "GETLK", 97 "SETLK", 98 "SETLKW", 99 "ACCESS", 100 "CREATE", 101 "INTERRUPT", 102 "BMAP", 103 "DESTROY" 104 }; 105 if (opcode >= NUM_OPS) 106 return ("Unknown (opcode > max)"); 107 else 108 return (table[opcode]); 109 } 110 111 ProcessMockerT 112 ReturnErrno(int error) 113 { 114 return([=](auto in, auto &out) { 115 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 116 out0->header.unique = in.header.unique; 117 out0->header.error = -error; 118 out0->header.len = sizeof(out0->header); 119 out.push_back(std::move(out0)); 120 }); 121 } 122 123 /* Helper function used for returning negative cache entries for LOOKUP */ 124 ProcessMockerT 125 ReturnNegativeCache(const struct timespec *entry_valid) 126 { 127 return([=](auto in, auto &out) { 128 /* nodeid means ENOENT and cache it */ 129 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 130 out0->body.entry.nodeid = 0; 131 out0->header.unique = in.header.unique; 132 out0->header.error = 0; 133 out0->body.entry.entry_valid = entry_valid->tv_sec; 134 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec; 135 SET_OUT_HEADER_LEN(*out0, entry); 136 out.push_back(std::move(out0)); 137 }); 138 } 139 140 ProcessMockerT 141 ReturnImmediate(std::function<void(const mockfs_buf_in& in, 142 struct mockfs_buf_out &out)> f) 143 { 144 return([=](auto& in, auto &out) { 145 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 146 out0->header.unique = in.header.unique; 147 f(in, *out0); 148 out.push_back(std::move(out0)); 149 }); 150 } 151 152 void sigint_handler(int __unused sig) { 153 // Don't do anything except interrupt the daemon's read(2) call 154 } 155 156 void MockFS::debug_request(const mockfs_buf_in &in) 157 { 158 printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode), 159 in.header.nodeid); 160 if (verbosity > 1) { 161 printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u", 162 in.header.uid, in.header.gid, in.header.pid, 163 in.header.unique, in.header.len); 164 } 165 switch (in.header.opcode) { 166 const char *name, *value; 167 168 case FUSE_ACCESS: 169 printf(" mask=%#x", in.body.access.mask); 170 break; 171 case FUSE_BMAP: 172 printf(" block=%" PRIx64 " blocksize=%#x", 173 in.body.bmap.block, in.body.bmap.blocksize); 174 break; 175 case FUSE_CREATE: 176 if (m_kernel_minor_version >= 12) 177 name = (const char*)in.body.bytes + 178 sizeof(fuse_create_in); 179 else 180 name = (const char*)in.body.bytes + 181 sizeof(fuse_open_in); 182 printf(" flags=%#x name=%s", 183 in.body.open.flags, name); 184 break; 185 case FUSE_FLUSH: 186 printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64, 187 in.body.flush.fh, 188 in.body.flush.lock_owner); 189 break; 190 case FUSE_FORGET: 191 printf(" nlookup=%" PRIu64, in.body.forget.nlookup); 192 break; 193 case FUSE_FSYNC: 194 printf(" flags=%#x", in.body.fsync.fsync_flags); 195 break; 196 case FUSE_FSYNCDIR: 197 printf(" flags=%#x", in.body.fsyncdir.fsync_flags); 198 break; 199 case FUSE_INTERRUPT: 200 printf(" unique=%" PRIu64, in.body.interrupt.unique); 201 break; 202 case FUSE_LINK: 203 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 204 break; 205 case FUSE_LOOKUP: 206 printf(" %s", in.body.lookup); 207 break; 208 case FUSE_MKDIR: 209 name = (const char*)in.body.bytes + 210 sizeof(fuse_mkdir_in); 211 printf(" name=%s mode=%#o umask=%#o", name, 212 in.body.mkdir.mode, in.body.mkdir.umask); 213 break; 214 case FUSE_MKNOD: 215 if (m_kernel_minor_version >= 12) 216 name = (const char*)in.body.bytes + 217 sizeof(fuse_mknod_in); 218 else 219 name = (const char*)in.body.bytes + 220 FUSE_COMPAT_MKNOD_IN_SIZE; 221 printf(" mode=%#o rdev=%x umask=%#o name=%s", 222 in.body.mknod.mode, in.body.mknod.rdev, 223 in.body.mknod.umask, name); 224 break; 225 case FUSE_OPEN: 226 printf(" flags=%#x", in.body.open.flags); 227 break; 228 case FUSE_OPENDIR: 229 printf(" flags=%#x", in.body.opendir.flags); 230 break; 231 case FUSE_READ: 232 printf(" offset=%" PRIu64 " size=%u", 233 in.body.read.offset, 234 in.body.read.size); 235 if (verbosity > 1) 236 printf(" flags=%#x", in.body.read.flags); 237 break; 238 case FUSE_READDIR: 239 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 240 in.body.readdir.fh, in.body.readdir.offset, 241 in.body.readdir.size); 242 break; 243 case FUSE_RELEASE: 244 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 245 in.body.release.fh, 246 in.body.release.flags, 247 in.body.release.lock_owner); 248 break; 249 case FUSE_SETATTR: 250 if (verbosity <= 1) { 251 printf(" valid=%#x", in.body.setattr.valid); 252 break; 253 } 254 if (in.body.setattr.valid & FATTR_MODE) 255 printf(" mode=%#o", in.body.setattr.mode); 256 if (in.body.setattr.valid & FATTR_UID) 257 printf(" uid=%u", in.body.setattr.uid); 258 if (in.body.setattr.valid & FATTR_GID) 259 printf(" gid=%u", in.body.setattr.gid); 260 if (in.body.setattr.valid & FATTR_SIZE) 261 printf(" size=%" PRIu64, in.body.setattr.size); 262 if (in.body.setattr.valid & FATTR_ATIME) 263 printf(" atime=%" PRIu64 ".%u", 264 in.body.setattr.atime, 265 in.body.setattr.atimensec); 266 if (in.body.setattr.valid & FATTR_MTIME) 267 printf(" mtime=%" PRIu64 ".%u", 268 in.body.setattr.mtime, 269 in.body.setattr.mtimensec); 270 if (in.body.setattr.valid & FATTR_FH) 271 printf(" fh=%" PRIu64 "", in.body.setattr.fh); 272 break; 273 case FUSE_SETLK: 274 printf(" fh=%#" PRIx64 " owner=%" PRIu64 275 " type=%u pid=%u", 276 in.body.setlk.fh, in.body.setlk.owner, 277 in.body.setlk.lk.type, 278 in.body.setlk.lk.pid); 279 if (verbosity >= 2) { 280 printf(" range=[%" PRIu64 "-%" PRIu64 "]", 281 in.body.setlk.lk.start, 282 in.body.setlk.lk.end); 283 } 284 break; 285 case FUSE_SETXATTR: 286 /* 287 * In theory neither the xattr name and value need be 288 * ASCII, but in this test suite they always are. 289 */ 290 name = (const char*)in.body.bytes + 291 sizeof(fuse_setxattr_in); 292 value = name + strlen(name) + 1; 293 printf(" %s=%s", name, value); 294 break; 295 case FUSE_WRITE: 296 printf(" fh=%#" PRIx64 " offset=%" PRIu64 297 " size=%u write_flags=%u", 298 in.body.write.fh, 299 in.body.write.offset, in.body.write.size, 300 in.body.write.write_flags); 301 if (verbosity > 1) 302 printf(" flags=%#x", in.body.write.flags); 303 break; 304 default: 305 break; 306 } 307 printf("\n"); 308 } 309 310 /* 311 * Debug a FUSE response. 312 * 313 * This is mostly useful for asynchronous notifications, which don't correspond 314 * to any request 315 */ 316 void MockFS::debug_response(const mockfs_buf_out &out) { 317 const char *name; 318 319 if (verbosity == 0) 320 return; 321 322 switch (out.header.error) { 323 case FUSE_NOTIFY_INVAL_ENTRY: 324 name = (const char*)out.body.bytes + 325 sizeof(fuse_notify_inval_entry_out); 326 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 327 out.body.inval_entry.parent, name); 328 break; 329 case FUSE_NOTIFY_INVAL_INODE: 330 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64 331 " len=%" PRIi64 "\n", 332 out.body.inval_inode.ino, 333 out.body.inval_inode.off, 334 out.body.inval_inode.len); 335 break; 336 case FUSE_NOTIFY_STORE: 337 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64 338 " size=%" PRIu32 "\n", 339 out.body.store.nodeid, 340 out.body.store.offset, 341 out.body.store.size); 342 break; 343 default: 344 break; 345 } 346 } 347 348 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, 349 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 350 uint32_t kernel_minor_version, uint32_t max_write, bool async, 351 bool noclusterr, unsigned time_gran, bool nointr) 352 { 353 struct sigaction sa; 354 struct iovec *iov = NULL; 355 int iovlen = 0; 356 char fdstr[15]; 357 const bool trueval = true; 358 359 m_daemon_id = NULL; 360 m_kernel_minor_version = kernel_minor_version; 361 m_maxreadahead = max_readahead; 362 m_maxwrite = max_write; 363 m_nready = -1; 364 m_pm = pm; 365 m_time_gran = time_gran; 366 m_quit = false; 367 if (m_pm == KQ) 368 m_kq = kqueue(); 369 else 370 m_kq = -1; 371 372 /* 373 * Kyua sets pwd to a testcase-unique tempdir; no need to use 374 * mkdtemp 375 */ 376 /* 377 * googletest doesn't allow ASSERT_ in constructors, so we must throw 378 * instead. 379 */ 380 if (mkdir("mountpoint" , 0755) && errno != EEXIST) 381 throw(std::system_error(errno, std::system_category(), 382 "Couldn't make mountpoint directory")); 383 384 switch (m_pm) { 385 case BLOCKING: 386 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 387 break; 388 default: 389 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 390 break; 391 } 392 if (m_fuse_fd < 0) 393 throw(std::system_error(errno, std::system_category(), 394 "Couldn't open /dev/fuse")); 395 396 m_pid = getpid(); 397 m_child_pid = -1; 398 399 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 400 build_iovec(&iov, &iovlen, "fspath", 401 __DECONST(void *, "mountpoint"), -1); 402 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 403 sprintf(fdstr, "%d", m_fuse_fd); 404 build_iovec(&iov, &iovlen, "fd", fdstr, -1); 405 if (allow_other) { 406 build_iovec(&iov, &iovlen, "allow_other", 407 __DECONST(void*, &trueval), sizeof(bool)); 408 } 409 if (default_permissions) { 410 build_iovec(&iov, &iovlen, "default_permissions", 411 __DECONST(void*, &trueval), sizeof(bool)); 412 } 413 if (push_symlinks_in) { 414 build_iovec(&iov, &iovlen, "push_symlinks_in", 415 __DECONST(void*, &trueval), sizeof(bool)); 416 } 417 if (ro) { 418 build_iovec(&iov, &iovlen, "ro", 419 __DECONST(void*, &trueval), sizeof(bool)); 420 } 421 if (async) { 422 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), 423 sizeof(bool)); 424 } 425 if (noclusterr) { 426 build_iovec(&iov, &iovlen, "noclusterr", 427 __DECONST(void*, &trueval), sizeof(bool)); 428 } 429 if (nointr) { 430 build_iovec(&iov, &iovlen, "nointr", 431 __DECONST(void*, &trueval), sizeof(bool)); 432 } else { 433 build_iovec(&iov, &iovlen, "intr", 434 __DECONST(void*, &trueval), sizeof(bool)); 435 } 436 if (nmount(iov, iovlen, 0)) 437 throw(std::system_error(errno, std::system_category(), 438 "Couldn't mount filesystem")); 439 440 // Setup default handler 441 ON_CALL(*this, process(_, _)) 442 .WillByDefault(Invoke(this, &MockFS::process_default)); 443 444 init(flags); 445 bzero(&sa, sizeof(sa)); 446 sa.sa_handler = sigint_handler; 447 sa.sa_flags = 0; /* Don't set SA_RESTART! */ 448 if (0 != sigaction(SIGUSR1, &sa, NULL)) 449 throw(std::system_error(errno, std::system_category(), 450 "Couldn't handle SIGUSR1")); 451 if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 452 throw(std::system_error(errno, std::system_category(), 453 "Couldn't Couldn't start fuse thread")); 454 } 455 456 MockFS::~MockFS() { 457 kill_daemon(); 458 if (m_daemon_id != NULL) { 459 pthread_join(m_daemon_id, NULL); 460 m_daemon_id = NULL; 461 } 462 ::unmount("mountpoint", MNT_FORCE); 463 rmdir("mountpoint"); 464 if (m_kq >= 0) 465 close(m_kq); 466 } 467 468 void MockFS::init(uint32_t flags) { 469 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 470 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 471 472 read_request(*in); 473 ASSERT_EQ(FUSE_INIT, in->header.opcode); 474 475 out->header.unique = in->header.unique; 476 out->header.error = 0; 477 out->body.init.major = FUSE_KERNEL_VERSION; 478 out->body.init.minor = m_kernel_minor_version;; 479 out->body.init.flags = in->body.init.flags & flags; 480 out->body.init.max_write = m_maxwrite; 481 out->body.init.max_readahead = m_maxreadahead; 482 483 if (m_kernel_minor_version < 23) { 484 SET_OUT_HEADER_LEN(*out, init_7_22); 485 } else { 486 out->body.init.time_gran = m_time_gran; 487 SET_OUT_HEADER_LEN(*out, init); 488 } 489 490 write(m_fuse_fd, out.get(), out->header.len); 491 } 492 493 void MockFS::kill_daemon() { 494 m_quit = true; 495 if (m_daemon_id != NULL) 496 pthread_kill(m_daemon_id, SIGUSR1); 497 // Closing the /dev/fuse file descriptor first allows unmount to 498 // succeed even if the daemon doesn't correctly respond to commands 499 // during the unmount sequence. 500 close(m_fuse_fd); 501 m_fuse_fd = -1; 502 } 503 504 void MockFS::loop() { 505 std::vector<std::unique_ptr<mockfs_buf_out>> out; 506 507 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 508 ASSERT_TRUE(in != NULL); 509 while (!m_quit) { 510 bzero(in.get(), sizeof(*in)); 511 read_request(*in); 512 if (m_quit) 513 break; 514 if (verbosity > 0) 515 debug_request(*in); 516 if (pid_ok((pid_t)in->header.pid)) { 517 process(*in, out); 518 } else { 519 /* 520 * Reject any requests from unknown processes. Because 521 * we actually do mount a filesystem, plenty of 522 * unrelated system daemons may try to access it. 523 */ 524 if (verbosity > 1) 525 printf("\tREJECTED (wrong pid %d)\n", 526 in->header.pid); 527 process_default(*in, out); 528 } 529 for (auto &it: out) 530 write_response(*it); 531 out.clear(); 532 } 533 } 534 535 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) 536 { 537 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 538 539 out->header.unique = 0; /* 0 means asynchronous notification */ 540 out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 541 out->body.inval_entry.parent = parent; 542 out->body.inval_entry.namelen = namelen; 543 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 544 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 545 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 546 namelen; 547 debug_response(*out); 548 write_response(*out); 549 return 0; 550 } 551 552 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len) 553 { 554 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 555 556 out->header.unique = 0; /* 0 means asynchronous notification */ 557 out->header.error = FUSE_NOTIFY_INVAL_INODE; 558 out->body.inval_inode.ino = ino; 559 out->body.inval_inode.off = off; 560 out->body.inval_inode.len = len; 561 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode); 562 debug_response(*out); 563 write_response(*out); 564 return 0; 565 } 566 567 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size) 568 { 569 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 570 571 out->header.unique = 0; /* 0 means asynchronous notification */ 572 out->header.error = FUSE_NOTIFY_STORE; 573 out->body.store.nodeid = ino; 574 out->body.store.offset = off; 575 out->body.store.size = size; 576 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size); 577 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size; 578 debug_response(*out); 579 write_response(*out); 580 return 0; 581 } 582 583 bool MockFS::pid_ok(pid_t pid) { 584 if (pid == m_pid) { 585 return (true); 586 } else if (pid == m_child_pid) { 587 return (true); 588 } else { 589 struct kinfo_proc *ki; 590 bool ok = false; 591 592 ki = kinfo_getproc(pid); 593 if (ki == NULL) 594 return (false); 595 /* 596 * Allow access by the aio daemon processes so that our tests 597 * can use aio functions 598 */ 599 if (0 == strncmp("aiod", ki->ki_comm, 4)) 600 ok = true; 601 free(ki); 602 return (ok); 603 } 604 } 605 606 void MockFS::process_default(const mockfs_buf_in& in, 607 std::vector<std::unique_ptr<mockfs_buf_out>> &out) 608 { 609 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 610 out0->header.unique = in.header.unique; 611 out0->header.error = -EOPNOTSUPP; 612 out0->header.len = sizeof(out0->header); 613 out.push_back(std::move(out0)); 614 } 615 616 void MockFS::read_request(mockfs_buf_in &in) { 617 ssize_t res; 618 int nready = 0; 619 fd_set readfds; 620 pollfd fds[1]; 621 struct kevent changes[1]; 622 struct kevent events[1]; 623 struct timespec timeout_ts; 624 struct timeval timeout_tv; 625 const int timeout_ms = 999; 626 int timeout_int, nfds; 627 628 switch (m_pm) { 629 case BLOCKING: 630 break; 631 case KQ: 632 timeout_ts.tv_sec = 0; 633 timeout_ts.tv_nsec = timeout_ms * 1'000'000; 634 while (nready == 0) { 635 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0, 636 0, 0); 637 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 638 &timeout_ts); 639 if (m_quit) 640 return; 641 } 642 ASSERT_LE(0, nready) << strerror(errno); 643 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 644 if (events[0].flags & EV_ERROR) 645 FAIL() << strerror(events[0].data); 646 else if (events[0].flags & EV_EOF) 647 FAIL() << strerror(events[0].fflags); 648 m_nready = events[0].data; 649 break; 650 case POLL: 651 timeout_int = timeout_ms; 652 fds[0].fd = m_fuse_fd; 653 fds[0].events = POLLIN; 654 while (nready == 0) { 655 nready = poll(fds, 1, timeout_int); 656 if (m_quit) 657 return; 658 } 659 ASSERT_LE(0, nready) << strerror(errno); 660 ASSERT_TRUE(fds[0].revents & POLLIN); 661 break; 662 case SELECT: 663 timeout_tv.tv_sec = 0; 664 timeout_tv.tv_usec = timeout_ms * 1'000; 665 nfds = m_fuse_fd + 1; 666 while (nready == 0) { 667 FD_ZERO(&readfds); 668 FD_SET(m_fuse_fd, &readfds); 669 nready = select(nfds, &readfds, NULL, NULL, 670 &timeout_tv); 671 if (m_quit) 672 return; 673 } 674 ASSERT_LE(0, nready) << strerror(errno); 675 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds)); 676 break; 677 default: 678 FAIL() << "not yet implemented"; 679 } 680 res = read(m_fuse_fd, &in, sizeof(in)); 681 682 if (res < 0 && !m_quit) { 683 FAIL() << "read: " << strerror(errno); 684 m_quit = true; 685 } 686 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 687 } 688 689 void MockFS::write_response(const mockfs_buf_out &out) { 690 fd_set writefds; 691 pollfd fds[1]; 692 int nready, nfds; 693 ssize_t r; 694 695 switch (m_pm) { 696 case BLOCKING: 697 case KQ: /* EVFILT_WRITE is not supported */ 698 break; 699 case POLL: 700 fds[0].fd = m_fuse_fd; 701 fds[0].events = POLLOUT; 702 nready = poll(fds, 1, INFTIM); 703 ASSERT_LE(0, nready) << strerror(errno); 704 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 705 ASSERT_TRUE(fds[0].revents & POLLOUT); 706 break; 707 case SELECT: 708 FD_ZERO(&writefds); 709 FD_SET(m_fuse_fd, &writefds); 710 nfds = m_fuse_fd + 1; 711 nready = select(nfds, NULL, &writefds, NULL, NULL); 712 ASSERT_LE(0, nready) << strerror(errno); 713 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 714 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 715 break; 716 default: 717 FAIL() << "not yet implemented"; 718 } 719 r = write(m_fuse_fd, &out, out.header.len); 720 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 721 } 722 723 void* MockFS::service(void *pthr_data) { 724 MockFS *mock_fs = (MockFS*)pthr_data; 725 726 mock_fs->loop(); 727 728 return (NULL); 729 } 730 731 void MockFS::unmount() { 732 ::unmount("mountpoint", 0); 733 } 734