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