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