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