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