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