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