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