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