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_read, int max_readahead, bool allow_other, 420 bool default_permissions, 421 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 422 uint32_t kernel_minor_version, uint32_t max_write, bool async, 423 bool noclusterr, unsigned time_gran, bool nointr, bool noatime, 424 const char *fsname, const char *subtype) 425 : m_daemon_id(NULL), 426 m_kernel_minor_version(kernel_minor_version), 427 m_kq(pm == KQ ? kqueue() : -1), 428 m_maxread(max_read), 429 m_maxreadahead(max_readahead), 430 m_pid(getpid()), 431 m_uniques(new std::unordered_set<uint64_t>), 432 m_pm(pm), 433 m_time_gran(time_gran), 434 m_child_pid(-1), 435 m_maxwrite(MIN(max_write, max_max_write)), 436 m_nready(-1), 437 m_quit(false) 438 { 439 struct sigaction sa; 440 struct iovec *iov = NULL; 441 int iovlen = 0; 442 char fdstr[15]; 443 const bool trueval = true; 444 445 /* 446 * Kyua sets pwd to a testcase-unique tempdir; no need to use 447 * mkdtemp 448 */ 449 /* 450 * googletest doesn't allow ASSERT_ in constructors, so we must throw 451 * instead. 452 */ 453 if (mkdir("mountpoint" , 0755) && errno != EEXIST) 454 throw(std::system_error(errno, std::system_category(), 455 "Couldn't make mountpoint directory")); 456 457 switch (m_pm) { 458 case BLOCKING: 459 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 460 break; 461 default: 462 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 463 break; 464 } 465 if (m_fuse_fd < 0) 466 throw(std::system_error(errno, std::system_category(), 467 "Couldn't open /dev/fuse")); 468 469 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 470 build_iovec(&iov, &iovlen, "fspath", 471 __DECONST(void *, "mountpoint"), -1); 472 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 473 sprintf(fdstr, "%d", m_fuse_fd); 474 build_iovec(&iov, &iovlen, "fd", fdstr, -1); 475 if (m_maxread > 0) { 476 char val[10]; 477 478 snprintf(val, sizeof(val), "%d", m_maxread); 479 build_iovec(&iov, &iovlen, "max_read=", &val, -1); 480 } 481 if (allow_other) { 482 build_iovec(&iov, &iovlen, "allow_other", 483 __DECONST(void*, &trueval), sizeof(bool)); 484 } 485 if (default_permissions) { 486 build_iovec(&iov, &iovlen, "default_permissions", 487 __DECONST(void*, &trueval), sizeof(bool)); 488 } 489 if (push_symlinks_in) { 490 build_iovec(&iov, &iovlen, "push_symlinks_in", 491 __DECONST(void*, &trueval), sizeof(bool)); 492 } 493 if (ro) { 494 build_iovec(&iov, &iovlen, "ro", 495 __DECONST(void*, &trueval), sizeof(bool)); 496 } 497 if (async) { 498 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), 499 sizeof(bool)); 500 } 501 if (noatime) { 502 build_iovec(&iov, &iovlen, "noatime", 503 __DECONST(void*, &trueval), sizeof(bool)); 504 } 505 if (noclusterr) { 506 build_iovec(&iov, &iovlen, "noclusterr", 507 __DECONST(void*, &trueval), sizeof(bool)); 508 } 509 if (nointr) { 510 build_iovec(&iov, &iovlen, "nointr", 511 __DECONST(void*, &trueval), sizeof(bool)); 512 } else { 513 build_iovec(&iov, &iovlen, "intr", 514 __DECONST(void*, &trueval), sizeof(bool)); 515 } 516 if (*fsname) { 517 build_iovec(&iov, &iovlen, "fsname=", 518 __DECONST(void*, fsname), -1); 519 } 520 if (*subtype) { 521 build_iovec(&iov, &iovlen, "subtype=", 522 __DECONST(void*, subtype), -1); 523 } 524 if (nmount(iov, iovlen, 0)) 525 throw(std::system_error(errno, std::system_category(), 526 "Couldn't mount filesystem")); 527 free_iovec(&iov, &iovlen); 528 529 // Setup default handler 530 ON_CALL(*this, process(_, _)) 531 .WillByDefault(Invoke(this, &MockFS::process_default)); 532 533 init(flags); 534 bzero(&sa, sizeof(sa)); 535 sa.sa_handler = sigint_handler; 536 sa.sa_flags = 0; /* Don't set SA_RESTART! */ 537 if (0 != sigaction(SIGUSR1, &sa, NULL)) 538 throw(std::system_error(errno, std::system_category(), 539 "Couldn't handle SIGUSR1")); 540 if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 541 throw(std::system_error(errno, std::system_category(), 542 "Couldn't Couldn't start fuse thread")); 543 } 544 545 MockFS::~MockFS() { 546 kill_daemon(); 547 if (m_daemon_id != NULL) { 548 pthread_join(m_daemon_id, NULL); 549 m_daemon_id = NULL; 550 } 551 ::unmount("mountpoint", MNT_FORCE); 552 rmdir("mountpoint"); 553 if (m_kq >= 0) 554 close(m_kq); 555 } 556 557 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) { 558 uint32_t inlen = in.header.len; 559 size_t fih = sizeof(in.header); 560 switch (in.header.opcode) { 561 case FUSE_LOOKUP: 562 case FUSE_RMDIR: 563 case FUSE_SYMLINK: 564 case FUSE_UNLINK: 565 EXPECT_GT(inlen, fih) << "Missing request filename"; 566 // No redundant information for checking buflen 567 break; 568 case FUSE_FORGET: 569 EXPECT_EQ(inlen, fih + sizeof(in.body.forget)); 570 EXPECT_EQ((size_t)buflen, inlen); 571 break; 572 case FUSE_GETATTR: 573 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr)); 574 EXPECT_EQ((size_t)buflen, inlen); 575 break; 576 case FUSE_SETATTR: 577 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr)); 578 EXPECT_EQ((size_t)buflen, inlen); 579 break; 580 case FUSE_READLINK: 581 EXPECT_EQ(inlen, fih) << "Unexpected request body"; 582 EXPECT_EQ((size_t)buflen, inlen); 583 break; 584 case FUSE_MKNOD: 585 { 586 size_t s; 587 if (m_kernel_minor_version >= 12) 588 s = sizeof(in.body.mknod); 589 else 590 s = FUSE_COMPAT_MKNOD_IN_SIZE; 591 EXPECT_GE(inlen, fih + s) << "Missing request body"; 592 EXPECT_GT(inlen, fih + s) << "Missing request filename"; 593 // No redundant information for checking buflen 594 break; 595 } 596 case FUSE_MKDIR: 597 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) << 598 "Missing request body"; 599 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) << 600 "Missing request filename"; 601 // No redundant information for checking buflen 602 break; 603 case FUSE_RENAME: 604 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) << 605 "Missing request body"; 606 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) << 607 "Missing request filename"; 608 // No redundant information for checking buflen 609 break; 610 case FUSE_LINK: 611 EXPECT_GE(inlen, fih + sizeof(in.body.link)) << 612 "Missing request body"; 613 EXPECT_GT(inlen, fih + sizeof(in.body.link)) << 614 "Missing request filename"; 615 // No redundant information for checking buflen 616 break; 617 case FUSE_OPEN: 618 EXPECT_EQ(inlen, fih + sizeof(in.body.open)); 619 EXPECT_EQ((size_t)buflen, inlen); 620 break; 621 case FUSE_READ: 622 EXPECT_EQ(inlen, fih + sizeof(in.body.read)); 623 EXPECT_EQ((size_t)buflen, inlen); 624 break; 625 case FUSE_WRITE: 626 { 627 size_t s; 628 629 if (m_kernel_minor_version >= 9) 630 s = sizeof(in.body.write); 631 else 632 s = FUSE_COMPAT_WRITE_IN_SIZE; 633 // I suppose a 0-byte write should be allowed 634 EXPECT_GE(inlen, fih + s) << "Missing request body"; 635 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size); 636 break; 637 } 638 case FUSE_DESTROY: 639 case FUSE_STATFS: 640 EXPECT_EQ(inlen, fih); 641 EXPECT_EQ((size_t)buflen, inlen); 642 break; 643 case FUSE_RELEASE: 644 EXPECT_EQ(inlen, fih + sizeof(in.body.release)); 645 EXPECT_EQ((size_t)buflen, inlen); 646 break; 647 case FUSE_FSYNC: 648 case FUSE_FSYNCDIR: 649 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync)); 650 EXPECT_EQ((size_t)buflen, inlen); 651 break; 652 case FUSE_SETXATTR: 653 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) << 654 "Missing request body"; 655 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) << 656 "Missing request attribute name"; 657 // No redundant information for checking buflen 658 break; 659 case FUSE_GETXATTR: 660 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) << 661 "Missing request body"; 662 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) << 663 "Missing request attribute name"; 664 // No redundant information for checking buflen 665 break; 666 case FUSE_LISTXATTR: 667 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr)); 668 EXPECT_EQ((size_t)buflen, inlen); 669 break; 670 case FUSE_REMOVEXATTR: 671 EXPECT_GT(inlen, fih) << "Missing request attribute name"; 672 // No redundant information for checking buflen 673 break; 674 case FUSE_FLUSH: 675 EXPECT_EQ(inlen, fih + sizeof(in.body.flush)); 676 EXPECT_EQ((size_t)buflen, inlen); 677 break; 678 case FUSE_INIT: 679 EXPECT_EQ(inlen, fih + sizeof(in.body.init)); 680 EXPECT_EQ((size_t)buflen, inlen); 681 break; 682 case FUSE_OPENDIR: 683 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir)); 684 EXPECT_EQ((size_t)buflen, inlen); 685 break; 686 case FUSE_READDIR: 687 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir)); 688 EXPECT_EQ((size_t)buflen, inlen); 689 break; 690 case FUSE_RELEASEDIR: 691 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir)); 692 EXPECT_EQ((size_t)buflen, inlen); 693 break; 694 case FUSE_GETLK: 695 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk)); 696 EXPECT_EQ((size_t)buflen, inlen); 697 break; 698 case FUSE_SETLK: 699 case FUSE_SETLKW: 700 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk)); 701 EXPECT_EQ((size_t)buflen, inlen); 702 break; 703 case FUSE_ACCESS: 704 EXPECT_EQ(inlen, fih + sizeof(in.body.access)); 705 EXPECT_EQ((size_t)buflen, inlen); 706 break; 707 case FUSE_CREATE: 708 EXPECT_GE(inlen, fih + sizeof(in.body.create)) << 709 "Missing request body"; 710 EXPECT_GT(inlen, fih + sizeof(in.body.create)) << 711 "Missing request filename"; 712 // No redundant information for checking buflen 713 break; 714 case FUSE_INTERRUPT: 715 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt)); 716 EXPECT_EQ((size_t)buflen, inlen); 717 break; 718 case FUSE_FALLOCATE: 719 EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate)); 720 EXPECT_EQ((size_t)buflen, inlen); 721 break; 722 case FUSE_BMAP: 723 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap)); 724 EXPECT_EQ((size_t)buflen, inlen); 725 break; 726 case FUSE_LSEEK: 727 EXPECT_EQ(inlen, fih + sizeof(in.body.lseek)); 728 EXPECT_EQ((size_t)buflen, inlen); 729 break; 730 case FUSE_COPY_FILE_RANGE: 731 EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range)); 732 EXPECT_EQ(0ul, in.body.copy_file_range.flags); 733 EXPECT_EQ((size_t)buflen, inlen); 734 break; 735 case FUSE_NOTIFY_REPLY: 736 case FUSE_BATCH_FORGET: 737 case FUSE_IOCTL: 738 case FUSE_POLL: 739 case FUSE_READDIRPLUS: 740 FAIL() << "Unsupported opcode?"; 741 default: 742 FAIL() << "Unknown opcode " << in.header.opcode; 743 } 744 /* Verify that the ticket's unique value is actually unique. */ 745 if (m_uniques->find(in.header.unique) != m_uniques->end()) 746 FAIL() << "Non-unique \"unique\" value"; 747 m_uniques->insert(in.header.unique); 748 } 749 750 void MockFS::init(uint32_t flags) { 751 ssize_t buflen; 752 753 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 754 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 755 756 read_request(*in, buflen); 757 if (verbosity > 0) 758 debug_request(*in, buflen); 759 audit_request(*in, buflen); 760 ASSERT_EQ(FUSE_INIT, in->header.opcode); 761 762 out->header.unique = in->header.unique; 763 out->header.error = 0; 764 out->body.init.major = FUSE_KERNEL_VERSION; 765 out->body.init.minor = m_kernel_minor_version;; 766 out->body.init.flags = in->body.init.flags & flags; 767 out->body.init.max_write = m_maxwrite; 768 out->body.init.max_readahead = m_maxreadahead; 769 770 if (m_kernel_minor_version < 23) { 771 SET_OUT_HEADER_LEN(*out, init_7_22); 772 } else { 773 out->body.init.time_gran = m_time_gran; 774 SET_OUT_HEADER_LEN(*out, init); 775 } 776 777 write(m_fuse_fd, out.get(), out->header.len); 778 } 779 780 void MockFS::kill_daemon() { 781 m_quit = true; 782 if (m_daemon_id != NULL) 783 pthread_kill(m_daemon_id, SIGUSR1); 784 // Closing the /dev/fuse file descriptor first allows unmount to 785 // succeed even if the daemon doesn't correctly respond to commands 786 // during the unmount sequence. 787 close(m_fuse_fd); 788 m_fuse_fd = -1; 789 } 790 791 void MockFS::loop() { 792 std::vector<std::unique_ptr<mockfs_buf_out>> out; 793 794 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 795 ASSERT_TRUE(in != NULL); 796 while (!m_quit) { 797 ssize_t buflen; 798 799 bzero(in.get(), sizeof(*in)); 800 read_request(*in, buflen); 801 if (m_quit) 802 break; 803 if (verbosity > 0) 804 debug_request(*in, buflen); 805 audit_request(*in, buflen); 806 if (pid_ok((pid_t)in->header.pid)) { 807 process(*in, out); 808 } else { 809 /* 810 * Reject any requests from unknown processes. Because 811 * we actually do mount a filesystem, plenty of 812 * unrelated system daemons may try to access it. 813 */ 814 if (verbosity > 1) 815 printf("\tREJECTED (wrong pid %d)\n", 816 in->header.pid); 817 process_default(*in, out); 818 } 819 for (auto &it: out) 820 write_response(*it); 821 out.clear(); 822 } 823 } 824 825 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) 826 { 827 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 828 829 out->header.unique = 0; /* 0 means asynchronous notification */ 830 out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 831 out->body.inval_entry.parent = parent; 832 out->body.inval_entry.namelen = namelen; 833 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 834 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 835 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 836 namelen; 837 debug_response(*out); 838 write_response(*out); 839 return 0; 840 } 841 842 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len) 843 { 844 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 845 846 out->header.unique = 0; /* 0 means asynchronous notification */ 847 out->header.error = FUSE_NOTIFY_INVAL_INODE; 848 out->body.inval_inode.ino = ino; 849 out->body.inval_inode.off = off; 850 out->body.inval_inode.len = len; 851 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode); 852 debug_response(*out); 853 write_response(*out); 854 return 0; 855 } 856 857 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size) 858 { 859 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 860 861 out->header.unique = 0; /* 0 means asynchronous notification */ 862 out->header.error = FUSE_NOTIFY_STORE; 863 out->body.store.nodeid = ino; 864 out->body.store.offset = off; 865 out->body.store.size = size; 866 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size); 867 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size; 868 debug_response(*out); 869 write_response(*out); 870 return 0; 871 } 872 873 bool MockFS::pid_ok(pid_t pid) { 874 if (pid == m_pid) { 875 return (true); 876 } else if (pid == m_child_pid) { 877 return (true); 878 } else { 879 struct kinfo_proc *ki; 880 bool ok = false; 881 882 ki = kinfo_getproc(pid); 883 if (ki == NULL) 884 return (false); 885 /* 886 * Allow access by the aio daemon processes so that our tests 887 * can use aio functions 888 */ 889 if (0 == strncmp("aiod", ki->ki_comm, 4)) 890 ok = true; 891 free(ki); 892 return (ok); 893 } 894 } 895 896 void MockFS::process_default(const mockfs_buf_in& in, 897 std::vector<std::unique_ptr<mockfs_buf_out>> &out) 898 { 899 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 900 out0->header.unique = in.header.unique; 901 out0->header.error = -EOPNOTSUPP; 902 out0->header.len = sizeof(out0->header); 903 out.push_back(std::move(out0)); 904 } 905 906 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) { 907 int nready = 0; 908 fd_set readfds; 909 pollfd fds[1]; 910 struct kevent changes[1]; 911 struct kevent events[1]; 912 struct timespec timeout_ts; 913 struct timeval timeout_tv; 914 const int timeout_ms = 999; 915 int timeout_int, nfds; 916 int fuse_fd; 917 918 switch (m_pm) { 919 case BLOCKING: 920 break; 921 case KQ: 922 timeout_ts.tv_sec = 0; 923 timeout_ts.tv_nsec = timeout_ms * 1'000'000; 924 while (nready == 0) { 925 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, 926 EV_ADD | EV_ONESHOT, 0, 0, 0); 927 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 928 &timeout_ts); 929 if (m_quit) 930 return; 931 } 932 ASSERT_LE(0, nready) << strerror(errno); 933 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 934 if (events[0].flags & EV_ERROR) 935 FAIL() << strerror(events[0].data); 936 else if (events[0].flags & EV_EOF) 937 FAIL() << strerror(events[0].fflags); 938 m_nready = events[0].data; 939 break; 940 case POLL: 941 timeout_int = timeout_ms; 942 fds[0].fd = m_fuse_fd; 943 fds[0].events = POLLIN; 944 while (nready == 0) { 945 nready = poll(fds, 1, timeout_int); 946 if (m_quit) 947 return; 948 } 949 ASSERT_LE(0, nready) << strerror(errno); 950 ASSERT_TRUE(fds[0].revents & POLLIN); 951 break; 952 case SELECT: 953 fuse_fd = m_fuse_fd; 954 if (fuse_fd < 0) 955 break; 956 timeout_tv.tv_sec = 0; 957 timeout_tv.tv_usec = timeout_ms * 1'000; 958 nfds = fuse_fd + 1; 959 while (nready == 0) { 960 FD_ZERO(&readfds); 961 FD_SET(fuse_fd, &readfds); 962 nready = select(nfds, &readfds, NULL, NULL, 963 &timeout_tv); 964 if (m_quit) 965 return; 966 } 967 ASSERT_LE(0, nready) << strerror(errno); 968 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds)); 969 break; 970 default: 971 FAIL() << "not yet implemented"; 972 } 973 res = read(m_fuse_fd, &in, sizeof(in)); 974 975 if (res < 0 && !m_quit) { 976 m_quit = true; 977 FAIL() << "read: " << strerror(errno); 978 } 979 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 980 /* 981 * Inconsistently, fuse_in_header.len is the size of the entire 982 * request,including header, even though fuse_out_header.len excludes 983 * the size of the header. 984 */ 985 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit); 986 } 987 988 void MockFS::write_response(const mockfs_buf_out &out) { 989 fd_set writefds; 990 pollfd fds[1]; 991 struct kevent changes[1]; 992 struct kevent events[1]; 993 int nready, nfds; 994 ssize_t r; 995 996 switch (m_pm) { 997 case BLOCKING: 998 break; 999 case KQ: 1000 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE, 1001 EV_ADD | EV_ONESHOT, 0, 0, 0); 1002 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 1003 NULL); 1004 ASSERT_LE(0, nready) << strerror(errno); 1005 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 1006 if (events[0].flags & EV_ERROR) 1007 FAIL() << strerror(events[0].data); 1008 else if (events[0].flags & EV_EOF) 1009 FAIL() << strerror(events[0].fflags); 1010 m_nready = events[0].data; 1011 break; 1012 case POLL: 1013 fds[0].fd = m_fuse_fd; 1014 fds[0].events = POLLOUT; 1015 nready = poll(fds, 1, INFTIM); 1016 ASSERT_LE(0, nready) << strerror(errno); 1017 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1018 ASSERT_TRUE(fds[0].revents & POLLOUT); 1019 break; 1020 case SELECT: 1021 FD_ZERO(&writefds); 1022 FD_SET(m_fuse_fd, &writefds); 1023 nfds = m_fuse_fd + 1; 1024 nready = select(nfds, NULL, &writefds, NULL, NULL); 1025 ASSERT_LE(0, nready) << strerror(errno); 1026 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1027 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 1028 break; 1029 default: 1030 FAIL() << "not yet implemented"; 1031 } 1032 r = write(m_fuse_fd, &out, out.header.len); 1033 if (out.expected_errno) { 1034 ASSERT_EQ(-1, r); 1035 ASSERT_EQ(out.expected_errno, errno) << strerror(errno); 1036 } else { 1037 if (r <= 0 && errno == EINVAL) { 1038 printf("Failed to write response. unique=%" PRIu64 1039 ":\n", out.header.unique); 1040 } 1041 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 1042 } 1043 } 1044 1045 void* MockFS::service(void *pthr_data) { 1046 MockFS *mock_fs = (MockFS*)pthr_data; 1047 1048 mock_fs->loop(); 1049 1050 return (NULL); 1051 } 1052 1053 void MockFS::unmount() { 1054 ::unmount("mountpoint", 0); 1055 } 1056