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 * $FreeBSD$ 31 */ 32 33 extern "C" { 34 #include <sys/param.h> 35 36 #include <sys/mount.h> 37 #include <sys/select.h> 38 #include <sys/stat.h> 39 #include <sys/uio.h> 40 #include <sys/user.h> 41 42 #include <fcntl.h> 43 #include <libutil.h> 44 #include <poll.h> 45 #include <pthread.h> 46 #include <signal.h> 47 #include <stdlib.h> 48 #include <unistd.h> 49 50 #include "mntopts.h" // for build_iovec 51 } 52 53 #include <cinttypes> 54 55 #include <gtest/gtest.h> 56 57 #include "mockfs.hh" 58 59 using namespace testing; 60 61 int verbosity = 0; 62 63 const char* opcode2opname(uint32_t opcode) 64 { 65 const int NUM_OPS = 39; 66 const char* table[NUM_OPS] = { 67 "Unknown (opcode 0)", 68 "LOOKUP", 69 "FORGET", 70 "GETATTR", 71 "SETATTR", 72 "READLINK", 73 "SYMLINK", 74 "Unknown (opcode 7)", 75 "MKNOD", 76 "MKDIR", 77 "UNLINK", 78 "RMDIR", 79 "RENAME", 80 "LINK", 81 "OPEN", 82 "READ", 83 "WRITE", 84 "STATFS", 85 "RELEASE", 86 "Unknown (opcode 19)", 87 "FSYNC", 88 "SETXATTR", 89 "GETXATTR", 90 "LISTXATTR", 91 "REMOVEXATTR", 92 "FLUSH", 93 "INIT", 94 "OPENDIR", 95 "READDIR", 96 "RELEASEDIR", 97 "FSYNCDIR", 98 "GETLK", 99 "SETLK", 100 "SETLKW", 101 "ACCESS", 102 "CREATE", 103 "INTERRUPT", 104 "BMAP", 105 "DESTROY" 106 }; 107 if (opcode >= NUM_OPS) 108 return ("Unknown (opcode > max)"); 109 else 110 return (table[opcode]); 111 } 112 113 ProcessMockerT 114 ReturnErrno(int error) 115 { 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)); 122 }); 123 } 124 125 /* Helper function used for returning negative cache entries for LOOKUP */ 126 ProcessMockerT 127 ReturnNegativeCache(const struct timespec *entry_valid) 128 { 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)); 139 }); 140 } 141 142 ProcessMockerT 143 ReturnImmediate(std::function<void(const mockfs_buf_in& in, 144 struct mockfs_buf_out &out)> f) 145 { 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; 149 f(in, *out0); 150 out.push_back(std::move(out0)); 151 }); 152 } 153 154 void sigint_handler(int __unused sig) { 155 // Don't do anything except interrupt the daemon's read(2) call 156 } 157 158 void MockFS::debug_request(const mockfs_buf_in &in) 159 { 160 printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode), 161 in.header.nodeid); 162 if (verbosity > 1) { 163 printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u", 164 in.header.uid, in.header.gid, in.header.pid, 165 in.header.unique, in.header.len); 166 } 167 switch (in.header.opcode) { 168 const char *name, *value; 169 170 case FUSE_ACCESS: 171 printf(" mask=%#x", in.body.access.mask); 172 break; 173 case FUSE_BMAP: 174 printf(" block=%" PRIx64 " blocksize=%#x", 175 in.body.bmap.block, in.body.bmap.blocksize); 176 break; 177 case FUSE_CREATE: 178 if (m_kernel_minor_version >= 12) 179 name = (const char*)in.body.bytes + 180 sizeof(fuse_create_in); 181 else 182 name = (const char*)in.body.bytes + 183 sizeof(fuse_open_in); 184 printf(" flags=%#x name=%s", 185 in.body.open.flags, name); 186 break; 187 case FUSE_FLUSH: 188 printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64, 189 in.body.flush.fh, 190 in.body.flush.lock_owner); 191 break; 192 case FUSE_FORGET: 193 printf(" nlookup=%" PRIu64, in.body.forget.nlookup); 194 break; 195 case FUSE_FSYNC: 196 printf(" flags=%#x", in.body.fsync.fsync_flags); 197 break; 198 case FUSE_FSYNCDIR: 199 printf(" flags=%#x", in.body.fsyncdir.fsync_flags); 200 break; 201 case FUSE_INTERRUPT: 202 printf(" unique=%" PRIu64, in.body.interrupt.unique); 203 break; 204 case FUSE_LINK: 205 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 206 break; 207 case FUSE_LISTXATTR: 208 printf(" size=%" PRIu32, in.body.listxattr.size); 209 break; 210 case FUSE_LOOKUP: 211 printf(" %s", in.body.lookup); 212 break; 213 case FUSE_MKDIR: 214 name = (const char*)in.body.bytes + 215 sizeof(fuse_mkdir_in); 216 printf(" name=%s mode=%#o umask=%#o", name, 217 in.body.mkdir.mode, in.body.mkdir.umask); 218 break; 219 case FUSE_MKNOD: 220 if (m_kernel_minor_version >= 12) 221 name = (const char*)in.body.bytes + 222 sizeof(fuse_mknod_in); 223 else 224 name = (const char*)in.body.bytes + 225 FUSE_COMPAT_MKNOD_IN_SIZE; 226 printf(" mode=%#o rdev=%x umask=%#o name=%s", 227 in.body.mknod.mode, in.body.mknod.rdev, 228 in.body.mknod.umask, name); 229 break; 230 case FUSE_OPEN: 231 printf(" flags=%#x", in.body.open.flags); 232 break; 233 case FUSE_OPENDIR: 234 printf(" flags=%#x", in.body.opendir.flags); 235 break; 236 case FUSE_READ: 237 printf(" offset=%" PRIu64 " size=%u", 238 in.body.read.offset, 239 in.body.read.size); 240 if (verbosity > 1) 241 printf(" flags=%#x", in.body.read.flags); 242 break; 243 case FUSE_READDIR: 244 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 245 in.body.readdir.fh, in.body.readdir.offset, 246 in.body.readdir.size); 247 break; 248 case FUSE_RELEASE: 249 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 250 in.body.release.fh, 251 in.body.release.flags, 252 in.body.release.lock_owner); 253 break; 254 case FUSE_SETATTR: 255 if (verbosity <= 1) { 256 printf(" valid=%#x", in.body.setattr.valid); 257 break; 258 } 259 if (in.body.setattr.valid & FATTR_MODE) 260 printf(" mode=%#o", in.body.setattr.mode); 261 if (in.body.setattr.valid & FATTR_UID) 262 printf(" uid=%u", in.body.setattr.uid); 263 if (in.body.setattr.valid & FATTR_GID) 264 printf(" gid=%u", in.body.setattr.gid); 265 if (in.body.setattr.valid & FATTR_SIZE) 266 printf(" size=%" PRIu64, in.body.setattr.size); 267 if (in.body.setattr.valid & FATTR_ATIME) 268 printf(" atime=%" PRIu64 ".%u", 269 in.body.setattr.atime, 270 in.body.setattr.atimensec); 271 if (in.body.setattr.valid & FATTR_MTIME) 272 printf(" mtime=%" PRIu64 ".%u", 273 in.body.setattr.mtime, 274 in.body.setattr.mtimensec); 275 if (in.body.setattr.valid & FATTR_FH) 276 printf(" fh=%" PRIu64 "", in.body.setattr.fh); 277 break; 278 case FUSE_SETLK: 279 printf(" fh=%#" PRIx64 " owner=%" PRIu64 280 " type=%u pid=%u", 281 in.body.setlk.fh, in.body.setlk.owner, 282 in.body.setlk.lk.type, 283 in.body.setlk.lk.pid); 284 if (verbosity >= 2) { 285 printf(" range=[%" PRIu64 "-%" PRIu64 "]", 286 in.body.setlk.lk.start, 287 in.body.setlk.lk.end); 288 } 289 break; 290 case FUSE_SETXATTR: 291 /* 292 * In theory neither the xattr name and value need be 293 * ASCII, but in this test suite they always are. 294 */ 295 name = (const char*)in.body.bytes + 296 sizeof(fuse_setxattr_in); 297 value = name + strlen(name) + 1; 298 printf(" %s=%s", name, value); 299 break; 300 case FUSE_WRITE: 301 printf(" fh=%#" PRIx64 " offset=%" PRIu64 302 " size=%u write_flags=%u", 303 in.body.write.fh, 304 in.body.write.offset, in.body.write.size, 305 in.body.write.write_flags); 306 if (verbosity > 1) 307 printf(" flags=%#x", in.body.write.flags); 308 break; 309 default: 310 break; 311 } 312 printf("\n"); 313 } 314 315 /* 316 * Debug a FUSE response. 317 * 318 * This is mostly useful for asynchronous notifications, which don't correspond 319 * to any request 320 */ 321 void MockFS::debug_response(const mockfs_buf_out &out) { 322 const char *name; 323 324 if (verbosity == 0) 325 return; 326 327 switch (out.header.error) { 328 case FUSE_NOTIFY_INVAL_ENTRY: 329 name = (const char*)out.body.bytes + 330 sizeof(fuse_notify_inval_entry_out); 331 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 332 out.body.inval_entry.parent, name); 333 break; 334 case FUSE_NOTIFY_INVAL_INODE: 335 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64 336 " len=%" PRIi64 "\n", 337 out.body.inval_inode.ino, 338 out.body.inval_inode.off, 339 out.body.inval_inode.len); 340 break; 341 case FUSE_NOTIFY_STORE: 342 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64 343 " size=%" PRIu32 "\n", 344 out.body.store.nodeid, 345 out.body.store.offset, 346 out.body.store.size); 347 break; 348 default: 349 break; 350 } 351 } 352 353 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, 354 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 355 uint32_t kernel_minor_version, uint32_t max_write, bool async, 356 bool noclusterr, unsigned time_gran, bool nointr) 357 { 358 struct sigaction sa; 359 struct iovec *iov = NULL; 360 int iovlen = 0; 361 char fdstr[15]; 362 const bool trueval = true; 363 364 m_daemon_id = NULL; 365 m_kernel_minor_version = kernel_minor_version; 366 m_maxreadahead = max_readahead; 367 m_maxwrite = max_write; 368 m_nready = -1; 369 m_pm = pm; 370 m_time_gran = time_gran; 371 m_quit = false; 372 if (m_pm == KQ) 373 m_kq = kqueue(); 374 else 375 m_kq = -1; 376 377 /* 378 * Kyua sets pwd to a testcase-unique tempdir; no need to use 379 * mkdtemp 380 */ 381 /* 382 * googletest doesn't allow ASSERT_ in constructors, so we must throw 383 * instead. 384 */ 385 if (mkdir("mountpoint" , 0755) && errno != EEXIST) 386 throw(std::system_error(errno, std::system_category(), 387 "Couldn't make mountpoint directory")); 388 389 switch (m_pm) { 390 case BLOCKING: 391 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 392 break; 393 default: 394 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 395 break; 396 } 397 if (m_fuse_fd < 0) 398 throw(std::system_error(errno, std::system_category(), 399 "Couldn't open /dev/fuse")); 400 401 m_pid = getpid(); 402 m_child_pid = -1; 403 404 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 405 build_iovec(&iov, &iovlen, "fspath", 406 __DECONST(void *, "mountpoint"), -1); 407 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 408 sprintf(fdstr, "%d", m_fuse_fd); 409 build_iovec(&iov, &iovlen, "fd", fdstr, -1); 410 if (allow_other) { 411 build_iovec(&iov, &iovlen, "allow_other", 412 __DECONST(void*, &trueval), sizeof(bool)); 413 } 414 if (default_permissions) { 415 build_iovec(&iov, &iovlen, "default_permissions", 416 __DECONST(void*, &trueval), sizeof(bool)); 417 } 418 if (push_symlinks_in) { 419 build_iovec(&iov, &iovlen, "push_symlinks_in", 420 __DECONST(void*, &trueval), sizeof(bool)); 421 } 422 if (ro) { 423 build_iovec(&iov, &iovlen, "ro", 424 __DECONST(void*, &trueval), sizeof(bool)); 425 } 426 if (async) { 427 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), 428 sizeof(bool)); 429 } 430 if (noclusterr) { 431 build_iovec(&iov, &iovlen, "noclusterr", 432 __DECONST(void*, &trueval), sizeof(bool)); 433 } 434 if (nointr) { 435 build_iovec(&iov, &iovlen, "nointr", 436 __DECONST(void*, &trueval), sizeof(bool)); 437 } else { 438 build_iovec(&iov, &iovlen, "intr", 439 __DECONST(void*, &trueval), sizeof(bool)); 440 } 441 if (nmount(iov, iovlen, 0)) 442 throw(std::system_error(errno, std::system_category(), 443 "Couldn't mount filesystem")); 444 445 // Setup default handler 446 ON_CALL(*this, process(_, _)) 447 .WillByDefault(Invoke(this, &MockFS::process_default)); 448 449 init(flags); 450 bzero(&sa, sizeof(sa)); 451 sa.sa_handler = sigint_handler; 452 sa.sa_flags = 0; /* Don't set SA_RESTART! */ 453 if (0 != sigaction(SIGUSR1, &sa, NULL)) 454 throw(std::system_error(errno, std::system_category(), 455 "Couldn't handle SIGUSR1")); 456 if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 457 throw(std::system_error(errno, std::system_category(), 458 "Couldn't Couldn't start fuse thread")); 459 } 460 461 MockFS::~MockFS() { 462 kill_daemon(); 463 if (m_daemon_id != NULL) { 464 pthread_join(m_daemon_id, NULL); 465 m_daemon_id = NULL; 466 } 467 ::unmount("mountpoint", MNT_FORCE); 468 rmdir("mountpoint"); 469 if (m_kq >= 0) 470 close(m_kq); 471 } 472 473 void MockFS::audit_request(const mockfs_buf_in &in) { 474 uint32_t inlen = in.header.len; 475 size_t fih = sizeof(in.header); 476 switch (in.header.opcode) { 477 case FUSE_LOOKUP: 478 case FUSE_RMDIR: 479 case FUSE_SYMLINK: 480 case FUSE_UNLINK: 481 ASSERT_GT(inlen, fih) << "Missing request filename"; 482 break; 483 case FUSE_FORGET: 484 ASSERT_EQ(inlen, fih + sizeof(in.body.forget)); 485 break; 486 case FUSE_GETATTR: 487 ASSERT_EQ(inlen, fih + sizeof(in.body.getattr)); 488 break; 489 case FUSE_SETATTR: 490 ASSERT_EQ(inlen, fih + sizeof(in.body.setattr)); 491 break; 492 case FUSE_READLINK: 493 ASSERT_EQ(inlen, fih) << "Unexpected request body"; 494 break; 495 case FUSE_MKNOD: 496 { 497 size_t s; 498 if (m_kernel_minor_version >= 12) 499 s = sizeof(in.body.mknod); 500 else 501 s = FUSE_COMPAT_MKNOD_IN_SIZE; 502 ASSERT_GE(inlen, fih + s) << "Missing request body"; 503 ASSERT_GT(inlen, fih + s) << "Missing request filename"; 504 break; 505 } 506 case FUSE_MKDIR: 507 ASSERT_GE(inlen, fih + sizeof(in.body.mkdir)) << 508 "Missing request body"; 509 ASSERT_GT(inlen, fih + sizeof(in.body.mkdir)) << 510 "Missing request filename"; 511 break; 512 case FUSE_RENAME: 513 ASSERT_GE(inlen, fih + sizeof(in.body.rename)) << 514 "Missing request body"; 515 ASSERT_GT(inlen, fih + sizeof(in.body.rename)) << 516 "Missing request filename"; 517 break; 518 case FUSE_LINK: 519 ASSERT_GE(inlen, fih + sizeof(in.body.link)) << 520 "Missing request body"; 521 ASSERT_GT(inlen, fih + sizeof(in.body.link)) << 522 "Missing request filename"; 523 break; 524 case FUSE_OPEN: 525 ASSERT_EQ(inlen, fih + sizeof(in.body.open)); 526 break; 527 case FUSE_READ: 528 ASSERT_EQ(inlen, fih + sizeof(in.body.read)); 529 break; 530 case FUSE_WRITE: 531 { 532 size_t s; 533 534 if (m_kernel_minor_version >= 9) 535 s = sizeof(in.body.write); 536 else 537 s = FUSE_COMPAT_WRITE_IN_SIZE; 538 ASSERT_GE(inlen, fih + s) << "Missing request body"; 539 // I suppose a 0-byte write should be allowed 540 break; 541 } 542 case FUSE_DESTROY: 543 case FUSE_STATFS: 544 ASSERT_EQ(inlen, fih); 545 break; 546 case FUSE_RELEASE: 547 ASSERT_EQ(inlen, fih + sizeof(in.body.release)); 548 break; 549 case FUSE_FSYNC: 550 case FUSE_FSYNCDIR: 551 ASSERT_EQ(inlen, fih + sizeof(in.body.fsync)); 552 break; 553 case FUSE_SETXATTR: 554 ASSERT_GE(inlen, fih + sizeof(in.body.setxattr)) << 555 "Missing request body"; 556 ASSERT_GT(inlen, fih + sizeof(in.body.setxattr)) << 557 "Missing request attribute name"; 558 break; 559 case FUSE_GETXATTR: 560 ASSERT_GE(inlen, fih + sizeof(in.body.getxattr)) << 561 "Missing request body"; 562 ASSERT_GT(inlen, fih + sizeof(in.body.getxattr)) << 563 "Missing request attribute name"; 564 break; 565 case FUSE_LISTXATTR: 566 ASSERT_EQ(inlen, fih + sizeof(in.body.listxattr)); 567 break; 568 case FUSE_REMOVEXATTR: 569 ASSERT_GT(inlen, fih) << "Missing request attribute name"; 570 break; 571 case FUSE_FLUSH: 572 ASSERT_EQ(inlen, fih + sizeof(in.body.flush)); 573 break; 574 case FUSE_INIT: 575 ASSERT_EQ(inlen, fih + sizeof(in.body.init)); 576 break; 577 case FUSE_OPENDIR: 578 ASSERT_EQ(inlen, fih + sizeof(in.body.opendir)); 579 break; 580 case FUSE_READDIR: 581 ASSERT_EQ(inlen, fih + sizeof(in.body.readdir)); 582 break; 583 case FUSE_RELEASEDIR: 584 ASSERT_EQ(inlen, fih + sizeof(in.body.releasedir)); 585 break; 586 case FUSE_GETLK: 587 ASSERT_EQ(inlen, fih + sizeof(in.body.getlk)); 588 break; 589 case FUSE_SETLK: 590 case FUSE_SETLKW: 591 ASSERT_EQ(inlen, fih + sizeof(in.body.setlk)); 592 break; 593 case FUSE_ACCESS: 594 ASSERT_EQ(inlen, fih + sizeof(in.body.access)); 595 break; 596 case FUSE_CREATE: 597 ASSERT_GE(inlen, fih + sizeof(in.body.create)) << 598 "Missing request body"; 599 ASSERT_GT(inlen, fih + sizeof(in.body.create)) << 600 "Missing request filename"; 601 break; 602 case FUSE_INTERRUPT: 603 ASSERT_EQ(inlen, fih + sizeof(in.body.interrupt)); 604 break; 605 case FUSE_BMAP: 606 ASSERT_EQ(inlen, fih + sizeof(in.body.bmap)); 607 break; 608 case FUSE_NOTIFY_REPLY: 609 case FUSE_BATCH_FORGET: 610 case FUSE_FALLOCATE: 611 case FUSE_IOCTL: 612 case FUSE_POLL: 613 case FUSE_READDIRPLUS: 614 FAIL() << "Unsupported opcode?"; 615 default: 616 FAIL() << "Unknown opcode " << in.header.opcode; 617 } 618 } 619 620 void MockFS::init(uint32_t flags) { 621 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 622 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 623 624 read_request(*in); 625 ASSERT_EQ(FUSE_INIT, in->header.opcode); 626 627 out->header.unique = in->header.unique; 628 out->header.error = 0; 629 out->body.init.major = FUSE_KERNEL_VERSION; 630 out->body.init.minor = m_kernel_minor_version;; 631 out->body.init.flags = in->body.init.flags & flags; 632 out->body.init.max_write = m_maxwrite; 633 out->body.init.max_readahead = m_maxreadahead; 634 635 if (m_kernel_minor_version < 23) { 636 SET_OUT_HEADER_LEN(*out, init_7_22); 637 } else { 638 out->body.init.time_gran = m_time_gran; 639 SET_OUT_HEADER_LEN(*out, init); 640 } 641 642 write(m_fuse_fd, out.get(), out->header.len); 643 } 644 645 void MockFS::kill_daemon() { 646 m_quit = true; 647 if (m_daemon_id != NULL) 648 pthread_kill(m_daemon_id, SIGUSR1); 649 // Closing the /dev/fuse file descriptor first allows unmount to 650 // succeed even if the daemon doesn't correctly respond to commands 651 // during the unmount sequence. 652 close(m_fuse_fd); 653 m_fuse_fd = -1; 654 } 655 656 void MockFS::loop() { 657 std::vector<std::unique_ptr<mockfs_buf_out>> out; 658 659 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 660 ASSERT_TRUE(in != NULL); 661 while (!m_quit) { 662 bzero(in.get(), sizeof(*in)); 663 read_request(*in); 664 if (m_quit) 665 break; 666 if (verbosity > 0) 667 debug_request(*in); 668 audit_request(*in); 669 if (pid_ok((pid_t)in->header.pid)) { 670 process(*in, out); 671 } else { 672 /* 673 * Reject any requests from unknown processes. Because 674 * we actually do mount a filesystem, plenty of 675 * unrelated system daemons may try to access it. 676 */ 677 if (verbosity > 1) 678 printf("\tREJECTED (wrong pid %d)\n", 679 in->header.pid); 680 process_default(*in, out); 681 } 682 for (auto &it: out) 683 write_response(*it); 684 out.clear(); 685 } 686 } 687 688 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) 689 { 690 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 691 692 out->header.unique = 0; /* 0 means asynchronous notification */ 693 out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 694 out->body.inval_entry.parent = parent; 695 out->body.inval_entry.namelen = namelen; 696 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 697 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 698 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 699 namelen; 700 debug_response(*out); 701 write_response(*out); 702 return 0; 703 } 704 705 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len) 706 { 707 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 708 709 out->header.unique = 0; /* 0 means asynchronous notification */ 710 out->header.error = FUSE_NOTIFY_INVAL_INODE; 711 out->body.inval_inode.ino = ino; 712 out->body.inval_inode.off = off; 713 out->body.inval_inode.len = len; 714 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode); 715 debug_response(*out); 716 write_response(*out); 717 return 0; 718 } 719 720 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size) 721 { 722 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 723 724 out->header.unique = 0; /* 0 means asynchronous notification */ 725 out->header.error = FUSE_NOTIFY_STORE; 726 out->body.store.nodeid = ino; 727 out->body.store.offset = off; 728 out->body.store.size = size; 729 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size); 730 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size; 731 debug_response(*out); 732 write_response(*out); 733 return 0; 734 } 735 736 bool MockFS::pid_ok(pid_t pid) { 737 if (pid == m_pid) { 738 return (true); 739 } else if (pid == m_child_pid) { 740 return (true); 741 } else { 742 struct kinfo_proc *ki; 743 bool ok = false; 744 745 ki = kinfo_getproc(pid); 746 if (ki == NULL) 747 return (false); 748 /* 749 * Allow access by the aio daemon processes so that our tests 750 * can use aio functions 751 */ 752 if (0 == strncmp("aiod", ki->ki_comm, 4)) 753 ok = true; 754 free(ki); 755 return (ok); 756 } 757 } 758 759 void MockFS::process_default(const mockfs_buf_in& in, 760 std::vector<std::unique_ptr<mockfs_buf_out>> &out) 761 { 762 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 763 out0->header.unique = in.header.unique; 764 out0->header.error = -EOPNOTSUPP; 765 out0->header.len = sizeof(out0->header); 766 out.push_back(std::move(out0)); 767 } 768 769 void MockFS::read_request(mockfs_buf_in &in) { 770 ssize_t res; 771 int nready = 0; 772 fd_set readfds; 773 pollfd fds[1]; 774 struct kevent changes[1]; 775 struct kevent events[1]; 776 struct timespec timeout_ts; 777 struct timeval timeout_tv; 778 const int timeout_ms = 999; 779 int timeout_int, nfds; 780 781 switch (m_pm) { 782 case BLOCKING: 783 break; 784 case KQ: 785 timeout_ts.tv_sec = 0; 786 timeout_ts.tv_nsec = timeout_ms * 1'000'000; 787 while (nready == 0) { 788 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0, 789 0, 0); 790 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 791 &timeout_ts); 792 if (m_quit) 793 return; 794 } 795 ASSERT_LE(0, nready) << strerror(errno); 796 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 797 if (events[0].flags & EV_ERROR) 798 FAIL() << strerror(events[0].data); 799 else if (events[0].flags & EV_EOF) 800 FAIL() << strerror(events[0].fflags); 801 m_nready = events[0].data; 802 break; 803 case POLL: 804 timeout_int = timeout_ms; 805 fds[0].fd = m_fuse_fd; 806 fds[0].events = POLLIN; 807 while (nready == 0) { 808 nready = poll(fds, 1, timeout_int); 809 if (m_quit) 810 return; 811 } 812 ASSERT_LE(0, nready) << strerror(errno); 813 ASSERT_TRUE(fds[0].revents & POLLIN); 814 break; 815 case SELECT: 816 timeout_tv.tv_sec = 0; 817 timeout_tv.tv_usec = timeout_ms * 1'000; 818 nfds = m_fuse_fd + 1; 819 while (nready == 0) { 820 FD_ZERO(&readfds); 821 FD_SET(m_fuse_fd, &readfds); 822 nready = select(nfds, &readfds, NULL, NULL, 823 &timeout_tv); 824 if (m_quit) 825 return; 826 } 827 ASSERT_LE(0, nready) << strerror(errno); 828 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds)); 829 break; 830 default: 831 FAIL() << "not yet implemented"; 832 } 833 res = read(m_fuse_fd, &in, sizeof(in)); 834 835 if (res < 0 && !m_quit) { 836 FAIL() << "read: " << strerror(errno); 837 m_quit = true; 838 } 839 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 840 /* 841 * Inconsistently, fuse_in_header.len is the size of the entire 842 * request,including header, even though fuse_out_header.len excludes 843 * the size of the header. 844 */ 845 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit); 846 } 847 848 void MockFS::write_response(const mockfs_buf_out &out) { 849 fd_set writefds; 850 pollfd fds[1]; 851 int nready, nfds; 852 ssize_t r; 853 854 switch (m_pm) { 855 case BLOCKING: 856 case KQ: /* EVFILT_WRITE is not supported */ 857 break; 858 case POLL: 859 fds[0].fd = m_fuse_fd; 860 fds[0].events = POLLOUT; 861 nready = poll(fds, 1, INFTIM); 862 ASSERT_LE(0, nready) << strerror(errno); 863 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 864 ASSERT_TRUE(fds[0].revents & POLLOUT); 865 break; 866 case SELECT: 867 FD_ZERO(&writefds); 868 FD_SET(m_fuse_fd, &writefds); 869 nfds = m_fuse_fd + 1; 870 nready = select(nfds, NULL, &writefds, NULL, NULL); 871 ASSERT_LE(0, nready) << strerror(errno); 872 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 873 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 874 break; 875 default: 876 FAIL() << "not yet implemented"; 877 } 878 r = write(m_fuse_fd, &out, out.header.len); 879 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 880 } 881 882 void* MockFS::service(void *pthr_data) { 883 MockFS *mock_fs = (MockFS*)pthr_data; 884 885 mock_fs->loop(); 886 887 return (NULL); 888 } 889 890 void MockFS::unmount() { 891 ::unmount("mountpoint", 0); 892 } 893