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_INTERRUPT: 233 printf(" unique=%" PRIu64, in.body.interrupt.unique); 234 break; 235 case FUSE_LINK: 236 printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid); 237 break; 238 case FUSE_LISTXATTR: 239 printf(" size=%" PRIu32, in.body.listxattr.size); 240 break; 241 case FUSE_LOOKUP: 242 printf(" %s", in.body.lookup); 243 break; 244 case FUSE_LSEEK: 245 switch (in.body.lseek.whence) { 246 case SEEK_HOLE: 247 printf(" SEEK_HOLE offset=%jd", 248 in.body.lseek.offset); 249 break; 250 case SEEK_DATA: 251 printf(" SEEK_DATA offset=%jd", 252 in.body.lseek.offset); 253 break; 254 default: 255 printf(" whence=%u offset=%jd", 256 in.body.lseek.whence, in.body.lseek.offset); 257 break; 258 } 259 break; 260 case FUSE_MKDIR: 261 name = (const char*)in.body.bytes + 262 sizeof(fuse_mkdir_in); 263 printf(" name=%s mode=%#o umask=%#o", name, 264 in.body.mkdir.mode, in.body.mkdir.umask); 265 break; 266 case FUSE_MKNOD: 267 if (m_kernel_minor_version >= 12) 268 name = (const char*)in.body.bytes + 269 sizeof(fuse_mknod_in); 270 else 271 name = (const char*)in.body.bytes + 272 FUSE_COMPAT_MKNOD_IN_SIZE; 273 printf(" mode=%#o rdev=%x umask=%#o name=%s", 274 in.body.mknod.mode, in.body.mknod.rdev, 275 in.body.mknod.umask, name); 276 break; 277 case FUSE_OPEN: 278 printf(" flags=%#x", in.body.open.flags); 279 break; 280 case FUSE_OPENDIR: 281 printf(" flags=%#x", in.body.opendir.flags); 282 break; 283 case FUSE_READ: 284 printf(" offset=%" PRIu64 " size=%u", 285 in.body.read.offset, 286 in.body.read.size); 287 if (verbosity > 1) 288 printf(" flags=%#x", in.body.read.flags); 289 break; 290 case FUSE_READDIR: 291 printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u", 292 in.body.readdir.fh, in.body.readdir.offset, 293 in.body.readdir.size); 294 break; 295 case FUSE_RELEASE: 296 printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64, 297 in.body.release.fh, 298 in.body.release.flags, 299 in.body.release.lock_owner); 300 break; 301 case FUSE_RENAME: 302 { 303 const char *src = (const char*)in.body.bytes + 304 sizeof(fuse_rename_in); 305 const char *dst = src + strlen(src) + 1; 306 printf(" src=%s newdir=%" PRIu64 " dst=%s", 307 src, in.body.rename.newdir, dst); 308 } 309 break; 310 case FUSE_SETATTR: 311 if (verbosity <= 1) { 312 printf(" valid=%#x", in.body.setattr.valid); 313 break; 314 } 315 if (in.body.setattr.valid & FATTR_MODE) 316 printf(" mode=%#o", in.body.setattr.mode); 317 if (in.body.setattr.valid & FATTR_UID) 318 printf(" uid=%u", in.body.setattr.uid); 319 if (in.body.setattr.valid & FATTR_GID) 320 printf(" gid=%u", in.body.setattr.gid); 321 if (in.body.setattr.valid & FATTR_SIZE) 322 printf(" size=%" PRIu64, in.body.setattr.size); 323 if (in.body.setattr.valid & FATTR_ATIME) 324 printf(" atime=%" PRIu64 ".%u", 325 in.body.setattr.atime, 326 in.body.setattr.atimensec); 327 if (in.body.setattr.valid & FATTR_MTIME) 328 printf(" mtime=%" PRIu64 ".%u", 329 in.body.setattr.mtime, 330 in.body.setattr.mtimensec); 331 if (in.body.setattr.valid & FATTR_FH) 332 printf(" fh=%" PRIu64 "", in.body.setattr.fh); 333 break; 334 case FUSE_SETLK: 335 printf(" fh=%#" PRIx64 " owner=%" PRIu64 336 " type=%u pid=%u", 337 in.body.setlk.fh, in.body.setlk.owner, 338 in.body.setlk.lk.type, 339 in.body.setlk.lk.pid); 340 if (verbosity >= 2) { 341 printf(" range=[%" PRIu64 "-%" PRIu64 "]", 342 in.body.setlk.lk.start, 343 in.body.setlk.lk.end); 344 } 345 break; 346 case FUSE_SETXATTR: 347 /* 348 * In theory neither the xattr name and value need be 349 * ASCII, but in this test suite they always are. 350 */ 351 name = (const char*)in.body.bytes + 352 sizeof(fuse_setxattr_in); 353 value = name + strlen(name) + 1; 354 printf(" %s=%s", name, value); 355 break; 356 case FUSE_WRITE: 357 printf(" fh=%#" PRIx64 " offset=%" PRIu64 358 " size=%u write_flags=%u", 359 in.body.write.fh, 360 in.body.write.offset, in.body.write.size, 361 in.body.write.write_flags); 362 if (verbosity > 1) 363 printf(" flags=%#x", in.body.write.flags); 364 break; 365 default: 366 break; 367 } 368 printf("\n"); 369 } 370 371 /* 372 * Debug a FUSE response. 373 * 374 * This is mostly useful for asynchronous notifications, which don't correspond 375 * to any request 376 */ 377 void MockFS::debug_response(const mockfs_buf_out &out) { 378 const char *name; 379 380 if (verbosity == 0) 381 return; 382 383 switch (out.header.error) { 384 case FUSE_NOTIFY_INVAL_ENTRY: 385 name = (const char*)out.body.bytes + 386 sizeof(fuse_notify_inval_entry_out); 387 printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n", 388 out.body.inval_entry.parent, name); 389 break; 390 case FUSE_NOTIFY_INVAL_INODE: 391 printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64 392 " len=%" PRIi64 "\n", 393 out.body.inval_inode.ino, 394 out.body.inval_inode.off, 395 out.body.inval_inode.len); 396 break; 397 case FUSE_NOTIFY_STORE: 398 printf("<- STORE ino=%" PRIu64 " off=%" PRIu64 399 " size=%" PRIu32 "\n", 400 out.body.store.nodeid, 401 out.body.store.offset, 402 out.body.store.size); 403 break; 404 default: 405 break; 406 } 407 } 408 409 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions, 410 bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags, 411 uint32_t kernel_minor_version, uint32_t max_write, bool async, 412 bool noclusterr, unsigned time_gran, bool nointr, bool noatime, 413 const char *fsname, const char *subtype) 414 { 415 struct sigaction sa; 416 struct iovec *iov = NULL; 417 int iovlen = 0; 418 char fdstr[15]; 419 const bool trueval = true; 420 421 m_daemon_id = NULL; 422 m_expected_write_errno = 0; 423 m_kernel_minor_version = kernel_minor_version; 424 m_maxreadahead = max_readahead; 425 m_maxwrite = MIN(max_write, max_max_write); 426 m_nready = -1; 427 m_pm = pm; 428 m_time_gran = time_gran; 429 m_quit = false; 430 m_last_unique = 0; 431 if (m_pm == KQ) 432 m_kq = kqueue(); 433 else 434 m_kq = -1; 435 436 /* 437 * Kyua sets pwd to a testcase-unique tempdir; no need to use 438 * mkdtemp 439 */ 440 /* 441 * googletest doesn't allow ASSERT_ in constructors, so we must throw 442 * instead. 443 */ 444 if (mkdir("mountpoint" , 0755) && errno != EEXIST) 445 throw(std::system_error(errno, std::system_category(), 446 "Couldn't make mountpoint directory")); 447 448 switch (m_pm) { 449 case BLOCKING: 450 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR); 451 break; 452 default: 453 m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK); 454 break; 455 } 456 if (m_fuse_fd < 0) 457 throw(std::system_error(errno, std::system_category(), 458 "Couldn't open /dev/fuse")); 459 460 m_pid = getpid(); 461 m_child_pid = -1; 462 463 build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1); 464 build_iovec(&iov, &iovlen, "fspath", 465 __DECONST(void *, "mountpoint"), -1); 466 build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1); 467 sprintf(fdstr, "%d", m_fuse_fd); 468 build_iovec(&iov, &iovlen, "fd", fdstr, -1); 469 if (allow_other) { 470 build_iovec(&iov, &iovlen, "allow_other", 471 __DECONST(void*, &trueval), sizeof(bool)); 472 } 473 if (default_permissions) { 474 build_iovec(&iov, &iovlen, "default_permissions", 475 __DECONST(void*, &trueval), sizeof(bool)); 476 } 477 if (push_symlinks_in) { 478 build_iovec(&iov, &iovlen, "push_symlinks_in", 479 __DECONST(void*, &trueval), sizeof(bool)); 480 } 481 if (ro) { 482 build_iovec(&iov, &iovlen, "ro", 483 __DECONST(void*, &trueval), sizeof(bool)); 484 } 485 if (async) { 486 build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval), 487 sizeof(bool)); 488 } 489 if (noatime) { 490 build_iovec(&iov, &iovlen, "noatime", 491 __DECONST(void*, &trueval), sizeof(bool)); 492 } 493 if (noclusterr) { 494 build_iovec(&iov, &iovlen, "noclusterr", 495 __DECONST(void*, &trueval), sizeof(bool)); 496 } 497 if (nointr) { 498 build_iovec(&iov, &iovlen, "nointr", 499 __DECONST(void*, &trueval), sizeof(bool)); 500 } else { 501 build_iovec(&iov, &iovlen, "intr", 502 __DECONST(void*, &trueval), sizeof(bool)); 503 } 504 if (*fsname) { 505 build_iovec(&iov, &iovlen, "fsname=", 506 __DECONST(void*, fsname), -1); 507 } 508 if (*subtype) { 509 build_iovec(&iov, &iovlen, "subtype=", 510 __DECONST(void*, subtype), -1); 511 } 512 if (nmount(iov, iovlen, 0)) 513 throw(std::system_error(errno, std::system_category(), 514 "Couldn't mount filesystem")); 515 516 // Setup default handler 517 ON_CALL(*this, process(_, _)) 518 .WillByDefault(Invoke(this, &MockFS::process_default)); 519 520 init(flags); 521 bzero(&sa, sizeof(sa)); 522 sa.sa_handler = sigint_handler; 523 sa.sa_flags = 0; /* Don't set SA_RESTART! */ 524 if (0 != sigaction(SIGUSR1, &sa, NULL)) 525 throw(std::system_error(errno, std::system_category(), 526 "Couldn't handle SIGUSR1")); 527 if (pthread_create(&m_daemon_id, NULL, service, (void*)this)) 528 throw(std::system_error(errno, std::system_category(), 529 "Couldn't Couldn't start fuse thread")); 530 } 531 532 MockFS::~MockFS() { 533 kill_daemon(); 534 if (m_daemon_id != NULL) { 535 pthread_join(m_daemon_id, NULL); 536 m_daemon_id = NULL; 537 } 538 ::unmount("mountpoint", MNT_FORCE); 539 rmdir("mountpoint"); 540 if (m_kq >= 0) 541 close(m_kq); 542 } 543 544 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) { 545 uint32_t inlen = in.header.len; 546 size_t fih = sizeof(in.header); 547 switch (in.header.opcode) { 548 case FUSE_LOOKUP: 549 case FUSE_RMDIR: 550 case FUSE_SYMLINK: 551 case FUSE_UNLINK: 552 EXPECT_GT(inlen, fih) << "Missing request filename"; 553 // No redundant information for checking buflen 554 break; 555 case FUSE_FORGET: 556 EXPECT_EQ(inlen, fih + sizeof(in.body.forget)); 557 EXPECT_EQ((size_t)buflen, inlen); 558 break; 559 case FUSE_GETATTR: 560 EXPECT_EQ(inlen, fih + sizeof(in.body.getattr)); 561 EXPECT_EQ((size_t)buflen, inlen); 562 break; 563 case FUSE_SETATTR: 564 EXPECT_EQ(inlen, fih + sizeof(in.body.setattr)); 565 EXPECT_EQ((size_t)buflen, inlen); 566 break; 567 case FUSE_READLINK: 568 EXPECT_EQ(inlen, fih) << "Unexpected request body"; 569 EXPECT_EQ((size_t)buflen, inlen); 570 break; 571 case FUSE_MKNOD: 572 { 573 size_t s; 574 if (m_kernel_minor_version >= 12) 575 s = sizeof(in.body.mknod); 576 else 577 s = FUSE_COMPAT_MKNOD_IN_SIZE; 578 EXPECT_GE(inlen, fih + s) << "Missing request body"; 579 EXPECT_GT(inlen, fih + s) << "Missing request filename"; 580 // No redundant information for checking buflen 581 break; 582 } 583 case FUSE_MKDIR: 584 EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) << 585 "Missing request body"; 586 EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) << 587 "Missing request filename"; 588 // No redundant information for checking buflen 589 break; 590 case FUSE_RENAME: 591 EXPECT_GE(inlen, fih + sizeof(in.body.rename)) << 592 "Missing request body"; 593 EXPECT_GT(inlen, fih + sizeof(in.body.rename)) << 594 "Missing request filename"; 595 // No redundant information for checking buflen 596 break; 597 case FUSE_LINK: 598 EXPECT_GE(inlen, fih + sizeof(in.body.link)) << 599 "Missing request body"; 600 EXPECT_GT(inlen, fih + sizeof(in.body.link)) << 601 "Missing request filename"; 602 // No redundant information for checking buflen 603 break; 604 case FUSE_OPEN: 605 EXPECT_EQ(inlen, fih + sizeof(in.body.open)); 606 EXPECT_EQ((size_t)buflen, inlen); 607 break; 608 case FUSE_READ: 609 EXPECT_EQ(inlen, fih + sizeof(in.body.read)); 610 EXPECT_EQ((size_t)buflen, inlen); 611 break; 612 case FUSE_WRITE: 613 { 614 size_t s; 615 616 if (m_kernel_minor_version >= 9) 617 s = sizeof(in.body.write); 618 else 619 s = FUSE_COMPAT_WRITE_IN_SIZE; 620 // I suppose a 0-byte write should be allowed 621 EXPECT_GE(inlen, fih + s) << "Missing request body"; 622 EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size); 623 break; 624 } 625 case FUSE_DESTROY: 626 case FUSE_STATFS: 627 EXPECT_EQ(inlen, fih); 628 EXPECT_EQ((size_t)buflen, inlen); 629 break; 630 case FUSE_RELEASE: 631 EXPECT_EQ(inlen, fih + sizeof(in.body.release)); 632 EXPECT_EQ((size_t)buflen, inlen); 633 break; 634 case FUSE_FSYNC: 635 case FUSE_FSYNCDIR: 636 EXPECT_EQ(inlen, fih + sizeof(in.body.fsync)); 637 EXPECT_EQ((size_t)buflen, inlen); 638 break; 639 case FUSE_SETXATTR: 640 EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) << 641 "Missing request body"; 642 EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) << 643 "Missing request attribute name"; 644 // No redundant information for checking buflen 645 break; 646 case FUSE_GETXATTR: 647 EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) << 648 "Missing request body"; 649 EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) << 650 "Missing request attribute name"; 651 // No redundant information for checking buflen 652 break; 653 case FUSE_LISTXATTR: 654 EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr)); 655 EXPECT_EQ((size_t)buflen, inlen); 656 break; 657 case FUSE_REMOVEXATTR: 658 EXPECT_GT(inlen, fih) << "Missing request attribute name"; 659 // No redundant information for checking buflen 660 break; 661 case FUSE_FLUSH: 662 EXPECT_EQ(inlen, fih + sizeof(in.body.flush)); 663 EXPECT_EQ((size_t)buflen, inlen); 664 break; 665 case FUSE_INIT: 666 EXPECT_EQ(inlen, fih + sizeof(in.body.init)); 667 EXPECT_EQ((size_t)buflen, inlen); 668 break; 669 case FUSE_OPENDIR: 670 EXPECT_EQ(inlen, fih + sizeof(in.body.opendir)); 671 EXPECT_EQ((size_t)buflen, inlen); 672 break; 673 case FUSE_READDIR: 674 EXPECT_EQ(inlen, fih + sizeof(in.body.readdir)); 675 EXPECT_EQ((size_t)buflen, inlen); 676 break; 677 case FUSE_RELEASEDIR: 678 EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir)); 679 EXPECT_EQ((size_t)buflen, inlen); 680 break; 681 case FUSE_GETLK: 682 EXPECT_EQ(inlen, fih + sizeof(in.body.getlk)); 683 EXPECT_EQ((size_t)buflen, inlen); 684 break; 685 case FUSE_SETLK: 686 case FUSE_SETLKW: 687 EXPECT_EQ(inlen, fih + sizeof(in.body.setlk)); 688 EXPECT_EQ((size_t)buflen, inlen); 689 break; 690 case FUSE_ACCESS: 691 EXPECT_EQ(inlen, fih + sizeof(in.body.access)); 692 EXPECT_EQ((size_t)buflen, inlen); 693 break; 694 case FUSE_CREATE: 695 EXPECT_GE(inlen, fih + sizeof(in.body.create)) << 696 "Missing request body"; 697 EXPECT_GT(inlen, fih + sizeof(in.body.create)) << 698 "Missing request filename"; 699 // No redundant information for checking buflen 700 break; 701 case FUSE_INTERRUPT: 702 EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt)); 703 EXPECT_EQ((size_t)buflen, inlen); 704 break; 705 case FUSE_FALLOCATE: 706 EXPECT_EQ(inlen, fih + sizeof(in.body.fallocate)); 707 EXPECT_EQ((size_t)buflen, inlen); 708 break; 709 case FUSE_BMAP: 710 EXPECT_EQ(inlen, fih + sizeof(in.body.bmap)); 711 EXPECT_EQ((size_t)buflen, inlen); 712 break; 713 case FUSE_LSEEK: 714 EXPECT_EQ(inlen, fih + sizeof(in.body.lseek)); 715 EXPECT_EQ((size_t)buflen, inlen); 716 break; 717 case FUSE_COPY_FILE_RANGE: 718 EXPECT_EQ(inlen, fih + sizeof(in.body.copy_file_range)); 719 EXPECT_EQ(0ul, in.body.copy_file_range.flags); 720 EXPECT_EQ((size_t)buflen, inlen); 721 break; 722 case FUSE_NOTIFY_REPLY: 723 case FUSE_BATCH_FORGET: 724 case FUSE_IOCTL: 725 case FUSE_POLL: 726 case FUSE_READDIRPLUS: 727 FAIL() << "Unsupported opcode?"; 728 default: 729 FAIL() << "Unknown opcode " << in.header.opcode; 730 } 731 /* 732 * Check that the ticket's unique value is sequential. Technically it 733 * doesn't need to be sequential, merely unique. But the current 734 * fusefs driver _does_ make it sequential, and that's easy to check 735 * for. 736 */ 737 if (in.header.unique != ++m_last_unique) 738 FAIL() << "Non-sequential unique value"; 739 } 740 741 void MockFS::init(uint32_t flags) { 742 ssize_t buflen; 743 744 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 745 std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out); 746 747 read_request(*in, buflen); 748 if (verbosity > 0) 749 debug_request(*in, buflen); 750 audit_request(*in, buflen); 751 ASSERT_EQ(FUSE_INIT, in->header.opcode); 752 753 out->header.unique = in->header.unique; 754 out->header.error = 0; 755 out->body.init.major = FUSE_KERNEL_VERSION; 756 out->body.init.minor = m_kernel_minor_version;; 757 out->body.init.flags = in->body.init.flags & flags; 758 out->body.init.max_write = m_maxwrite; 759 out->body.init.max_readahead = m_maxreadahead; 760 761 if (m_kernel_minor_version < 23) { 762 SET_OUT_HEADER_LEN(*out, init_7_22); 763 } else { 764 out->body.init.time_gran = m_time_gran; 765 SET_OUT_HEADER_LEN(*out, init); 766 } 767 768 write(m_fuse_fd, out.get(), out->header.len); 769 } 770 771 void MockFS::kill_daemon() { 772 m_quit = true; 773 if (m_daemon_id != NULL) 774 pthread_kill(m_daemon_id, SIGUSR1); 775 // Closing the /dev/fuse file descriptor first allows unmount to 776 // succeed even if the daemon doesn't correctly respond to commands 777 // during the unmount sequence. 778 close(m_fuse_fd); 779 m_fuse_fd = -1; 780 } 781 782 void MockFS::loop() { 783 std::vector<std::unique_ptr<mockfs_buf_out>> out; 784 785 std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in); 786 ASSERT_TRUE(in != NULL); 787 while (!m_quit) { 788 ssize_t buflen; 789 790 bzero(in.get(), sizeof(*in)); 791 read_request(*in, buflen); 792 m_expected_write_errno = 0; 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 (m_expected_write_errno) { 1026 ASSERT_EQ(-1, r); 1027 ASSERT_EQ(m_expected_write_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