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 <poll.h> 43 #include <pthread.h> 44 #include <signal.h> 45 #include <stdlib.h> 46 #include <unistd.h> 47 48 #include "mntopts.h" // for build_iovec 49 } 50 51 #include <cinttypes> 52 53 #include <gtest/gtest.h> 54 55 #include "mockfs.hh" 56 57 using namespace testing; 58 59 int verbosity = 0; 60 61 const char* opcode2opname(uint32_t opcode) 62 { 63 const char* table[] = { 64 "Unknown (opcode 0)", 65 "LOOKUP", 66 "FORGET", 67 "GETATTR", 68 "SETATTR", 69 "READLINK", 70 "SYMLINK", 71 "Unknown (opcode 7)", 72 "MKNOD", 73 "MKDIR", 74 "UNLINK", 75 "RMDIR", 76 "RENAME", 77 "LINK", 78 "OPEN", 79 "READ", 80 "WRITE", 81 "STATFS", 82 "RELEASE", 83 "Unknown (opcode 19)", 84 "FSYNC", 85 "SETXATTR", 86 "GETXATTR", 87 "LISTXATTR", 88 "REMOVEXATTR", 89 "FLUSH", 90 "INIT", 91 "OPENDIR", 92 "READDIR", 93 "RELEASEDIR", 94 "FSYNCDIR", 95 "GETLK", 96 "SETLK", 97 "SETLKW", 98 "ACCESS", 99 "CREATE", 100 "INTERRUPT", 101 "BMAP", 102 "DESTROY", 103 "IOCTL", 104 "POLL", 105 "NOTIFY_REPLY", 106 "BATCH_FORGET", 107 "FALLOCATE", 108 "READDIRPLUS", 109 "RENAME2", 110 "LSEEK", 111 "COPY_FILE_RANGE", 112 }; 113 if (opcode >= nitems(table)) 114 return ("Unknown (opcode > max)"); 115 else 116 return (table[opcode]); 117 } 118 119 ProcessMockerT 120 ReturnErrno(int error) 121 { 122 return([=](auto in, auto &out) { 123 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 124 out0->header.unique = in.header.unique; 125 out0->header.error = -error; 126 out0->header.len = sizeof(out0->header); 127 out.push_back(std::move(out0)); 128 }); 129 } 130 131 /* Helper function used for returning negative cache entries for LOOKUP */ 132 ProcessMockerT 133 ReturnNegativeCache(const struct timespec *entry_valid) 134 { 135 return([=](auto in, auto &out) { 136 /* nodeid means ENOENT and cache it */ 137 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 138 out0->body.entry.nodeid = 0; 139 out0->header.unique = in.header.unique; 140 out0->header.error = 0; 141 out0->body.entry.entry_valid = entry_valid->tv_sec; 142 out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec; 143 SET_OUT_HEADER_LEN(*out0, entry); 144 out.push_back(std::move(out0)); 145 }); 146 } 147 148 ProcessMockerT 149 ReturnImmediate(std::function<void(const mockfs_buf_in& in, 150 struct mockfs_buf_out &out)> f) 151 { 152 return([=](auto& in, auto &out) { 153 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 154 out0->header.unique = in.header.unique; 155 f(in, *out0); 156 out.push_back(std::move(out0)); 157 }); 158 } 159 160 void sigint_handler(int __unused sig) { 161 // Don't do anything except interrupt the daemon's read(2) call 162 } 163 164 void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen) 165 { 166 printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode), 167 in.header.nodeid); 168 if (verbosity > 1) { 169 printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u" 170 " buflen=%zd", 171 in.header.uid, in.header.gid, in.header.pid, 172 in.header.unique, in.header.len, buflen); 173 } 174 switch (in.header.opcode) { 175 const char *name, *value; 176 177 case FUSE_ACCESS: 178 printf(" mask=%#x", in.body.access.mask); 179 break; 180 case FUSE_BMAP: 181 printf(" block=%" PRIx64 " blocksize=%#x", 182 in.body.bmap.block, in.body.bmap.blocksize); 183 break; 184 case FUSE_COPY_FILE_RANGE: 185 printf(" off_in=%" PRIu64 " ino_out=%" PRIu64 186 " off_out=%" PRIu64 " size=%" PRIu64, 187 in.body.copy_file_range.off_in, 188 in.body.copy_file_range.nodeid_out, 189 in.body.copy_file_range.off_out, 190 in.body.copy_file_range.len); 191 if (verbosity > 1) 192 printf(" fh_in=%" PRIu64 " fh_out=%" PRIu64 193 " flags=%" PRIx64, 194 in.body.copy_file_range.fh_in, 195 in.body.copy_file_range.fh_out, 196 in.body.copy_file_range.flags); 197 break; 198 case FUSE_CREATE: 199 if (m_kernel_minor_version >= 12) 200 name = (const char*)in.body.bytes + 201 sizeof(fuse_create_in); 202 else 203 name = (const char*)in.body.bytes + 204 sizeof(fuse_open_in); 205 printf(" flags=%#x name=%s", 206 in.body.open.flags, name); 207 break; 208 case FUSE_FALLOCATE: 209 printf(" fh=%#" PRIx64 " offset=%" PRIu64 210 " length=%" PRIx64 " mode=%#x", 211 in.body.fallocate.fh, 212 in.body.fallocate.offset, 213 in.body.fallocate.length, 214 in.body.fallocate.mode); 215 break; 216 case FUSE_FLUSH: 217 printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64, 218 in.body.flush.fh, 219 in.body.flush.lock_owner); 220 break; 221 case FUSE_FORGET: 222 printf(" nlookup=%" PRIu64, in.body.forget.nlookup); 223 break; 224 case FUSE_FSYNC: 225 printf(" flags=%#x", in.body.fsync.fsync_flags); 226 break; 227 case FUSE_FSYNCDIR: 228 printf(" flags=%#x", in.body.fsyncdir.fsync_flags); 229 break; 230 case FUSE_GETLK: 231 printf(" fh=%#" PRIx64 232 " type=%u pid=%u", 233 in.body.getlk.fh, 234 in.body.getlk.lk.type, 235 in.body.getlk.lk.pid); 236 if (verbosity >= 2) { 237 printf(" range=[%" PRIi64 ":%" PRIi64 "]", 238 in.body.getlk.lk.start, 239 in.body.getlk.lk.end); 240 } 241 break; 242 case FUSE_INTERRUPT: 243 printf(" unique=%" PRIu64, in.body.interrupt.unique); 244 break; 245 case FUSE_LINK: 246 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 247 break; 248 case FUSE_LISTXATTR: 249 printf(" size=%" PRIu32, in.body.listxattr.size); 250 break; 251 case FUSE_LOOKUP: 252 printf(" %s", in.body.lookup); 253 break; 254 case FUSE_LSEEK: 255 switch (in.body.lseek.whence) { 256 case SEEK_HOLE: 257 printf(" SEEK_HOLE offset=%jd", 258 in.body.lseek.offset); 259 break; 260 case SEEK_DATA: 261 printf(" SEEK_DATA offset=%jd", 262 in.body.lseek.offset); 263 break; 264 default: 265 printf(" whence=%u offset=%jd", 266 in.body.lseek.whence, in.body.lseek.offset); 267 break; 268 } 269 break; 270 case FUSE_MKDIR: 271 name = (const char*)in.body.bytes + 272 sizeof(fuse_mkdir_in); 273 printf(" name=%s mode=%#o umask=%#o", name, 274 in.body.mkdir.mode, in.body.mkdir.umask); 275 break; 276 case FUSE_MKNOD: 277 if (m_kernel_minor_version >= 12) 278 name = (const char*)in.body.bytes + 279 sizeof(fuse_mknod_in); 280 else 281 name = (const char*)in.body.bytes + 282 FUSE_COMPAT_MKNOD_IN_SIZE; 283 printf(" mode=%#o rdev=%x umask=%#o name=%s", 284 in.body.mknod.mode, in.body.mknod.rdev, 285 in.body.mknod.umask, name); 286 break; 287 case FUSE_OPEN: 288 printf(" flags=%#x", in.body.open.flags); 289 break; 290 case FUSE_OPENDIR: 291 printf(" flags=%#x", in.body.opendir.flags); 292 break; 293 case FUSE_READ: 294 printf(" offset=%" PRIu64 " size=%u", 295 in.body.read.offset, 296 in.body.read.size); 297 if (verbosity > 1) 298 printf(" flags=%#x", in.body.read.flags); 299 break; 300 case FUSE_READDIR: 301 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 302 in.body.readdir.fh, in.body.readdir.offset, 303 in.body.readdir.size); 304 break; 305 case FUSE_RELEASE: 306 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 307 in.body.release.fh, 308 in.body.release.flags, 309 in.body.release.lock_owner); 310 break; 311 case FUSE_RENAME: 312 { 313 const char *src = (const char*)in.body.bytes + 314 sizeof(fuse_rename_in); 315 const char *dst = src + strlen(src) + 1; 316 printf(" src=%s newdir=%" PRIu64 " dst=%s", 317 src, in.body.rename.newdir, dst); 318 } 319 break; 320 case FUSE_SETATTR: 321 if (verbosity <= 1) { 322 printf(" valid=%#x", in.body.setattr.valid); 323 break; 324 } 325 if (in.body.setattr.valid & FATTR_MODE) 326 printf(" mode=%#o", in.body.setattr.mode); 327 if (in.body.setattr.valid & FATTR_UID) 328 printf(" uid=%u", in.body.setattr.uid); 329 if (in.body.setattr.valid & FATTR_GID) 330 printf(" gid=%u", in.body.setattr.gid); 331 if (in.body.setattr.valid & FATTR_SIZE) 332 printf(" size=%" PRIu64, in.body.setattr.size); 333 if (in.body.setattr.valid & FATTR_ATIME) 334 printf(" atime=%" PRIu64 ".%u", 335 in.body.setattr.atime, 336 in.body.setattr.atimensec); 337 if (in.body.setattr.valid & FATTR_MTIME) 338 printf(" mtime=%" PRIu64 ".%u", 339 in.body.setattr.mtime, 340 in.body.setattr.mtimensec); 341 if (in.body.setattr.valid & FATTR_FH) 342 printf(" fh=%" PRIu64 "", in.body.setattr.fh); 343 break; 344 case FUSE_SETLK: 345 printf(" fh=%#" PRIx64 " owner=%" PRIu64 346 " type=%u pid=%u", 347 in.body.setlk.fh, in.body.setlk.owner, 348 in.body.setlk.lk.type, 349 in.body.setlk.lk.pid); 350 if (verbosity >= 2) { 351 printf(" range=[%" PRIi64 ":%" PRIi64 "]", 352 in.body.setlk.lk.start, 353 in.body.setlk.lk.end); 354 } 355 break; 356 case FUSE_SETXATTR: 357 /* 358 * In theory neither the xattr name and value need be 359 * ASCII, but in this test suite they always are. 360 */ 361 name = (const char*)in.body.bytes + 362 sizeof(fuse_setxattr_in); 363 value = name + strlen(name) + 1; 364 printf(" %s=%s", name, value); 365 break; 366 case FUSE_WRITE: 367 printf(" fh=%#" PRIx64 " offset=%" PRIu64 368 " size=%u write_flags=%u", 369 in.body.write.fh, 370 in.body.write.offset, in.body.write.size, 371 in.body.write.write_flags); 372 if (verbosity > 1) 373 printf(" flags=%#x", in.body.write.flags); 374 break; 375 default: 376 break; 377 } 378 printf("\n"); 379 } 380 381 /* 382 * Debug a FUSE response. 383 * 384 * This is mostly useful for asynchronous notifications, which don't correspond 385 * to any request 386 */ 387 void MockFS::debug_response(const mockfs_buf_out &out) { 388 const char *name; 389 390 if (verbosity == 0) 391 return; 392 393 switch (out.header.error) { 394 case FUSE_NOTIFY_INVAL_ENTRY: 395 name = (const char*)out.body.bytes + 396 sizeof(fuse_notify_inval_entry_out); 397 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 398 out.body.inval_entry.parent, name); 399 break; 400 case FUSE_NOTIFY_INVAL_INODE: 401 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64 402 " len=%" PRIi64 "\n", 403 out.body.inval_inode.ino, 404 out.body.inval_inode.off, 405 out.body.inval_inode.len); 406 break; 407 case FUSE_NOTIFY_STORE: 408 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64 409 " size=%" PRIu32 "\n", 410 out.body.store.nodeid, 411 out.body.store.offset, 412 out.body.store.size); 413 break; 414 default: 415 break; 416 } 417 } 418 419 MockFS::MockFS(int max_readahead, bool allow_other, 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_maxreadahead(max_readahead), 428 m_pid(getpid()), 429 m_uniques(new std::unordered_set<uint64_t>), 430 m_pm(pm), 431 m_time_gran(time_gran), 432 m_child_pid(-1), 433 m_maxwrite(MIN(max_write, max_max_write)), 434 m_nready(-1), 435 m_quit(false) 436 { 437 struct sigaction sa; 438 struct iovec *iov = NULL; 439 int iovlen = 0; 440 char fdstr[15]; 441 const bool trueval = true; 442 443 /* 444 * Kyua sets pwd to a testcase-unique tempdir; no need to use 445 * mkdtemp 446 */ 447 /* 448 * googletest doesn't allow ASSERT_ in constructors, so we must throw 449 * instead. 450 */ 451 if (mkdir("mountpoint" , 0755) && errno != EEXIST) 452 throw(std::system_error(errno, std::system_category(), 453 "Couldn't make mountpoint directory")); 454 455 switch (m_pm) { 456 case BLOCKING: 457 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 458 break; 459 default: 460 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 461 break; 462 } 463 if (m_fuse_fd < 0) 464 throw(std::system_error(errno, std::system_category(), 465 "Couldn't open /dev/fuse")); 466 467 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 468 build_iovec(&iov, &iovlen, "fspath", 469 __DECONST(void *, "mountpoint"), -1); 470 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 471 sprintf(fdstr, "%d", m_fuse_fd); 472 build_iovec(&iov, &iovlen, "fd", fdstr, -1); 473 if (allow_other) { 474 build_iovec(&iov, &iovlen, "allow_other", 475 __DECONST(void*, &trueval), sizeof(bool)); 476 } 477 if (default_permissions) { 478 build_iovec(&iov, &iovlen, "default_permissions", 479 __DECONST(void*, &trueval), sizeof(bool)); 480 } 481 if (push_symlinks_in) { 482 build_iovec(&iov, &iovlen, "push_symlinks_in", 483 __DECONST(void*, &trueval), sizeof(bool)); 484 } 485 if (ro) { 486 build_iovec(&iov, &iovlen, "ro", 487 __DECONST(void*, &trueval), sizeof(bool)); 488 } 489 if (async) { 490 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), 491 sizeof(bool)); 492 } 493 if (noatime) { 494 build_iovec(&iov, &iovlen, "noatime", 495 __DECONST(void*, &trueval), sizeof(bool)); 496 } 497 if (noclusterr) { 498 build_iovec(&iov, &iovlen, "noclusterr", 499 __DECONST(void*, &trueval), sizeof(bool)); 500 } 501 if (nointr) { 502 build_iovec(&iov, &iovlen, "nointr", 503 __DECONST(void*, &trueval), sizeof(bool)); 504 } else { 505 build_iovec(&iov, &iovlen, "intr", 506 __DECONST(void*, &trueval), sizeof(bool)); 507 } 508 if (*fsname) { 509 build_iovec(&iov, &iovlen, "fsname=", 510 __DECONST(void*, fsname), -1); 511 } 512 if (*subtype) { 513 build_iovec(&iov, &iovlen, "subtype=", 514 __DECONST(void*, subtype), -1); 515 } 516 if (nmount(iov, iovlen, 0)) 517 throw(std::system_error(errno, std::system_category(), 518 "Couldn't mount filesystem")); 519 free_iovec(&iov, &iovlen); 520 521 // Setup default handler 522 ON_CALL(*this, process(_, _)) 523 .WillByDefault(Invoke(this, &MockFS::process_default)); 524 525 init(flags); 526 bzero(&sa, sizeof(sa)); 527 sa.sa_handler = sigint_handler; 528 sa.sa_flags = 0; /* Don't set SA_RESTART! */ 529 if (0 != sigaction(SIGUSR1, &sa, NULL)) 530 throw(std::system_error(errno, std::system_category(), 531 "Couldn't handle SIGUSR1")); 532 if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 533 throw(std::system_error(errno, std::system_category(), 534 "Couldn't Couldn't start fuse thread")); 535 } 536 537 MockFS::~MockFS() { 538 kill_daemon(); 539 if (m_daemon_id != NULL) { 540 pthread_join(m_daemon_id, NULL); 541 m_daemon_id = NULL; 542 } 543 ::unmount("mountpoint", MNT_FORCE); 544 rmdir("mountpoint"); 545 if (m_kq >= 0) 546 close(m_kq); 547 } 548 549 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) { 550 uint32_t inlen = in.header.len; 551 size_t fih = sizeof(in.header); 552 switch (in.header.opcode) { 553 case FUSE_LOOKUP: 554 case FUSE_RMDIR: 555 case FUSE_SYMLINK: 556 case FUSE_UNLINK: 557 EXPECT_GT(inlen, fih) << "Missing request filename"; 558 // No redundant information for checking buflen 559 break; 560 case FUSE_FORGET: 561 EXPECT_EQ(inlen, fih + sizeof(in.body.forget)); 562 EXPECT_EQ((size_t)buflen, inlen); 563 break; 564 case FUSE_GETATTR: 565 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr)); 566 EXPECT_EQ((size_t)buflen, inlen); 567 break; 568 case FUSE_SETATTR: 569 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr)); 570 EXPECT_EQ((size_t)buflen, inlen); 571 break; 572 case FUSE_READLINK: 573 EXPECT_EQ(inlen, fih) << "Unexpected request body"; 574 EXPECT_EQ((size_t)buflen, inlen); 575 break; 576 case FUSE_MKNOD: 577 { 578 size_t s; 579 if (m_kernel_minor_version >= 12) 580 s = sizeof(in.body.mknod); 581 else 582 s = FUSE_COMPAT_MKNOD_IN_SIZE; 583 EXPECT_GE(inlen, fih + s) << "Missing request body"; 584 EXPECT_GT(inlen, fih + s) << "Missing request filename"; 585 // No redundant information for checking buflen 586 break; 587 } 588 case FUSE_MKDIR: 589 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) << 590 "Missing request body"; 591 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) << 592 "Missing request filename"; 593 // No redundant information for checking buflen 594 break; 595 case FUSE_RENAME: 596 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) << 597 "Missing request body"; 598 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) << 599 "Missing request filename"; 600 // No redundant information for checking buflen 601 break; 602 case FUSE_LINK: 603 EXPECT_GE(inlen, fih + sizeof(in.body.link)) << 604 "Missing request body"; 605 EXPECT_GT(inlen, fih + sizeof(in.body.link)) << 606 "Missing request filename"; 607 // No redundant information for checking buflen 608 break; 609 case FUSE_OPEN: 610 EXPECT_EQ(inlen, fih + sizeof(in.body.open)); 611 EXPECT_EQ((size_t)buflen, inlen); 612 break; 613 case FUSE_READ: 614 EXPECT_EQ(inlen, fih + sizeof(in.body.read)); 615 EXPECT_EQ((size_t)buflen, inlen); 616 break; 617 case FUSE_WRITE: 618 { 619 size_t s; 620 621 if (m_kernel_minor_version >= 9) 622 s = sizeof(in.body.write); 623 else 624 s = FUSE_COMPAT_WRITE_IN_SIZE; 625 // I suppose a 0-byte write should be allowed 626 EXPECT_GE(inlen, fih + s) << "Missing request body"; 627 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size); 628 break; 629 } 630 case FUSE_DESTROY: 631 case FUSE_STATFS: 632 EXPECT_EQ(inlen, fih); 633 EXPECT_EQ((size_t)buflen, inlen); 634 break; 635 case FUSE_RELEASE: 636 EXPECT_EQ(inlen, fih + sizeof(in.body.release)); 637 EXPECT_EQ((size_t)buflen, inlen); 638 break; 639 case FUSE_FSYNC: 640 case FUSE_FSYNCDIR: 641 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync)); 642 EXPECT_EQ((size_t)buflen, inlen); 643 break; 644 case FUSE_SETXATTR: 645 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) << 646 "Missing request body"; 647 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) << 648 "Missing request attribute name"; 649 // No redundant information for checking buflen 650 break; 651 case FUSE_GETXATTR: 652 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) << 653 "Missing request body"; 654 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) << 655 "Missing request attribute name"; 656 // No redundant information for checking buflen 657 break; 658 case FUSE_LISTXATTR: 659 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr)); 660 EXPECT_EQ((size_t)buflen, inlen); 661 break; 662 case FUSE_REMOVEXATTR: 663 EXPECT_GT(inlen, fih) << "Missing request attribute name"; 664 // No redundant information for checking buflen 665 break; 666 case FUSE_FLUSH: 667 EXPECT_EQ(inlen, fih + sizeof(in.body.flush)); 668 EXPECT_EQ((size_t)buflen, inlen); 669 break; 670 case FUSE_INIT: 671 EXPECT_EQ(inlen, fih + sizeof(in.body.init)); 672 EXPECT_EQ((size_t)buflen, inlen); 673 break; 674 case FUSE_OPENDIR: 675 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir)); 676 EXPECT_EQ((size_t)buflen, inlen); 677 break; 678 case FUSE_READDIR: 679 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir)); 680 EXPECT_EQ((size_t)buflen, inlen); 681 break; 682 case FUSE_RELEASEDIR: 683 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir)); 684 EXPECT_EQ((size_t)buflen, inlen); 685 break; 686 case FUSE_GETLK: 687 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk)); 688 EXPECT_EQ((size_t)buflen, inlen); 689 break; 690 case FUSE_SETLK: 691 case FUSE_SETLKW: 692 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk)); 693 EXPECT_EQ((size_t)buflen, inlen); 694 break; 695 case FUSE_ACCESS: 696 EXPECT_EQ(inlen, fih + sizeof(in.body.access)); 697 EXPECT_EQ((size_t)buflen, inlen); 698 break; 699 case FUSE_CREATE: 700 EXPECT_GE(inlen, fih + sizeof(in.body.create)) << 701 "Missing request body"; 702 EXPECT_GT(inlen, fih + sizeof(in.body.create)) << 703 "Missing request filename"; 704 // No redundant information for checking buflen 705 break; 706 case FUSE_INTERRUPT: 707 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt)); 708 EXPECT_EQ((size_t)buflen, inlen); 709 break; 710 case FUSE_FALLOCATE: 711 EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate)); 712 EXPECT_EQ((size_t)buflen, inlen); 713 break; 714 case FUSE_BMAP: 715 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap)); 716 EXPECT_EQ((size_t)buflen, inlen); 717 break; 718 case FUSE_LSEEK: 719 EXPECT_EQ(inlen, fih + sizeof(in.body.lseek)); 720 EXPECT_EQ((size_t)buflen, inlen); 721 break; 722 case FUSE_COPY_FILE_RANGE: 723 EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range)); 724 EXPECT_EQ(0ul, in.body.copy_file_range.flags); 725 EXPECT_EQ((size_t)buflen, inlen); 726 break; 727 case FUSE_NOTIFY_REPLY: 728 case FUSE_BATCH_FORGET: 729 case FUSE_IOCTL: 730 case FUSE_POLL: 731 case FUSE_READDIRPLUS: 732 FAIL() << "Unsupported opcode?"; 733 default: 734 FAIL() << "Unknown opcode " << in.header.opcode; 735 } 736 /* Verify that the ticket's unique value is actually unique. */ 737 if (m_uniques->find(in.header.unique) != m_uniques->end()) 738 FAIL() << "Non-unique \"unique\" value"; 739 m_uniques->insert(in.header.unique); 740 } 741 742 void MockFS::init(uint32_t flags) { 743 ssize_t buflen; 744 745 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 746 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 747 748 read_request(*in, buflen); 749 if (verbosity > 0) 750 debug_request(*in, buflen); 751 audit_request(*in, buflen); 752 ASSERT_EQ(FUSE_INIT, in->header.opcode); 753 754 out->header.unique = in->header.unique; 755 out->header.error = 0; 756 out->body.init.major = FUSE_KERNEL_VERSION; 757 out->body.init.minor = m_kernel_minor_version;; 758 out->body.init.flags = in->body.init.flags & flags; 759 out->body.init.max_write = m_maxwrite; 760 out->body.init.max_readahead = m_maxreadahead; 761 762 if (m_kernel_minor_version < 23) { 763 SET_OUT_HEADER_LEN(*out, init_7_22); 764 } else { 765 out->body.init.time_gran = m_time_gran; 766 SET_OUT_HEADER_LEN(*out, init); 767 } 768 769 write(m_fuse_fd, out.get(), out->header.len); 770 } 771 772 void MockFS::kill_daemon() { 773 m_quit = true; 774 if (m_daemon_id != NULL) 775 pthread_kill(m_daemon_id, SIGUSR1); 776 // Closing the /dev/fuse file descriptor first allows unmount to 777 // succeed even if the daemon doesn't correctly respond to commands 778 // during the unmount sequence. 779 close(m_fuse_fd); 780 m_fuse_fd = -1; 781 } 782 783 void MockFS::loop() { 784 std::vector<std::unique_ptr<mockfs_buf_out>> out; 785 786 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 787 ASSERT_TRUE(in != NULL); 788 while (!m_quit) { 789 ssize_t buflen; 790 791 bzero(in.get(), sizeof(*in)); 792 read_request(*in, buflen); 793 if (m_quit) 794 break; 795 if (verbosity > 0) 796 debug_request(*in, buflen); 797 audit_request(*in, buflen); 798 if (pid_ok((pid_t)in->header.pid)) { 799 process(*in, out); 800 } else { 801 /* 802 * Reject any requests from unknown processes. Because 803 * we actually do mount a filesystem, plenty of 804 * unrelated system daemons may try to access it. 805 */ 806 if (verbosity > 1) 807 printf("\tREJECTED (wrong pid %d)\n", 808 in->header.pid); 809 process_default(*in, out); 810 } 811 for (auto &it: out) 812 write_response(*it); 813 out.clear(); 814 } 815 } 816 817 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen) 818 { 819 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 820 821 out->header.unique = 0; /* 0 means asynchronous notification */ 822 out->header.error = FUSE_NOTIFY_INVAL_ENTRY; 823 out->body.inval_entry.parent = parent; 824 out->body.inval_entry.namelen = namelen; 825 strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry), 826 name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry)); 827 out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) + 828 namelen; 829 debug_response(*out); 830 write_response(*out); 831 return 0; 832 } 833 834 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len) 835 { 836 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 837 838 out->header.unique = 0; /* 0 means asynchronous notification */ 839 out->header.error = FUSE_NOTIFY_INVAL_INODE; 840 out->body.inval_inode.ino = ino; 841 out->body.inval_inode.off = off; 842 out->body.inval_inode.len = len; 843 out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode); 844 debug_response(*out); 845 write_response(*out); 846 return 0; 847 } 848 849 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size) 850 { 851 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 852 853 out->header.unique = 0; /* 0 means asynchronous notification */ 854 out->header.error = FUSE_NOTIFY_STORE; 855 out->body.store.nodeid = ino; 856 out->body.store.offset = off; 857 out->body.store.size = size; 858 bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size); 859 out->header.len = sizeof(out->header) + sizeof(out->body.store) + size; 860 debug_response(*out); 861 write_response(*out); 862 return 0; 863 } 864 865 bool MockFS::pid_ok(pid_t pid) { 866 if (pid == m_pid) { 867 return (true); 868 } else if (pid == m_child_pid) { 869 return (true); 870 } else { 871 struct kinfo_proc *ki; 872 bool ok = false; 873 874 ki = kinfo_getproc(pid); 875 if (ki == NULL) 876 return (false); 877 /* 878 * Allow access by the aio daemon processes so that our tests 879 * can use aio functions 880 */ 881 if (0 == strncmp("aiod", ki->ki_comm, 4)) 882 ok = true; 883 free(ki); 884 return (ok); 885 } 886 } 887 888 void MockFS::process_default(const mockfs_buf_in& in, 889 std::vector<std::unique_ptr<mockfs_buf_out>> &out) 890 { 891 std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out); 892 out0->header.unique = in.header.unique; 893 out0->header.error = -EOPNOTSUPP; 894 out0->header.len = sizeof(out0->header); 895 out.push_back(std::move(out0)); 896 } 897 898 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) { 899 int nready = 0; 900 fd_set readfds; 901 pollfd fds[1]; 902 struct kevent changes[1]; 903 struct kevent events[1]; 904 struct timespec timeout_ts; 905 struct timeval timeout_tv; 906 const int timeout_ms = 999; 907 int timeout_int, nfds; 908 int fuse_fd; 909 910 switch (m_pm) { 911 case BLOCKING: 912 break; 913 case KQ: 914 timeout_ts.tv_sec = 0; 915 timeout_ts.tv_nsec = timeout_ms * 1'000'000; 916 while (nready == 0) { 917 EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, 918 EV_ADD | EV_ONESHOT, 0, 0, 0); 919 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 920 &timeout_ts); 921 if (m_quit) 922 return; 923 } 924 ASSERT_LE(0, nready) << strerror(errno); 925 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 926 if (events[0].flags & EV_ERROR) 927 FAIL() << strerror(events[0].data); 928 else if (events[0].flags & EV_EOF) 929 FAIL() << strerror(events[0].fflags); 930 m_nready = events[0].data; 931 break; 932 case POLL: 933 timeout_int = timeout_ms; 934 fds[0].fd = m_fuse_fd; 935 fds[0].events = POLLIN; 936 while (nready == 0) { 937 nready = poll(fds, 1, timeout_int); 938 if (m_quit) 939 return; 940 } 941 ASSERT_LE(0, nready) << strerror(errno); 942 ASSERT_TRUE(fds[0].revents & POLLIN); 943 break; 944 case SELECT: 945 fuse_fd = m_fuse_fd; 946 if (fuse_fd < 0) 947 break; 948 timeout_tv.tv_sec = 0; 949 timeout_tv.tv_usec = timeout_ms * 1'000; 950 nfds = fuse_fd + 1; 951 while (nready == 0) { 952 FD_ZERO(&readfds); 953 FD_SET(fuse_fd, &readfds); 954 nready = select(nfds, &readfds, NULL, NULL, 955 &timeout_tv); 956 if (m_quit) 957 return; 958 } 959 ASSERT_LE(0, nready) << strerror(errno); 960 ASSERT_TRUE(FD_ISSET(fuse_fd, &readfds)); 961 break; 962 default: 963 FAIL() << "not yet implemented"; 964 } 965 res = read(m_fuse_fd, &in, sizeof(in)); 966 967 if (res < 0 && !m_quit) { 968 m_quit = true; 969 FAIL() << "read: " << strerror(errno); 970 } 971 ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit); 972 /* 973 * Inconsistently, fuse_in_header.len is the size of the entire 974 * request,including header, even though fuse_out_header.len excludes 975 * the size of the header. 976 */ 977 ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit); 978 } 979 980 void MockFS::write_response(const mockfs_buf_out &out) { 981 fd_set writefds; 982 pollfd fds[1]; 983 struct kevent changes[1]; 984 struct kevent events[1]; 985 int nready, nfds; 986 ssize_t r; 987 988 switch (m_pm) { 989 case BLOCKING: 990 break; 991 case KQ: 992 EV_SET(&changes[0], m_fuse_fd, EVFILT_WRITE, 993 EV_ADD | EV_ONESHOT, 0, 0, 0); 994 nready = kevent(m_kq, &changes[0], 1, &events[0], 1, 995 NULL); 996 ASSERT_LE(0, nready) << strerror(errno); 997 ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd); 998 if (events[0].flags & EV_ERROR) 999 FAIL() << strerror(events[0].data); 1000 else if (events[0].flags & EV_EOF) 1001 FAIL() << strerror(events[0].fflags); 1002 m_nready = events[0].data; 1003 break; 1004 case POLL: 1005 fds[0].fd = m_fuse_fd; 1006 fds[0].events = POLLOUT; 1007 nready = poll(fds, 1, INFTIM); 1008 ASSERT_LE(0, nready) << strerror(errno); 1009 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1010 ASSERT_TRUE(fds[0].revents & POLLOUT); 1011 break; 1012 case SELECT: 1013 FD_ZERO(&writefds); 1014 FD_SET(m_fuse_fd, &writefds); 1015 nfds = m_fuse_fd + 1; 1016 nready = select(nfds, NULL, &writefds, NULL, NULL); 1017 ASSERT_LE(0, nready) << strerror(errno); 1018 ASSERT_EQ(1, nready) << "NULL timeout expired?"; 1019 ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds)); 1020 break; 1021 default: 1022 FAIL() << "not yet implemented"; 1023 } 1024 r = write(m_fuse_fd, &out, out.header.len); 1025 if (out.expected_errno) { 1026 ASSERT_EQ(-1, r); 1027 ASSERT_EQ(out.expected_errno, errno) << strerror(errno); 1028 } else { 1029 ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno); 1030 } 1031 } 1032 1033 void* MockFS::service(void *pthr_data) { 1034 MockFS *mock_fs = (MockFS*)pthr_data; 1035 1036 mock_fs->loop(); 1037 1038 return (NULL); 1039 } 1040 1041 void MockFS::unmount() { 1042 ::unmount("mountpoint", 0); 1043 } 1044