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