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