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