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 * $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_GETLK: 233 printf(" fh=%#" PRIx64 234 " type=%u pid=%u", 235 in.body.getlk.fh, 236 in.body.getlk.lk.type, 237 in.body.getlk.lk.pid); 238 if (verbosity >= 2) { 239 printf(" range=[%" PRIi64 ":%" PRIi64 "]", 240 in.body.getlk.lk.start, 241 in.body.getlk.lk.end); 242 } 243 break; 244 case FUSE_INTERRUPT: 245 printf(" unique=%" PRIu64, in.body.interrupt.unique); 246 break; 247 case FUSE_LINK: 248 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 249 break; 250 case FUSE_LISTXATTR: 251 printf(" size=%" PRIu32, in.body.listxattr.size); 252 break; 253 case FUSE_LOOKUP: 254 printf(" %s", in.body.lookup); 255 break; 256 case FUSE_LSEEK: 257 switch (in.body.lseek.whence) { 258 case SEEK_HOLE: 259 printf(" SEEK_HOLE offset=%jd", 260 in.body.lseek.offset); 261 break; 262 case SEEK_DATA: 263 printf(" SEEK_DATA offset=%jd", 264 in.body.lseek.offset); 265 break; 266 default: 267 printf(" whence=%u offset=%jd", 268 in.body.lseek.whence, in.body.lseek.offset); 269 break; 270 } 271 break; 272 case FUSE_MKDIR: 273 name = (const char*)in.body.bytes + 274 sizeof(fuse_mkdir_in); 275 printf(" name=%s mode=%#o umask=%#o", name, 276 in.body.mkdir.mode, in.body.mkdir.umask); 277 break; 278 case FUSE_MKNOD: 279 if (m_kernel_minor_version >= 12) 280 name = (const char*)in.body.bytes + 281 sizeof(fuse_mknod_in); 282 else 283 name = (const char*)in.body.bytes + 284 FUSE_COMPAT_MKNOD_IN_SIZE; 285 printf(" mode=%#o rdev=%x umask=%#o name=%s", 286 in.body.mknod.mode, in.body.mknod.rdev, 287 in.body.mknod.umask, name); 288 break; 289 case FUSE_OPEN: 290 printf(" flags=%#x", in.body.open.flags); 291 break; 292 case FUSE_OPENDIR: 293 printf(" flags=%#x", in.body.opendir.flags); 294 break; 295 case FUSE_READ: 296 printf(" offset=%" PRIu64 " size=%u", 297 in.body.read.offset, 298 in.body.read.size); 299 if (verbosity > 1) 300 printf(" flags=%#x", in.body.read.flags); 301 break; 302 case FUSE_READDIR: 303 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 304 in.body.readdir.fh, in.body.readdir.offset, 305 in.body.readdir.size); 306 break; 307 case FUSE_RELEASE: 308 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 309 in.body.release.fh, 310 in.body.release.flags, 311 in.body.release.lock_owner); 312 break; 313 case FUSE_RENAME: 314 { 315 const char *src = (const char*)in.body.bytes + 316 sizeof(fuse_rename_in); 317 const char *dst = src + strlen(src) + 1; 318 printf(" src=%s newdir=%" PRIu64 " dst=%s", 319 src, in.body.rename.newdir, dst); 320 } 321 break; 322 case FUSE_SETATTR: 323 if (verbosity <= 1) { 324 printf(" valid=%#x", in.body.setattr.valid); 325 break; 326 } 327 if (in.body.setattr.valid & FATTR_MODE) 328 printf(" mode=%#o", in.body.setattr.mode); 329 if (in.body.setattr.valid & FATTR_UID) 330 printf(" uid=%u", in.body.setattr.uid); 331 if (in.body.setattr.valid & FATTR_GID) 332 printf(" gid=%u", in.body.setattr.gid); 333 if (in.body.setattr.valid & FATTR_SIZE) 334 printf(" size=%" PRIu64, in.body.setattr.size); 335 if (in.body.setattr.valid & FATTR_ATIME) 336 printf(" atime=%" PRIu64 ".%u", 337 in.body.setattr.atime, 338 in.body.setattr.atimensec); 339 if (in.body.setattr.valid & FATTR_MTIME) 340 printf(" mtime=%" PRIu64 ".%u", 341 in.body.setattr.mtime, 342 in.body.setattr.mtimensec); 343 if (in.body.setattr.valid & FATTR_FH) 344 printf(" fh=%" PRIu64 "", in.body.setattr.fh); 345 break; 346 case FUSE_SETLK: 347 printf(" fh=%#" PRIx64 " owner=%" PRIu64 348 " type=%u pid=%u", 349 in.body.setlk.fh, in.body.setlk.owner, 350 in.body.setlk.lk.type, 351 in.body.setlk.lk.pid); 352 if (verbosity >= 2) { 353 printf(" range=[%" PRIi64 ":%" PRIi64 "]", 354 in.body.setlk.lk.start, 355 in.body.setlk.lk.end); 356 } 357 break; 358 case FUSE_SETXATTR: 359 /* 360 * In theory neither the xattr name and value need be 361 * ASCII, but in this test suite they always are. 362 */ 363 name = (const char*)in.body.bytes + 364 sizeof(fuse_setxattr_in); 365 value = name + strlen(name) + 1; 366 printf(" %s=%s", name, value); 367 break; 368 case FUSE_WRITE: 369 printf(" fh=%#" PRIx64 " offset=%" PRIu64 370 " size=%u write_flags=%u", 371 in.body.write.fh, 372 in.body.write.offset, in.body.write.size, 373 in.body.write.write_flags); 374 if (verbosity > 1) 375 printf(" flags=%#x", in.body.write.flags); 376 break; 377 default: 378 break; 379 } 380 printf("\n"); 381 } 382 383 /* 384 * Debug a FUSE response. 385 * 386 * This is mostly useful for asynchronous notifications, which don't correspond 387 * to any request 388 */ 389 void MockFS::debug_response(const mockfs_buf_out &out) { 390 const char *name; 391 392 if (verbosity == 0) 393 return; 394 395 switch (out.header.error) { 396 case FUSE_NOTIFY_INVAL_ENTRY: 397 name = (const char*)out.body.bytes + 398 sizeof(fuse_notify_inval_entry_out); 399 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 400 out.body.inval_entry.parent, name); 401 break; 402 case FUSE_NOTIFY_INVAL_INODE: 403 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64 404 " len=%" PRIi64 "\n", 405 out.body.inval_inode.ino, 406 out.body.inval_inode.off, 407 out.body.inval_inode.len); 408 break; 409 case FUSE_NOTIFY_STORE: 410 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64 411 " size=%" PRIu32 "\n", 412 out.body.store.nodeid, 413 out.body.store.offset, 414 out.body.store.size); 415 break; 416 default: 417 break; 418 } 419 } 420 421 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, 422 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 423 uint32_t kernel_minor_version, uint32_t max_write, bool async, 424 bool noclusterr, unsigned time_gran, bool nointr, bool noatime, 425 const char *fsname, const char *subtype) 426 { 427 struct sigaction sa; 428 struct iovec *iov = NULL; 429 int iovlen = 0; 430 char fdstr[15]; 431 const bool trueval = true; 432 433 m_daemon_id = NULL; 434 m_kernel_minor_version = kernel_minor_version; 435 m_maxreadahead = max_readahead; 436 m_maxwrite = MIN(max_write, max_max_write); 437 m_nready = -1; 438 m_pm = pm; 439 m_time_gran = time_gran; 440 m_quit = false; 441 m_last_unique = 0; 442 if (m_pm == KQ) 443 m_kq = kqueue(); 444 else 445 m_kq = -1; 446 447 /* 448 * Kyua sets pwd to a testcase-unique tempdir; no need to use 449 * mkdtemp 450 */ 451 /* 452 * googletest doesn't allow ASSERT_ in constructors, so we must throw 453 * instead. 454 */ 455 if (mkdir("mountpoint" , 0755) && errno != EEXIST) 456 throw(std::system_error(errno, std::system_category(), 457 "Couldn't make mountpoint directory")); 458 459 switch (m_pm) { 460 case BLOCKING: 461 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 462 break; 463 default: 464 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 465 break; 466 } 467 if (m_fuse_fd < 0) 468 throw(std::system_error(errno, std::system_category(), 469 "Couldn't open /dev/fuse")); 470 471 m_pid = getpid(); 472 m_child_pid = -1; 473 474 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 475 build_iovec(&iov, &iovlen, "fspath", 476 __DECONST(void *, "mountpoint"), -1); 477 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 478 sprintf(fdstr, "%d", m_fuse_fd); 479 build_iovec(&iov, &iovlen, "fd", fdstr, -1); 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 527 // Setup default handler 528 ON_CALL(*this, process(_, _)) 529 .WillByDefault(Invoke(this, &MockFS::process_default)); 530 531 init(flags); 532 bzero(&sa, sizeof(sa)); 533 sa.sa_handler = sigint_handler; 534 sa.sa_flags = 0; /* Don't set SA_RESTART! */ 535 if (0 != sigaction(SIGUSR1, &sa, NULL)) 536 throw(std::system_error(errno, std::system_category(), 537 "Couldn't handle SIGUSR1")); 538 if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 539 throw(std::system_error(errno, std::system_category(), 540 "Couldn't Couldn't start fuse thread")); 541 } 542 543 MockFS::~MockFS() { 544 kill_daemon(); 545 if (m_daemon_id != NULL) { 546 pthread_join(m_daemon_id, NULL); 547 m_daemon_id = NULL; 548 } 549 ::unmount("mountpoint", MNT_FORCE); 550 rmdir("mountpoint"); 551 if (m_kq >= 0) 552 close(m_kq); 553 } 554 555 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) { 556 uint32_t inlen = in.header.len; 557 size_t fih = sizeof(in.header); 558 switch (in.header.opcode) { 559 case FUSE_LOOKUP: 560 case FUSE_RMDIR: 561 case FUSE_SYMLINK: 562 case FUSE_UNLINK: 563 EXPECT_GT(inlen, fih) << "Missing request filename"; 564 // No redundant information for checking buflen 565 break; 566 case FUSE_FORGET: 567 EXPECT_EQ(inlen, fih + sizeof(in.body.forget)); 568 EXPECT_EQ((size_t)buflen, inlen); 569 break; 570 case FUSE_GETATTR: 571 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr)); 572 EXPECT_EQ((size_t)buflen, inlen); 573 break; 574 case FUSE_SETATTR: 575 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr)); 576 EXPECT_EQ((size_t)buflen, inlen); 577 break; 578 case FUSE_READLINK: 579 EXPECT_EQ(inlen, fih) << "Unexpected request body"; 580 EXPECT_EQ((size_t)buflen, inlen); 581 break; 582 case FUSE_MKNOD: 583 { 584 size_t s; 585 if (m_kernel_minor_version >= 12) 586 s = sizeof(in.body.mknod); 587 else 588 s = FUSE_COMPAT_MKNOD_IN_SIZE; 589 EXPECT_GE(inlen, fih + s) << "Missing request body"; 590 EXPECT_GT(inlen, fih + s) << "Missing request filename"; 591 // No redundant information for checking buflen 592 break; 593 } 594 case FUSE_MKDIR: 595 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) << 596 "Missing request body"; 597 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) << 598 "Missing request filename"; 599 // No redundant information for checking buflen 600 break; 601 case FUSE_RENAME: 602 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) << 603 "Missing request body"; 604 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) << 605 "Missing request filename"; 606 // No redundant information for checking buflen 607 break; 608 case FUSE_LINK: 609 EXPECT_GE(inlen, fih + sizeof(in.body.link)) << 610 "Missing request body"; 611 EXPECT_GT(inlen, fih + sizeof(in.body.link)) << 612 "Missing request filename"; 613 // No redundant information for checking buflen 614 break; 615 case FUSE_OPEN: 616 EXPECT_EQ(inlen, fih + sizeof(in.body.open)); 617 EXPECT_EQ((size_t)buflen, inlen); 618 break; 619 case FUSE_READ: 620 EXPECT_EQ(inlen, fih + sizeof(in.body.read)); 621 EXPECT_EQ((size_t)buflen, inlen); 622 break; 623 case FUSE_WRITE: 624 { 625 size_t s; 626 627 if (m_kernel_minor_version >= 9) 628 s = sizeof(in.body.write); 629 else 630 s = FUSE_COMPAT_WRITE_IN_SIZE; 631 // I suppose a 0-byte write should be allowed 632 EXPECT_GE(inlen, fih + s) << "Missing request body"; 633 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size); 634 break; 635 } 636 case FUSE_DESTROY: 637 case FUSE_STATFS: 638 EXPECT_EQ(inlen, fih); 639 EXPECT_EQ((size_t)buflen, inlen); 640 break; 641 case FUSE_RELEASE: 642 EXPECT_EQ(inlen, fih + sizeof(in.body.release)); 643 EXPECT_EQ((size_t)buflen, inlen); 644 break; 645 case FUSE_FSYNC: 646 case FUSE_FSYNCDIR: 647 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync)); 648 EXPECT_EQ((size_t)buflen, inlen); 649 break; 650 case FUSE_SETXATTR: 651 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) << 652 "Missing request body"; 653 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) << 654 "Missing request attribute name"; 655 // No redundant information for checking buflen 656 break; 657 case FUSE_GETXATTR: 658 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) << 659 "Missing request body"; 660 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) << 661 "Missing request attribute name"; 662 // No redundant information for checking buflen 663 break; 664 case FUSE_LISTXATTR: 665 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr)); 666 EXPECT_EQ((size_t)buflen, inlen); 667 break; 668 case FUSE_REMOVEXATTR: 669 EXPECT_GT(inlen, fih) << "Missing request attribute name"; 670 // No redundant information for checking buflen 671 break; 672 case FUSE_FLUSH: 673 EXPECT_EQ(inlen, fih + sizeof(in.body.flush)); 674 EXPECT_EQ((size_t)buflen, inlen); 675 break; 676 case FUSE_INIT: 677 EXPECT_EQ(inlen, fih + sizeof(in.body.init)); 678 EXPECT_EQ((size_t)buflen, inlen); 679 break; 680 case FUSE_OPENDIR: 681 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir)); 682 EXPECT_EQ((size_t)buflen, inlen); 683 break; 684 case FUSE_READDIR: 685 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir)); 686 EXPECT_EQ((size_t)buflen, inlen); 687 break; 688 case FUSE_RELEASEDIR: 689 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir)); 690 EXPECT_EQ((size_t)buflen, inlen); 691 break; 692 case FUSE_GETLK: 693 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk)); 694 EXPECT_EQ((size_t)buflen, inlen); 695 break; 696 case FUSE_SETLK: 697 case FUSE_SETLKW: 698 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk)); 699 EXPECT_EQ((size_t)buflen, inlen); 700 break; 701 case FUSE_ACCESS: 702 EXPECT_EQ(inlen, fih + sizeof(in.body.access)); 703 EXPECT_EQ((size_t)buflen, inlen); 704 break; 705 case FUSE_CREATE: 706 EXPECT_GE(inlen, fih + sizeof(in.body.create)) << 707 "Missing request body"; 708 EXPECT_GT(inlen, fih + sizeof(in.body.create)) << 709 "Missing request filename"; 710 // No redundant information for checking buflen 711 break; 712 case FUSE_INTERRUPT: 713 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt)); 714 EXPECT_EQ((size_t)buflen, inlen); 715 break; 716 case FUSE_FALLOCATE: 717 EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate)); 718 EXPECT_EQ((size_t)buflen, inlen); 719 break; 720 case FUSE_BMAP: 721 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap)); 722 EXPECT_EQ((size_t)buflen, inlen); 723 break; 724 case FUSE_LSEEK: 725 EXPECT_EQ(inlen, fih + sizeof(in.body.lseek)); 726 EXPECT_EQ((size_t)buflen, inlen); 727 break; 728 case FUSE_COPY_FILE_RANGE: 729 EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range)); 730 EXPECT_EQ(0ul, in.body.copy_file_range.flags); 731 EXPECT_EQ((size_t)buflen, inlen); 732 break; 733 case FUSE_NOTIFY_REPLY: 734 case FUSE_BATCH_FORGET: 735 case FUSE_IOCTL: 736 case FUSE_POLL: 737 case FUSE_READDIRPLUS: 738 FAIL() << "Unsupported opcode?"; 739 default: 740 FAIL() << "Unknown opcode " << in.header.opcode; 741 } 742 /* 743 * Check that the ticket's unique value is sequential. Technically it 744 * doesn't need to be sequential, merely unique. But the current 745 * fusefs driver _does_ make it sequential, and that's easy to check 746 * for. 747 */ 748 if (in.header.unique != ++m_last_unique) 749 FAIL() << "Non-sequential unique value"; 750 } 751 752 void MockFS::init(uint32_t flags) { 753 ssize_t buflen; 754 755 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 756 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 757 758 read_request(*in, buflen); 759 if (verbosity > 0) 760 debug_request(*in, buflen); 761 audit_request(*in, buflen); 762 ASSERT_EQ(FUSE_INIT, in->header.opcode); 763 764 out->header.unique = in->header.unique; 765 out->header.error = 0; 766 out->body.init.major = FUSE_KERNEL_VERSION; 767 out->body.init.minor = m_kernel_minor_version;; 768 out->body.init.flags = in->body.init.flags & flags; 769 out->body.init.max_write = m_maxwrite; 770 out->body.init.max_readahead = m_maxreadahead; 771 772 if (m_kernel_minor_version < 23) { 773 SET_OUT_HEADER_LEN(*out, init_7_22); 774 } else { 775 out->body.init.time_gran = m_time_gran; 776 SET_OUT_HEADER_LEN(*out, init); 777 } 778 779 write(m_fuse_fd, out.get(), out->header.len); 780 } 781 782 void MockFS::kill_daemon() { 783 m_quit = true; 784 if (m_daemon_id != NULL) 785 pthread_kill(m_daemon_id, SIGUSR1); 786 // Closing the /dev/fuse file descriptor first allows unmount to 787 // succeed even if the daemon doesn't correctly respond to commands 788 // during the unmount sequence. 789 close(m_fuse_fd); 790 m_fuse_fd = -1; 791 } 792 793 void MockFS::loop() { 794 std::vector<std::unique_ptr<mockfs_buf_out>> out; 795 796 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 797 ASSERT_TRUE(in != NULL); 798 while (!m_quit) { 799 ssize_t buflen; 800 801 bzero(in.get(), sizeof(*in)); 802 read_request(*in, buflen); 803 if (m_quit) 804 break; 805 if (verbosity > 0) 806 debug_request(*in, buflen); 807 audit_request(*in, buflen); 808 if (pid_ok((pid_t)in->header.pid)) { 809 process(*in, out); 810 } else { 811 /* 812 * Reject any requests from unknown processes. Because 813 * we actually do mount a filesystem, plenty of 814 * unrelated system daemons may try to access it. 815 */ 816 if (verbosity > 1) 817 printf("\tREJECTED (wrong pid %d)\n", 818 in->header.pid); 819 process_default(*in, out); 820 } 821 for (auto &it: out) 822 write_response(*it); 823 out.clear(); 824 } 825 } 826 827 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) 828 { 829 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 830 831 out->header.unique = 0; /* 0 means asynchronous notification */ 832 out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 833 out->body.inval_entry.parent = parent; 834 out->body.inval_entry.namelen = namelen; 835 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 836 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 837 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 838 namelen; 839 debug_response(*out); 840 write_response(*out); 841 return 0; 842 } 843 844 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len) 845 { 846 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 847 848 out->header.unique = 0; /* 0 means asynchronous notification */ 849 out->header.error = FUSE_NOTIFY_INVAL_INODE; 850 out->body.inval_inode.ino = ino; 851 out->body.inval_inode.off = off; 852 out->body.inval_inode.len = len; 853 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode); 854 debug_response(*out); 855 write_response(*out); 856 return 0; 857 } 858 859 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size) 860 { 861 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 862 863 out->header.unique = 0; /* 0 means asynchronous notification */ 864 out->header.error = FUSE_NOTIFY_STORE; 865 out->body.store.nodeid = ino; 866 out->body.store.offset = off; 867 out->body.store.size = size; 868 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size); 869 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size; 870 debug_response(*out); 871 write_response(*out); 872 return 0; 873 } 874 875 bool MockFS::pid_ok(pid_t pid) { 876 if (pid == m_pid) { 877 return (true); 878 } else if (pid == m_child_pid) { 879 return (true); 880 } else { 881 struct kinfo_proc *ki; 882 bool ok = false; 883 884 ki = kinfo_getproc(pid); 885 if (ki == NULL) 886 return (false); 887 /* 888 * Allow access by the aio daemon processes so that our tests 889 * can use aio functions 890 */ 891 if (0 == strncmp("aiod", ki->ki_comm, 4)) 892 ok = true; 893 free(ki); 894 return (ok); 895 } 896 } 897 898 void MockFS::process_default(const mockfs_buf_in& in, 899 std::vector<std::unique_ptr<mockfs_buf_out>> &out) 900 { 901 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 902 out0->header.unique = in.header.unique; 903 out0->header.error = -EOPNOTSUPP; 904 out0->header.len = sizeof(out0->header); 905 out.push_back(std::move(out0)); 906 } 907 908 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) { 909 int nready = 0; 910 fd_set readfds; 911 pollfd fds[1]; 912 struct kevent changes[1]; 913 struct kevent events[1]; 914 struct timespec timeout_ts; 915 struct timeval timeout_tv; 916 const int timeout_ms = 999; 917 int timeout_int, nfds; 918 int fuse_fd; 919 920 switch (m_pm) { 921 case BLOCKING: 922 break; 923 case KQ: 924 timeout_ts.tv_sec = 0; 925 timeout_ts.tv_nsec = timeout_ms * 1'000'000; 926 while (nready == 0) { 927 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, 928 EV_ADD | EV_ONESHOT, 0, 0, 0); 929 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 930 &timeout_ts); 931 if (m_quit) 932 return; 933 } 934 ASSERT_LE(0, nready) << strerror(errno); 935 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 936 if (events[0].flags & EV_ERROR) 937 FAIL() << strerror(events[0].data); 938 else if (events[0].flags & EV_EOF) 939 FAIL() << strerror(events[0].fflags); 940 m_nready = events[0].data; 941 break; 942 case POLL: 943 timeout_int = timeout_ms; 944 fds[0].fd = m_fuse_fd; 945 fds[0].events = POLLIN; 946 while (nready == 0) { 947 nready = poll(fds, 1, timeout_int); 948 if (m_quit) 949 return; 950 } 951 ASSERT_LE(0, nready) << strerror(errno); 952 ASSERT_TRUE(fds[0].revents & POLLIN); 953 break; 954 case SELECT: 955 fuse_fd = m_fuse_fd; 956 if (fuse_fd < 0) 957 break; 958 timeout_tv.tv_sec = 0; 959 timeout_tv.tv_usec = timeout_ms * 1'000; 960 nfds = fuse_fd + 1; 961 while (nready == 0) { 962 FD_ZERO(&readfds); 963 FD_SET(fuse_fd, &readfds); 964 nready = select(nfds, &readfds, NULL, NULL, 965 &timeout_tv); 966 if (m_quit) 967 return; 968 } 969 ASSERT_LE(0, nready) << strerror(errno); 970 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds)); 971 break; 972 default: 973 FAIL() << "not yet implemented"; 974 } 975 res = read(m_fuse_fd, &in, sizeof(in)); 976 977 if (res < 0 && !m_quit) { 978 m_quit = true; 979 FAIL() << "read: " << strerror(errno); 980 } 981 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 982 /* 983 * Inconsistently, fuse_in_header.len is the size of the entire 984 * request,including header, even though fuse_out_header.len excludes 985 * the size of the header. 986 */ 987 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit); 988 } 989 990 void MockFS::write_response(const mockfs_buf_out &out) { 991 fd_set writefds; 992 pollfd fds[1]; 993 struct kevent changes[1]; 994 struct kevent events[1]; 995 int nready, nfds; 996 ssize_t r; 997 998 switch (m_pm) { 999 case BLOCKING: 1000 break; 1001 case KQ: 1002 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE, 1003 EV_ADD | EV_ONESHOT, 0, 0, 0); 1004 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 1005 NULL); 1006 ASSERT_LE(0, nready) << strerror(errno); 1007 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 1008 if (events[0].flags & EV_ERROR) 1009 FAIL() << strerror(events[0].data); 1010 else if (events[0].flags & EV_EOF) 1011 FAIL() << strerror(events[0].fflags); 1012 m_nready = events[0].data; 1013 break; 1014 case POLL: 1015 fds[0].fd = m_fuse_fd; 1016 fds[0].events = POLLOUT; 1017 nready = poll(fds, 1, INFTIM); 1018 ASSERT_LE(0, nready) << strerror(errno); 1019 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1020 ASSERT_TRUE(fds[0].revents & POLLOUT); 1021 break; 1022 case SELECT: 1023 FD_ZERO(&writefds); 1024 FD_SET(m_fuse_fd, &writefds); 1025 nfds = m_fuse_fd + 1; 1026 nready = select(nfds, NULL, &writefds, NULL, NULL); 1027 ASSERT_LE(0, nready) << strerror(errno); 1028 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1029 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 1030 break; 1031 default: 1032 FAIL() << "not yet implemented"; 1033 } 1034 r = write(m_fuse_fd, &out, out.header.len); 1035 if (out.expected_errno) { 1036 ASSERT_EQ(-1, r); 1037 ASSERT_EQ(out.expected_errno, errno) << strerror(errno); 1038 } else { 1039 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 1040 } 1041 } 1042 1043 void* MockFS::service(void *pthr_data) { 1044 MockFS *mock_fs = (MockFS*)pthr_data; 1045 1046 mock_fs->loop(); 1047 1048 return (NULL); 1049 } 1050 1051 void MockFS::unmount() { 1052 ::unmount("mountpoint", 0); 1053 } 1054