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_LINK: 245 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 246 break; 247 case FUSE_LISTXATTR: 248 printf(" size=%" PRIu32, in.body.listxattr.size); 249 break; 250 case FUSE_LOOKUP: 251 printf(" %s", in.body.lookup); 252 break; 253 case FUSE_LSEEK: 254 switch (in.body.lseek.whence) { 255 case SEEK_HOLE: 256 printf(" SEEK_HOLE offset=%jd", 257 in.body.lseek.offset); 258 break; 259 case SEEK_DATA: 260 printf(" SEEK_DATA offset=%jd", 261 in.body.lseek.offset); 262 break; 263 default: 264 printf(" whence=%u offset=%jd", 265 in.body.lseek.whence, in.body.lseek.offset); 266 break; 267 } 268 break; 269 case FUSE_MKDIR: 270 name = (const char*)in.body.bytes + 271 sizeof(fuse_mkdir_in); 272 printf(" name=%s mode=%#o umask=%#o", name, 273 in.body.mkdir.mode, in.body.mkdir.umask); 274 break; 275 case FUSE_MKNOD: 276 if (m_kernel_minor_version >= 12) 277 name = (const char*)in.body.bytes + 278 sizeof(fuse_mknod_in); 279 else 280 name = (const char*)in.body.bytes + 281 FUSE_COMPAT_MKNOD_IN_SIZE; 282 printf(" mode=%#o rdev=%x umask=%#o name=%s", 283 in.body.mknod.mode, in.body.mknod.rdev, 284 in.body.mknod.umask, name); 285 break; 286 case FUSE_OPEN: 287 printf(" flags=%#x", in.body.open.flags); 288 break; 289 case FUSE_OPENDIR: 290 printf(" flags=%#x", in.body.opendir.flags); 291 break; 292 case FUSE_READ: 293 printf(" offset=%" PRIu64 " size=%u", 294 in.body.read.offset, 295 in.body.read.size); 296 if (verbosity > 1) 297 printf(" flags=%#x", in.body.read.flags); 298 break; 299 case FUSE_READDIR: 300 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 301 in.body.readdir.fh, in.body.readdir.offset, 302 in.body.readdir.size); 303 break; 304 case FUSE_RELEASE: 305 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 306 in.body.release.fh, 307 in.body.release.flags, 308 in.body.release.lock_owner); 309 break; 310 case FUSE_RENAME: 311 { 312 const char *src = (const char*)in.body.bytes + 313 sizeof(fuse_rename_in); 314 const char *dst = src + strlen(src) + 1; 315 printf(" src=%s newdir=%" PRIu64 " dst=%s", 316 src, in.body.rename.newdir, dst); 317 } 318 break; 319 case FUSE_SETATTR: 320 if (verbosity <= 1) { 321 printf(" valid=%#x", in.body.setattr.valid); 322 break; 323 } 324 if (in.body.setattr.valid & FATTR_MODE) 325 printf(" mode=%#o", in.body.setattr.mode); 326 if (in.body.setattr.valid & FATTR_UID) 327 printf(" uid=%u", in.body.setattr.uid); 328 if (in.body.setattr.valid & FATTR_GID) 329 printf(" gid=%u", in.body.setattr.gid); 330 if (in.body.setattr.valid & FATTR_SIZE) 331 printf(" size=%" PRIu64, in.body.setattr.size); 332 if (in.body.setattr.valid & FATTR_ATIME) 333 printf(" atime=%" PRIu64 ".%u", 334 in.body.setattr.atime, 335 in.body.setattr.atimensec); 336 if (in.body.setattr.valid & FATTR_MTIME) 337 printf(" mtime=%" PRIu64 ".%u", 338 in.body.setattr.mtime, 339 in.body.setattr.mtimensec); 340 if (in.body.setattr.valid & FATTR_FH) 341 printf(" fh=%" PRIu64 "", in.body.setattr.fh); 342 break; 343 case FUSE_SETLK: 344 printf(" fh=%#" PRIx64 " owner=%" PRIu64 345 " type=%u pid=%u", 346 in.body.setlk.fh, in.body.setlk.owner, 347 in.body.setlk.lk.type, 348 in.body.setlk.lk.pid); 349 if (verbosity >= 2) { 350 printf(" range=[%" PRIi64 ":%" PRIi64 "]", 351 in.body.setlk.lk.start, 352 in.body.setlk.lk.end); 353 } 354 break; 355 case FUSE_SETXATTR: 356 /* 357 * In theory neither the xattr name and value need be 358 * ASCII, but in this test suite they always are. 359 */ 360 name = (const char*)in.body.bytes + 361 sizeof(fuse_setxattr_in); 362 value = name + strlen(name) + 1; 363 printf(" %s=%s", name, value); 364 break; 365 case FUSE_WRITE: 366 printf(" fh=%#" PRIx64 " offset=%" PRIu64 367 " size=%u write_flags=%u", 368 in.body.write.fh, 369 in.body.write.offset, in.body.write.size, 370 in.body.write.write_flags); 371 if (verbosity > 1) 372 printf(" flags=%#x", in.body.write.flags); 373 break; 374 default: 375 break; 376 } 377 printf("\n"); 378 } 379 380 /* 381 * Debug a FUSE response. 382 * 383 * This is mostly useful for asynchronous notifications, which don't correspond 384 * to any request 385 */ 386 void MockFS::debug_response(const mockfs_buf_out &out) { 387 const char *name; 388 389 if (verbosity == 0) 390 return; 391 392 switch (out.header.error) { 393 case FUSE_NOTIFY_INVAL_ENTRY: 394 name = (const char*)out.body.bytes + 395 sizeof(fuse_notify_inval_entry_out); 396 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 397 out.body.inval_entry.parent, name); 398 break; 399 case FUSE_NOTIFY_INVAL_INODE: 400 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64 401 " len=%" PRIi64 "\n", 402 out.body.inval_inode.ino, 403 out.body.inval_inode.off, 404 out.body.inval_inode.len); 405 break; 406 case FUSE_NOTIFY_STORE: 407 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64 408 " size=%" PRIu32 "\n", 409 out.body.store.nodeid, 410 out.body.store.offset, 411 out.body.store.size); 412 break; 413 default: 414 break; 415 } 416 } 417 418 MockFS::MockFS(int max_read, int max_readahead, bool allow_other, 419 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, bool no_auto_init) 424 : m_daemon_id(NULL), 425 m_kernel_minor_version(kernel_minor_version), 426 m_kq(pm == KQ ? kqueue() : -1), 427 m_maxread(max_read), 428 m_maxreadahead(max_readahead), 429 m_pid(getpid()), 430 m_uniques(new std::unordered_set<uint64_t>), 431 m_pm(pm), 432 m_time_gran(time_gran), 433 m_child_pid(-1), 434 m_maxwrite(MIN(max_write, max_max_write)), 435 m_nready(-1), 436 m_quit(false) 437 { 438 struct sigaction sa; 439 struct iovec *iov = NULL; 440 int iovlen = 0; 441 char fdstr[15]; 442 const bool trueval = true; 443 444 /* 445 * Kyua sets pwd to a testcase-unique tempdir; no need to use 446 * mkdtemp 447 */ 448 /* 449 * googletest doesn't allow ASSERT_ in constructors, so we must throw 450 * instead. 451 */ 452 if (mkdir("mountpoint" , 0755) && errno != EEXIST) 453 throw(std::system_error(errno, std::system_category(), 454 "Couldn't make mountpoint directory")); 455 456 switch (m_pm) { 457 case BLOCKING: 458 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 459 break; 460 default: 461 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 462 break; 463 } 464 if (m_fuse_fd < 0) 465 throw(std::system_error(errno, std::system_category(), 466 "Couldn't open /dev/fuse")); 467 468 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 469 build_iovec(&iov, &iovlen, "fspath", 470 __DECONST(void *, "mountpoint"), -1); 471 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 472 sprintf(fdstr, "%d", m_fuse_fd); 473 build_iovec(&iov, &iovlen, "fd", fdstr, -1); 474 if (m_maxread > 0) { 475 char val[12]; 476 477 snprintf(val, sizeof(val), "%d", m_maxread); 478 build_iovec(&iov, &iovlen, "max_read=", &val, -1); 479 } 480 if (allow_other) { 481 build_iovec(&iov, &iovlen, "allow_other", 482 __DECONST(void*, &trueval), sizeof(bool)); 483 } 484 if (default_permissions) { 485 build_iovec(&iov, &iovlen, "default_permissions", 486 __DECONST(void*, &trueval), sizeof(bool)); 487 } 488 if (push_symlinks_in) { 489 build_iovec(&iov, &iovlen, "push_symlinks_in", 490 __DECONST(void*, &trueval), sizeof(bool)); 491 } 492 if (ro) { 493 build_iovec(&iov, &iovlen, "ro", 494 __DECONST(void*, &trueval), sizeof(bool)); 495 } 496 if (async) { 497 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), 498 sizeof(bool)); 499 } 500 if (noatime) { 501 build_iovec(&iov, &iovlen, "noatime", 502 __DECONST(void*, &trueval), sizeof(bool)); 503 } 504 if (noclusterr) { 505 build_iovec(&iov, &iovlen, "noclusterr", 506 __DECONST(void*, &trueval), sizeof(bool)); 507 } 508 if (nointr) { 509 build_iovec(&iov, &iovlen, "nointr", 510 __DECONST(void*, &trueval), sizeof(bool)); 511 } else { 512 build_iovec(&iov, &iovlen, "intr", 513 __DECONST(void*, &trueval), sizeof(bool)); 514 } 515 if (*fsname) { 516 build_iovec(&iov, &iovlen, "fsname=", 517 __DECONST(void*, fsname), -1); 518 } 519 if (*subtype) { 520 build_iovec(&iov, &iovlen, "subtype=", 521 __DECONST(void*, subtype), -1); 522 } 523 if (nmount(iov, iovlen, 0)) 524 throw(std::system_error(errno, std::system_category(), 525 "Couldn't mount filesystem")); 526 free_iovec(&iov, &iovlen); 527 528 // Setup default handler 529 ON_CALL(*this, process(_, _)) 530 .WillByDefault(Invoke(this, &MockFS::process_default)); 531 532 if (!no_auto_init) 533 init(flags); 534 535 bzero(&sa, sizeof(sa)); 536 sa.sa_handler = sigint_handler; 537 sa.sa_flags = 0; /* Don't set SA_RESTART! */ 538 if (0 != sigaction(SIGUSR1, &sa, NULL)) 539 throw(std::system_error(errno, std::system_category(), 540 "Couldn't handle SIGUSR1")); 541 if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 542 throw(std::system_error(errno, std::system_category(), 543 "Couldn't Couldn't start fuse thread")); 544 } 545 546 MockFS::~MockFS() { 547 kill_daemon(); 548 join_daemon(); 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::join_daemon() { 790 if (m_daemon_id != NULL) { 791 pthread_join(m_daemon_id, NULL); 792 m_daemon_id = NULL; 793 } 794 } 795 796 void MockFS::loop() { 797 std::vector<std::unique_ptr<mockfs_buf_out>> out; 798 799 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 800 ASSERT_TRUE(in != NULL); 801 while (!m_quit) { 802 ssize_t buflen; 803 804 bzero(in.get(), sizeof(*in)); 805 read_request(*in, buflen); 806 if (m_quit) 807 break; 808 if (verbosity > 0) 809 debug_request(*in, buflen); 810 audit_request(*in, buflen); 811 if (pid_ok((pid_t)in->header.pid)) { 812 process(*in, out); 813 } else { 814 /* 815 * Reject any requests from unknown processes. Because 816 * we actually do mount a filesystem, plenty of 817 * unrelated system daemons may try to access it. 818 */ 819 if (verbosity > 1) 820 printf("\tREJECTED (wrong pid %d)\n", 821 in->header.pid); 822 process_default(*in, out); 823 } 824 for (auto &it: out) 825 write_response(*it); 826 out.clear(); 827 } 828 } 829 830 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen, 831 int expected_errno) 832 { 833 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 834 835 out->expected_errno = expected_errno; 836 out->header.unique = 0; /* 0 means asynchronous notification */ 837 out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 838 out->body.inval_entry.parent = parent; 839 out->body.inval_entry.namelen = namelen; 840 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 841 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 842 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 843 namelen; 844 debug_response(*out); 845 write_response(*out); 846 return 0; 847 } 848 849 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len) 850 { 851 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 852 853 out->header.unique = 0; /* 0 means asynchronous notification */ 854 out->header.error = FUSE_NOTIFY_INVAL_INODE; 855 out->body.inval_inode.ino = ino; 856 out->body.inval_inode.off = off; 857 out->body.inval_inode.len = len; 858 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode); 859 debug_response(*out); 860 write_response(*out); 861 return 0; 862 } 863 864 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size) 865 { 866 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 867 868 out->header.unique = 0; /* 0 means asynchronous notification */ 869 out->header.error = FUSE_NOTIFY_STORE; 870 out->body.store.nodeid = ino; 871 out->body.store.offset = off; 872 out->body.store.size = size; 873 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size); 874 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size; 875 debug_response(*out); 876 write_response(*out); 877 return 0; 878 } 879 880 bool MockFS::pid_ok(pid_t pid) { 881 if (pid == m_pid) { 882 return (true); 883 } else if (pid == m_child_pid) { 884 return (true); 885 } else { 886 struct kinfo_proc *ki; 887 bool ok = false; 888 889 ki = kinfo_getproc(pid); 890 if (ki == NULL) 891 return (false); 892 /* 893 * Allow access by the aio daemon processes so that our tests 894 * can use aio functions 895 */ 896 if (0 == strncmp("aiod", ki->ki_comm, 4)) 897 ok = true; 898 free(ki); 899 return (ok); 900 } 901 } 902 903 void MockFS::process_default(const mockfs_buf_in& in, 904 std::vector<std::unique_ptr<mockfs_buf_out>> &out) 905 { 906 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 907 out0->header.unique = in.header.unique; 908 out0->header.error = -EOPNOTSUPP; 909 out0->header.len = sizeof(out0->header); 910 out.push_back(std::move(out0)); 911 } 912 913 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) { 914 int nready = 0; 915 fd_set readfds; 916 pollfd fds[1]; 917 struct kevent changes[1]; 918 struct kevent events[1]; 919 struct timespec timeout_ts; 920 struct timeval timeout_tv; 921 const int timeout_ms = 999; 922 int timeout_int, nfds; 923 int fuse_fd; 924 925 switch (m_pm) { 926 case BLOCKING: 927 break; 928 case KQ: 929 timeout_ts.tv_sec = 0; 930 timeout_ts.tv_nsec = timeout_ms * 1'000'000; 931 while (nready == 0) { 932 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, 933 EV_ADD | EV_ONESHOT, 0, 0, 0); 934 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 935 &timeout_ts); 936 if (m_quit) 937 return; 938 } 939 ASSERT_LE(0, nready) << strerror(errno); 940 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 941 if (events[0].flags & EV_ERROR) 942 FAIL() << strerror(events[0].data); 943 else if (events[0].flags & EV_EOF) 944 FAIL() << strerror(events[0].fflags); 945 m_nready = events[0].data; 946 break; 947 case POLL: 948 timeout_int = timeout_ms; 949 fds[0].fd = m_fuse_fd; 950 fds[0].events = POLLIN; 951 while (nready == 0) { 952 nready = poll(fds, 1, timeout_int); 953 if (m_quit) 954 return; 955 } 956 ASSERT_LE(0, nready) << strerror(errno); 957 ASSERT_TRUE(fds[0].revents & POLLIN); 958 break; 959 case SELECT: 960 fuse_fd = m_fuse_fd; 961 if (fuse_fd < 0) 962 break; 963 timeout_tv.tv_sec = 0; 964 timeout_tv.tv_usec = timeout_ms * 1'000; 965 nfds = fuse_fd + 1; 966 while (nready == 0) { 967 FD_ZERO(&readfds); 968 FD_SET(fuse_fd, &readfds); 969 nready = select(nfds, &readfds, NULL, NULL, 970 &timeout_tv); 971 if (m_quit) 972 return; 973 } 974 ASSERT_LE(0, nready) << strerror(errno); 975 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds)); 976 break; 977 default: 978 FAIL() << "not yet implemented"; 979 } 980 res = read(m_fuse_fd, &in, sizeof(in)); 981 982 if (res < 0 && !m_quit) { 983 m_quit = true; 984 FAIL() << "read: " << strerror(errno); 985 } 986 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 987 /* 988 * Inconsistently, fuse_in_header.len is the size of the entire 989 * request,including header, even though fuse_out_header.len excludes 990 * the size of the header. 991 */ 992 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit); 993 } 994 995 void MockFS::write_response(const mockfs_buf_out &out) { 996 fd_set writefds; 997 pollfd fds[1]; 998 struct kevent changes[1]; 999 struct kevent events[1]; 1000 int nready, nfds; 1001 ssize_t r; 1002 1003 switch (m_pm) { 1004 case BLOCKING: 1005 break; 1006 case KQ: 1007 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE, 1008 EV_ADD | EV_ONESHOT, 0, 0, 0); 1009 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 1010 NULL); 1011 ASSERT_LE(0, nready) << strerror(errno); 1012 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 1013 if (events[0].flags & EV_ERROR) 1014 FAIL() << strerror(events[0].data); 1015 else if (events[0].flags & EV_EOF) 1016 FAIL() << strerror(events[0].fflags); 1017 m_nready = events[0].data; 1018 break; 1019 case POLL: 1020 fds[0].fd = m_fuse_fd; 1021 fds[0].events = POLLOUT; 1022 nready = poll(fds, 1, INFTIM); 1023 ASSERT_LE(0, nready) << strerror(errno); 1024 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1025 ASSERT_TRUE(fds[0].revents & POLLOUT); 1026 break; 1027 case SELECT: 1028 FD_ZERO(&writefds); 1029 FD_SET(m_fuse_fd, &writefds); 1030 nfds = m_fuse_fd + 1; 1031 nready = select(nfds, NULL, &writefds, NULL, NULL); 1032 ASSERT_LE(0, nready) << strerror(errno); 1033 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1034 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 1035 break; 1036 default: 1037 FAIL() << "not yet implemented"; 1038 } 1039 r = write(m_fuse_fd, &out, out.header.len); 1040 if (out.expected_errno) { 1041 ASSERT_EQ(-1, r); 1042 ASSERT_EQ(out.expected_errno, errno) << strerror(errno); 1043 } else { 1044 if (r <= 0 && errno == EINVAL) { 1045 printf("Failed to write response. unique=%" PRIu64 1046 ":\n", out.header.unique); 1047 } 1048 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 1049 } 1050 } 1051 1052 void* MockFS::service(void *pthr_data) { 1053 MockFS *mock_fs = (MockFS*)pthr_data; 1054 1055 mock_fs->loop(); 1056 1057 return (NULL); 1058 } 1059 1060 void MockFS::unmount() { 1061 ::unmount("mountpoint", 0); 1062 } 1063