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