1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 The FreeBSD Foundation 5 * 6 * This software was developed by BFF Storage Systems, LLC under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 extern "C" { 32 #include <sys/param.h> 33 34 #include <sys/mount.h> 35 #include <sys/select.h> 36 #include <sys/stat.h> 37 #include <sys/uio.h> 38 #include <sys/user.h> 39 40 #include <fcntl.h> 41 #include <libutil.h> 42 #include <mntopts.h> // for build_iovec 43 #include <poll.h> 44 #include <pthread.h> 45 #include <signal.h> 46 #include <stdlib.h> 47 #include <unistd.h> 48 } 49 50 #include <cinttypes> 51 52 #include <gtest/gtest.h> 53 54 #include "mockfs.hh" 55 56 using namespace testing; 57 58 int verbosity = 0; 59 60 const char* opcode2opname(uint32_t opcode) 61 { 62 const char* table[] = { 63 "Unknown (opcode 0)", 64 "LOOKUP", 65 "FORGET", 66 "GETATTR", 67 "SETATTR", 68 "READLINK", 69 "SYMLINK", 70 "Unknown (opcode 7)", 71 "MKNOD", 72 "MKDIR", 73 "UNLINK", 74 "RMDIR", 75 "RENAME", 76 "LINK", 77 "OPEN", 78 "READ", 79 "WRITE", 80 "STATFS", 81 "RELEASE", 82 "Unknown (opcode 19)", 83 "FSYNC", 84 "SETXATTR", 85 "GETXATTR", 86 "LISTXATTR", 87 "REMOVEXATTR", 88 "FLUSH", 89 "INIT", 90 "OPENDIR", 91 "READDIR", 92 "RELEASEDIR", 93 "FSYNCDIR", 94 "GETLK", 95 "SETLK", 96 "SETLKW", 97 "ACCESS", 98 "CREATE", 99 "INTERRUPT", 100 "BMAP", 101 "DESTROY", 102 "IOCTL", 103 "POLL", 104 "NOTIFY_REPLY", 105 "BATCH_FORGET", 106 "FALLOCATE", 107 "READDIRPLUS", 108 "RENAME2", 109 "LSEEK", 110 "COPY_FILE_RANGE", 111 }; 112 if (opcode >= nitems(table)) 113 return ("Unknown (opcode > max)"); 114 else 115 return (table[opcode]); 116 } 117 118 ProcessMockerT 119 ReturnErrno(int error) 120 { 121 return([=](auto in, auto &out) { 122 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 123 out0->header.unique = in.header.unique; 124 out0->header.error = -error; 125 out0->header.len = sizeof(out0->header); 126 out.push_back(std::move(out0)); 127 }); 128 } 129 130 /* Helper function used for returning negative cache entries for LOOKUP */ 131 ProcessMockerT 132 ReturnNegativeCache(const struct timespec *entry_valid) 133 { 134 return([=](auto in, auto &out) { 135 /* nodeid means ENOENT and cache it */ 136 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 137 out0->body.entry.nodeid = 0; 138 out0->header.unique = in.header.unique; 139 out0->header.error = 0; 140 out0->body.entry.entry_valid = entry_valid->tv_sec; 141 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec; 142 SET_OUT_HEADER_LEN(*out0, entry); 143 out.push_back(std::move(out0)); 144 }); 145 } 146 147 ProcessMockerT 148 ReturnImmediate(std::function<void(const mockfs_buf_in& in, 149 struct mockfs_buf_out &out)> f) 150 { 151 return([=](auto& in, auto &out) { 152 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 153 out0->header.unique = in.header.unique; 154 f(in, *out0); 155 out.push_back(std::move(out0)); 156 }); 157 } 158 159 void sigint_handler(int __unused sig) { 160 // Don't do anything except interrupt the daemon's read(2) call 161 } 162 163 void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen) 164 { 165 printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode), 166 in.header.nodeid); 167 if (verbosity > 1) { 168 printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u" 169 " buflen=%zd", 170 in.header.uid, in.header.gid, in.header.pid, 171 in.header.unique, in.header.len, buflen); 172 } 173 switch (in.header.opcode) { 174 const char *name, *value; 175 176 case FUSE_ACCESS: 177 printf(" mask=%#x", in.body.access.mask); 178 break; 179 case FUSE_BMAP: 180 printf(" block=%" PRIx64 " blocksize=%#x", 181 in.body.bmap.block, in.body.bmap.blocksize); 182 break; 183 case FUSE_COPY_FILE_RANGE: 184 printf(" off_in=%" PRIu64 " ino_out=%" PRIu64 185 " off_out=%" PRIu64 " size=%" PRIu64, 186 in.body.copy_file_range.off_in, 187 in.body.copy_file_range.nodeid_out, 188 in.body.copy_file_range.off_out, 189 in.body.copy_file_range.len); 190 if (verbosity > 1) 191 printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64 192 " flags=%" PRIx64, 193 in.body.copy_file_range.fh_in, 194 in.body.copy_file_range.fh_out, 195 in.body.copy_file_range.flags); 196 break; 197 case FUSE_CREATE: 198 if (m_kernel_minor_version >= 12) 199 name = (const char*)in.body.bytes + 200 sizeof(fuse_create_in); 201 else 202 name = (const char*)in.body.bytes + 203 sizeof(fuse_open_in); 204 printf(" flags=%#x name=%s", 205 in.body.open.flags, name); 206 break; 207 case FUSE_FALLOCATE: 208 printf(" fh=%#" PRIx64 " offset=%" PRIu64 209 " length=%" PRIx64 " mode=%#x", 210 in.body.fallocate.fh, 211 in.body.fallocate.offset, 212 in.body.fallocate.length, 213 in.body.fallocate.mode); 214 break; 215 case FUSE_FLUSH: 216 printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64, 217 in.body.flush.fh, 218 in.body.flush.lock_owner); 219 break; 220 case FUSE_FORGET: 221 printf(" nlookup=%" PRIu64, in.body.forget.nlookup); 222 break; 223 case FUSE_FSYNC: 224 printf(" flags=%#x", in.body.fsync.fsync_flags); 225 break; 226 case FUSE_FSYNCDIR: 227 printf(" flags=%#x", in.body.fsyncdir.fsync_flags); 228 break; 229 case FUSE_GETLK: 230 printf(" fh=%#" PRIx64 231 " type=%u pid=%u", 232 in.body.getlk.fh, 233 in.body.getlk.lk.type, 234 in.body.getlk.lk.pid); 235 if (verbosity >= 2) { 236 printf(" range=[%" PRIi64 ":%" PRIi64 "]", 237 in.body.getlk.lk.start, 238 in.body.getlk.lk.end); 239 } 240 break; 241 case FUSE_INTERRUPT: 242 printf(" unique=%" PRIu64, in.body.interrupt.unique); 243 break; 244 case FUSE_LINK: 245 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 246 break; 247 case FUSE_LISTXATTR: 248 printf(" size=%" PRIu32, in.body.listxattr.size); 249 break; 250 case FUSE_LOOKUP: 251 printf(" %s", in.body.lookup); 252 break; 253 case FUSE_LSEEK: 254 switch (in.body.lseek.whence) { 255 case SEEK_HOLE: 256 printf(" SEEK_HOLE offset=%jd", 257 in.body.lseek.offset); 258 break; 259 case SEEK_DATA: 260 printf(" SEEK_DATA offset=%jd", 261 in.body.lseek.offset); 262 break; 263 default: 264 printf(" whence=%u offset=%jd", 265 in.body.lseek.whence, in.body.lseek.offset); 266 break; 267 } 268 break; 269 case FUSE_MKDIR: 270 name = (const char*)in.body.bytes + 271 sizeof(fuse_mkdir_in); 272 printf(" name=%s mode=%#o umask=%#o", name, 273 in.body.mkdir.mode, in.body.mkdir.umask); 274 break; 275 case FUSE_MKNOD: 276 if (m_kernel_minor_version >= 12) 277 name = (const char*)in.body.bytes + 278 sizeof(fuse_mknod_in); 279 else 280 name = (const char*)in.body.bytes + 281 FUSE_COMPAT_MKNOD_IN_SIZE; 282 printf(" mode=%#o rdev=%x umask=%#o name=%s", 283 in.body.mknod.mode, in.body.mknod.rdev, 284 in.body.mknod.umask, name); 285 break; 286 case FUSE_OPEN: 287 printf(" flags=%#x", in.body.open.flags); 288 break; 289 case FUSE_OPENDIR: 290 printf(" flags=%#x", in.body.opendir.flags); 291 break; 292 case FUSE_READ: 293 printf(" offset=%" PRIu64 " size=%u", 294 in.body.read.offset, 295 in.body.read.size); 296 if (verbosity > 1) 297 printf(" flags=%#x", in.body.read.flags); 298 break; 299 case FUSE_READDIR: 300 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 301 in.body.readdir.fh, in.body.readdir.offset, 302 in.body.readdir.size); 303 break; 304 case FUSE_RELEASE: 305 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 306 in.body.release.fh, 307 in.body.release.flags, 308 in.body.release.lock_owner); 309 break; 310 case FUSE_RENAME: 311 { 312 const char *src = (const char*)in.body.bytes + 313 sizeof(fuse_rename_in); 314 const char *dst = src + strlen(src) + 1; 315 printf(" src=%s newdir=%" PRIu64 " dst=%s", 316 src, in.body.rename.newdir, dst); 317 } 318 break; 319 case FUSE_SETATTR: 320 if (verbosity <= 1) { 321 printf(" valid=%#x", in.body.setattr.valid); 322 break; 323 } 324 if (in.body.setattr.valid & FATTR_MODE) 325 printf(" mode=%#o", in.body.setattr.mode); 326 if (in.body.setattr.valid & FATTR_UID) 327 printf(" uid=%u", in.body.setattr.uid); 328 if (in.body.setattr.valid & FATTR_GID) 329 printf(" gid=%u", in.body.setattr.gid); 330 if (in.body.setattr.valid & FATTR_SIZE) 331 printf(" size=%" PRIu64, in.body.setattr.size); 332 if (in.body.setattr.valid & FATTR_ATIME) 333 printf(" atime=%" PRIu64 ".%u", 334 in.body.setattr.atime, 335 in.body.setattr.atimensec); 336 if (in.body.setattr.valid & FATTR_MTIME) 337 printf(" mtime=%" PRIu64 ".%u", 338 in.body.setattr.mtime, 339 in.body.setattr.mtimensec); 340 if (in.body.setattr.valid & FATTR_FH) 341 printf(" fh=%" PRIu64 "", in.body.setattr.fh); 342 break; 343 case FUSE_SETLK: 344 printf(" fh=%#" PRIx64 " owner=%" PRIu64 345 " type=%u pid=%u", 346 in.body.setlk.fh, in.body.setlk.owner, 347 in.body.setlk.lk.type, 348 in.body.setlk.lk.pid); 349 if (verbosity >= 2) { 350 printf(" range=[%" PRIi64 ":%" PRIi64 "]", 351 in.body.setlk.lk.start, 352 in.body.setlk.lk.end); 353 } 354 break; 355 case FUSE_SETXATTR: 356 /* 357 * In theory neither the xattr name and value need be 358 * ASCII, but in this test suite they always are. 359 */ 360 name = (const char*)in.body.bytes + 361 sizeof(fuse_setxattr_in); 362 value = name + strlen(name) + 1; 363 printf(" %s=%s", name, value); 364 break; 365 case FUSE_WRITE: 366 printf(" fh=%#" PRIx64 " offset=%" PRIu64 367 " size=%u write_flags=%u", 368 in.body.write.fh, 369 in.body.write.offset, in.body.write.size, 370 in.body.write.write_flags); 371 if (verbosity > 1) 372 printf(" flags=%#x", in.body.write.flags); 373 break; 374 default: 375 break; 376 } 377 printf("\n"); 378 } 379 380 /* 381 * Debug a FUSE response. 382 * 383 * This is mostly useful for asynchronous notifications, which don't correspond 384 * to any request 385 */ 386 void MockFS::debug_response(const mockfs_buf_out &out) { 387 const char *name; 388 389 if (verbosity == 0) 390 return; 391 392 switch (out.header.error) { 393 case FUSE_NOTIFY_INVAL_ENTRY: 394 name = (const char*)out.body.bytes + 395 sizeof(fuse_notify_inval_entry_out); 396 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 397 out.body.inval_entry.parent, name); 398 break; 399 case FUSE_NOTIFY_INVAL_INODE: 400 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64 401 " len=%" PRIi64 "\n", 402 out.body.inval_inode.ino, 403 out.body.inval_inode.off, 404 out.body.inval_inode.len); 405 break; 406 case FUSE_NOTIFY_STORE: 407 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64 408 " size=%" PRIu32 "\n", 409 out.body.store.nodeid, 410 out.body.store.offset, 411 out.body.store.size); 412 break; 413 default: 414 break; 415 } 416 } 417 418 MockFS::MockFS(int max_read, int max_readahead, bool allow_other, 419 bool default_permissions, 420 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 421 uint32_t kernel_minor_version, uint32_t max_write, bool async, 422 bool noclusterr, unsigned time_gran, bool nointr, bool noatime, 423 const char *fsname, const char *subtype) 424 : m_daemon_id(NULL), 425 m_kernel_minor_version(kernel_minor_version), 426 m_kq(pm == KQ ? kqueue() : -1), 427 m_maxread(max_read), 428 m_maxreadahead(max_readahead), 429 m_pid(getpid()), 430 m_uniques(new std::unordered_set<uint64_t>), 431 m_pm(pm), 432 m_time_gran(time_gran), 433 m_child_pid(-1), 434 m_maxwrite(MIN(max_write, max_max_write)), 435 m_nready(-1), 436 m_quit(false) 437 { 438 struct sigaction sa; 439 struct iovec *iov = NULL; 440 int iovlen = 0; 441 char fdstr[15]; 442 const bool trueval = true; 443 444 /* 445 * Kyua sets pwd to a testcase-unique tempdir; no need to use 446 * mkdtemp 447 */ 448 /* 449 * googletest doesn't allow ASSERT_ in constructors, so we must throw 450 * instead. 451 */ 452 if (mkdir("mountpoint" , 0755) && errno != EEXIST) 453 throw(std::system_error(errno, std::system_category(), 454 "Couldn't make mountpoint directory")); 455 456 switch (m_pm) { 457 case BLOCKING: 458 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 459 break; 460 default: 461 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 462 break; 463 } 464 if (m_fuse_fd < 0) 465 throw(std::system_error(errno, std::system_category(), 466 "Couldn't open /dev/fuse")); 467 468 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 469 build_iovec(&iov, &iovlen, "fspath", 470 __DECONST(void *, "mountpoint"), -1); 471 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 472 sprintf(fdstr, "%d", m_fuse_fd); 473 build_iovec(&iov, &iovlen, "fd", fdstr, -1); 474 if (m_maxread > 0) { 475 char val[10]; 476 477 snprintf(val, sizeof(val), "%d", m_maxread); 478 build_iovec(&iov, &iovlen, "max_read=", &val, -1); 479 } 480 if (allow_other) { 481 build_iovec(&iov, &iovlen, "allow_other", 482 __DECONST(void*, &trueval), sizeof(bool)); 483 } 484 if (default_permissions) { 485 build_iovec(&iov, &iovlen, "default_permissions", 486 __DECONST(void*, &trueval), sizeof(bool)); 487 } 488 if (push_symlinks_in) { 489 build_iovec(&iov, &iovlen, "push_symlinks_in", 490 __DECONST(void*, &trueval), sizeof(bool)); 491 } 492 if (ro) { 493 build_iovec(&iov, &iovlen, "ro", 494 __DECONST(void*, &trueval), sizeof(bool)); 495 } 496 if (async) { 497 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), 498 sizeof(bool)); 499 } 500 if (noatime) { 501 build_iovec(&iov, &iovlen, "noatime", 502 __DECONST(void*, &trueval), sizeof(bool)); 503 } 504 if (noclusterr) { 505 build_iovec(&iov, &iovlen, "noclusterr", 506 __DECONST(void*, &trueval), sizeof(bool)); 507 } 508 if (nointr) { 509 build_iovec(&iov, &iovlen, "nointr", 510 __DECONST(void*, &trueval), sizeof(bool)); 511 } else { 512 build_iovec(&iov, &iovlen, "intr", 513 __DECONST(void*, &trueval), sizeof(bool)); 514 } 515 if (*fsname) { 516 build_iovec(&iov, &iovlen, "fsname=", 517 __DECONST(void*, fsname), -1); 518 } 519 if (*subtype) { 520 build_iovec(&iov, &iovlen, "subtype=", 521 __DECONST(void*, subtype), -1); 522 } 523 if (nmount(iov, iovlen, 0)) 524 throw(std::system_error(errno, std::system_category(), 525 "Couldn't mount filesystem")); 526 free_iovec(&iov, &iovlen); 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 /* Verify that the ticket's unique value is actually unique. */ 744 if (m_uniques->find(in.header.unique) != m_uniques->end()) 745 FAIL() << "Non-unique \"unique\" value"; 746 m_uniques->insert(in.header.unique); 747 } 748 749 void MockFS::init(uint32_t flags) { 750 ssize_t buflen; 751 752 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 753 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 754 755 read_request(*in, buflen); 756 if (verbosity > 0) 757 debug_request(*in, buflen); 758 audit_request(*in, buflen); 759 ASSERT_EQ(FUSE_INIT, in->header.opcode); 760 761 out->header.unique = in->header.unique; 762 out->header.error = 0; 763 out->body.init.major = FUSE_KERNEL_VERSION; 764 out->body.init.minor = m_kernel_minor_version;; 765 out->body.init.flags = in->body.init.flags & flags; 766 out->body.init.max_write = m_maxwrite; 767 out->body.init.max_readahead = m_maxreadahead; 768 769 if (m_kernel_minor_version < 23) { 770 SET_OUT_HEADER_LEN(*out, init_7_22); 771 } else { 772 out->body.init.time_gran = m_time_gran; 773 SET_OUT_HEADER_LEN(*out, init); 774 } 775 776 write(m_fuse_fd, out.get(), out->header.len); 777 } 778 779 void MockFS::kill_daemon() { 780 m_quit = true; 781 if (m_daemon_id != NULL) 782 pthread_kill(m_daemon_id, SIGUSR1); 783 // Closing the /dev/fuse file descriptor first allows unmount to 784 // succeed even if the daemon doesn't correctly respond to commands 785 // during the unmount sequence. 786 close(m_fuse_fd); 787 m_fuse_fd = -1; 788 } 789 790 void MockFS::loop() { 791 std::vector<std::unique_ptr<mockfs_buf_out>> out; 792 793 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 794 ASSERT_TRUE(in != NULL); 795 while (!m_quit) { 796 ssize_t buflen; 797 798 bzero(in.get(), sizeof(*in)); 799 read_request(*in, buflen); 800 if (m_quit) 801 break; 802 if (verbosity > 0) 803 debug_request(*in, buflen); 804 audit_request(*in, buflen); 805 if (pid_ok((pid_t)in->header.pid)) { 806 process(*in, out); 807 } else { 808 /* 809 * Reject any requests from unknown processes. Because 810 * we actually do mount a filesystem, plenty of 811 * unrelated system daemons may try to access it. 812 */ 813 if (verbosity > 1) 814 printf("\tREJECTED (wrong pid %d)\n", 815 in->header.pid); 816 process_default(*in, out); 817 } 818 for (auto &it: out) 819 write_response(*it); 820 out.clear(); 821 } 822 } 823 824 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) 825 { 826 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 827 828 out->header.unique = 0; /* 0 means asynchronous notification */ 829 out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 830 out->body.inval_entry.parent = parent; 831 out->body.inval_entry.namelen = namelen; 832 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 833 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 834 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 835 namelen; 836 debug_response(*out); 837 write_response(*out); 838 return 0; 839 } 840 841 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len) 842 { 843 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 844 845 out->header.unique = 0; /* 0 means asynchronous notification */ 846 out->header.error = FUSE_NOTIFY_INVAL_INODE; 847 out->body.inval_inode.ino = ino; 848 out->body.inval_inode.off = off; 849 out->body.inval_inode.len = len; 850 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode); 851 debug_response(*out); 852 write_response(*out); 853 return 0; 854 } 855 856 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size) 857 { 858 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 859 860 out->header.unique = 0; /* 0 means asynchronous notification */ 861 out->header.error = FUSE_NOTIFY_STORE; 862 out->body.store.nodeid = ino; 863 out->body.store.offset = off; 864 out->body.store.size = size; 865 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size); 866 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size; 867 debug_response(*out); 868 write_response(*out); 869 return 0; 870 } 871 872 bool MockFS::pid_ok(pid_t pid) { 873 if (pid == m_pid) { 874 return (true); 875 } else if (pid == m_child_pid) { 876 return (true); 877 } else { 878 struct kinfo_proc *ki; 879 bool ok = false; 880 881 ki = kinfo_getproc(pid); 882 if (ki == NULL) 883 return (false); 884 /* 885 * Allow access by the aio daemon processes so that our tests 886 * can use aio functions 887 */ 888 if (0 == strncmp("aiod", ki->ki_comm, 4)) 889 ok = true; 890 free(ki); 891 return (ok); 892 } 893 } 894 895 void MockFS::process_default(const mockfs_buf_in& in, 896 std::vector<std::unique_ptr<mockfs_buf_out>> &out) 897 { 898 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 899 out0->header.unique = in.header.unique; 900 out0->header.error = -EOPNOTSUPP; 901 out0->header.len = sizeof(out0->header); 902 out.push_back(std::move(out0)); 903 } 904 905 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) { 906 int nready = 0; 907 fd_set readfds; 908 pollfd fds[1]; 909 struct kevent changes[1]; 910 struct kevent events[1]; 911 struct timespec timeout_ts; 912 struct timeval timeout_tv; 913 const int timeout_ms = 999; 914 int timeout_int, nfds; 915 int fuse_fd; 916 917 switch (m_pm) { 918 case BLOCKING: 919 break; 920 case KQ: 921 timeout_ts.tv_sec = 0; 922 timeout_ts.tv_nsec = timeout_ms * 1'000'000; 923 while (nready == 0) { 924 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, 925 EV_ADD | EV_ONESHOT, 0, 0, 0); 926 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 927 &timeout_ts); 928 if (m_quit) 929 return; 930 } 931 ASSERT_LE(0, nready) << strerror(errno); 932 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 933 if (events[0].flags & EV_ERROR) 934 FAIL() << strerror(events[0].data); 935 else if (events[0].flags & EV_EOF) 936 FAIL() << strerror(events[0].fflags); 937 m_nready = events[0].data; 938 break; 939 case POLL: 940 timeout_int = timeout_ms; 941 fds[0].fd = m_fuse_fd; 942 fds[0].events = POLLIN; 943 while (nready == 0) { 944 nready = poll(fds, 1, timeout_int); 945 if (m_quit) 946 return; 947 } 948 ASSERT_LE(0, nready) << strerror(errno); 949 ASSERT_TRUE(fds[0].revents & POLLIN); 950 break; 951 case SELECT: 952 fuse_fd = m_fuse_fd; 953 if (fuse_fd < 0) 954 break; 955 timeout_tv.tv_sec = 0; 956 timeout_tv.tv_usec = timeout_ms * 1'000; 957 nfds = fuse_fd + 1; 958 while (nready == 0) { 959 FD_ZERO(&readfds); 960 FD_SET(fuse_fd, &readfds); 961 nready = select(nfds, &readfds, NULL, NULL, 962 &timeout_tv); 963 if (m_quit) 964 return; 965 } 966 ASSERT_LE(0, nready) << strerror(errno); 967 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds)); 968 break; 969 default: 970 FAIL() << "not yet implemented"; 971 } 972 res = read(m_fuse_fd, &in, sizeof(in)); 973 974 if (res < 0 && !m_quit) { 975 m_quit = true; 976 FAIL() << "read: " << strerror(errno); 977 } 978 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 979 /* 980 * Inconsistently, fuse_in_header.len is the size of the entire 981 * request,including header, even though fuse_out_header.len excludes 982 * the size of the header. 983 */ 984 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit); 985 } 986 987 void MockFS::write_response(const mockfs_buf_out &out) { 988 fd_set writefds; 989 pollfd fds[1]; 990 struct kevent changes[1]; 991 struct kevent events[1]; 992 int nready, nfds; 993 ssize_t r; 994 995 switch (m_pm) { 996 case BLOCKING: 997 break; 998 case KQ: 999 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE, 1000 EV_ADD | EV_ONESHOT, 0, 0, 0); 1001 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 1002 NULL); 1003 ASSERT_LE(0, nready) << strerror(errno); 1004 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 1005 if (events[0].flags & EV_ERROR) 1006 FAIL() << strerror(events[0].data); 1007 else if (events[0].flags & EV_EOF) 1008 FAIL() << strerror(events[0].fflags); 1009 m_nready = events[0].data; 1010 break; 1011 case POLL: 1012 fds[0].fd = m_fuse_fd; 1013 fds[0].events = POLLOUT; 1014 nready = poll(fds, 1, INFTIM); 1015 ASSERT_LE(0, nready) << strerror(errno); 1016 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1017 ASSERT_TRUE(fds[0].revents & POLLOUT); 1018 break; 1019 case SELECT: 1020 FD_ZERO(&writefds); 1021 FD_SET(m_fuse_fd, &writefds); 1022 nfds = m_fuse_fd + 1; 1023 nready = select(nfds, NULL, &writefds, NULL, NULL); 1024 ASSERT_LE(0, nready) << strerror(errno); 1025 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1026 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 1027 break; 1028 default: 1029 FAIL() << "not yet implemented"; 1030 } 1031 r = write(m_fuse_fd, &out, out.header.len); 1032 if (out.expected_errno) { 1033 ASSERT_EQ(-1, r); 1034 ASSERT_EQ(out.expected_errno, errno) << strerror(errno); 1035 } else { 1036 if (r <= 0 && errno == EINVAL) { 1037 printf("Failed to write response. unique=%" PRIu64 1038 ":\n", out.header.unique); 1039 } 1040 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 1041 } 1042 } 1043 1044 void* MockFS::service(void *pthr_data) { 1045 MockFS *mock_fs = (MockFS*)pthr_data; 1046 1047 mock_fs->loop(); 1048 1049 return (NULL); 1050 } 1051 1052 void MockFS::unmount() { 1053 ::unmount("mountpoint", 0); 1054 } 1055