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 #include <sys/mman.h> 36 #include <sys/module.h> 37 #include <sys/sysctl.h> 38 #include <sys/wait.h> 39 40 #include <dirent.h> 41 #include <fcntl.h> 42 #include <grp.h> 43 #include <pwd.h> 44 #include <semaphore.h> 45 #include <unistd.h> 46 } 47 48 #include <gtest/gtest.h> 49 50 #include "mockfs.hh" 51 #include "utils.hh" 52 53 using namespace testing; 54 55 /* 56 * The default max_write is set to this formula in libfuse, though 57 * individual filesystems can lower it. The "- 4096" was added in 58 * commit 154ffe2, with the commit message "fix". 59 */ 60 const uint32_t libfuse_max_write = 32 * getpagesize() + 0x1000 - 4096; 61 62 /* 63 * Set the default max_write to a distinct value from MAXPHYS to catch bugs 64 * that confuse the two. 65 */ 66 const uint32_t default_max_write = MIN(libfuse_max_write, MAXPHYS / 2); 67 68 69 /* Check that fusefs(4) is accessible and the current user can mount(2) */ 70 void check_environment() 71 { 72 const char *devnode = "/dev/fuse"; 73 const char *bsdextended_node = "security.mac.bsdextended.enabled"; 74 int bsdextended_val = 0; 75 size_t bsdextended_size = sizeof(bsdextended_val); 76 int bsdextended_found; 77 const char *usermount_node = "vfs.usermount"; 78 int usermount_val = 0; 79 size_t usermount_size = sizeof(usermount_val); 80 if (eaccess(devnode, R_OK | W_OK)) { 81 if (errno == ENOENT) { 82 GTEST_SKIP() << devnode << " does not exist"; 83 } else if (errno == EACCES) { 84 GTEST_SKIP() << devnode << 85 " is not accessible by the current user"; 86 } else { 87 GTEST_SKIP() << strerror(errno); 88 } 89 } 90 // mac_bsdextended(4), when enabled, generates many more GETATTR 91 // operations. The fusefs tests' expectations don't account for those, 92 // and adding extra code to handle them obfuscates the real purpose of 93 // the tests. Better just to skip the fusefs tests if mac_bsdextended 94 // is enabled. 95 bsdextended_found = sysctlbyname(bsdextended_node, &bsdextended_val, 96 &bsdextended_size, NULL, 0); 97 if (bsdextended_found == 0 && bsdextended_val != 0) 98 GTEST_SKIP() << 99 "The fusefs tests are incompatible with mac_bsdextended."; 100 ASSERT_EQ(sysctlbyname(usermount_node, &usermount_val, &usermount_size, 101 NULL, 0), 102 0); 103 if (geteuid() != 0 && !usermount_val) 104 GTEST_SKIP() << "current user is not allowed to mount"; 105 } 106 107 const char *cache_mode_to_s(enum cache_mode cm) { 108 switch (cm) { 109 case Uncached: 110 return "Uncached"; 111 case Writethrough: 112 return "Writethrough"; 113 case Writeback: 114 return "Writeback"; 115 case WritebackAsync: 116 return "WritebackAsync"; 117 default: 118 return "Unknown"; 119 } 120 } 121 122 bool is_unsafe_aio_enabled(void) { 123 const char *node = "vfs.aio.enable_unsafe"; 124 int val = 0; 125 size_t size = sizeof(val); 126 127 if (sysctlbyname(node, &val, &size, NULL, 0)) { 128 perror("sysctlbyname"); 129 return (false); 130 } 131 return (val != 0); 132 } 133 134 class FuseEnv: public Environment { 135 virtual void SetUp() { 136 } 137 }; 138 139 void FuseTest::SetUp() { 140 const char *maxbcachebuf_node = "vfs.maxbcachebuf"; 141 const char *maxphys_node = "kern.maxphys"; 142 int val = 0; 143 size_t size = sizeof(val); 144 145 /* 146 * XXX check_environment should be called from FuseEnv::SetUp, but 147 * can't due to https://github.com/google/googletest/issues/2189 148 */ 149 check_environment(); 150 if (IsSkipped()) 151 return; 152 153 ASSERT_EQ(0, sysctlbyname(maxbcachebuf_node, &val, &size, NULL, 0)) 154 << strerror(errno); 155 m_maxbcachebuf = val; 156 ASSERT_EQ(0, sysctlbyname(maxphys_node, &val, &size, NULL, 0)) 157 << strerror(errno); 158 m_maxphys = val; 159 160 try { 161 m_mock = new MockFS(m_maxreadahead, m_allow_other, 162 m_default_permissions, m_push_symlinks_in, m_ro, 163 m_pm, m_init_flags, m_kernel_minor_version, 164 m_maxwrite, m_async, m_noclusterr, m_time_gran, 165 m_nointr); 166 /* 167 * FUSE_ACCESS is called almost universally. Expecting it in 168 * each test case would be super-annoying. Instead, set a 169 * default expectation for FUSE_ACCESS and return ENOSYS. 170 * 171 * Individual test cases can override this expectation since 172 * googlemock evaluates expectations in LIFO order. 173 */ 174 EXPECT_CALL(*m_mock, process( 175 ResultOf([=](auto in) { 176 return (in.header.opcode == FUSE_ACCESS); 177 }, Eq(true)), 178 _) 179 ).Times(AnyNumber()) 180 .WillRepeatedly(Invoke(ReturnErrno(ENOSYS))); 181 /* 182 * FUSE_BMAP is called for most test cases that read data. Set 183 * a default expectation and return ENOSYS. 184 * 185 * Individual test cases can override this expectation since 186 * googlemock evaluates expectations in LIFO order. 187 */ 188 EXPECT_CALL(*m_mock, process( 189 ResultOf([=](auto in) { 190 return (in.header.opcode == FUSE_BMAP); 191 }, Eq(true)), 192 _) 193 ).Times(AnyNumber()) 194 .WillRepeatedly(Invoke(ReturnErrno(ENOSYS))); 195 } catch (std::system_error err) { 196 FAIL() << err.what(); 197 } 198 } 199 200 void 201 FuseTest::expect_access(uint64_t ino, mode_t access_mode, int error) 202 { 203 EXPECT_CALL(*m_mock, process( 204 ResultOf([=](auto in) { 205 return (in.header.opcode == FUSE_ACCESS && 206 in.header.nodeid == ino && 207 in.body.access.mask == access_mode); 208 }, Eq(true)), 209 _) 210 ).WillOnce(Invoke(ReturnErrno(error))); 211 } 212 213 void 214 FuseTest::expect_destroy(int error) 215 { 216 EXPECT_CALL(*m_mock, process( 217 ResultOf([=](auto in) { 218 return (in.header.opcode == FUSE_DESTROY); 219 }, Eq(true)), 220 _) 221 ).WillOnce(Invoke( ReturnImmediate([&](auto in, auto& out) { 222 m_mock->m_quit = true; 223 out.header.len = sizeof(out.header); 224 out.header.unique = in.header.unique; 225 out.header.error = -error; 226 }))); 227 } 228 229 void 230 FuseTest::expect_flush(uint64_t ino, int times, ProcessMockerT r) 231 { 232 EXPECT_CALL(*m_mock, process( 233 ResultOf([=](auto in) { 234 return (in.header.opcode == FUSE_FLUSH && 235 in.header.nodeid == ino); 236 }, Eq(true)), 237 _) 238 ).Times(times) 239 .WillRepeatedly(Invoke(r)); 240 } 241 242 void 243 FuseTest::expect_forget(uint64_t ino, uint64_t nlookup, sem_t *sem) 244 { 245 EXPECT_CALL(*m_mock, process( 246 ResultOf([=](auto in) { 247 return (in.header.opcode == FUSE_FORGET && 248 in.header.nodeid == ino && 249 in.body.forget.nlookup == nlookup); 250 }, Eq(true)), 251 _) 252 ).WillOnce(Invoke([=](auto in __unused, auto &out __unused) { 253 if (sem != NULL) 254 sem_post(sem); 255 /* FUSE_FORGET has no response! */ 256 })); 257 } 258 259 void FuseTest::expect_getattr(uint64_t ino, uint64_t size) 260 { 261 EXPECT_CALL(*m_mock, process( 262 ResultOf([=](auto in) { 263 return (in.header.opcode == FUSE_GETATTR && 264 in.header.nodeid == ino); 265 }, Eq(true)), 266 _) 267 ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 268 SET_OUT_HEADER_LEN(out, attr); 269 out.body.attr.attr.ino = ino; // Must match nodeid 270 out.body.attr.attr.mode = S_IFREG | 0644; 271 out.body.attr.attr.size = size; 272 out.body.attr.attr_valid = UINT64_MAX; 273 }))); 274 } 275 276 void FuseTest::expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r) 277 { 278 EXPECT_CALL(*m_mock, process( 279 ResultOf([=](auto in) { 280 const char *a = (const char*)in.body.bytes + 281 sizeof(fuse_getxattr_in); 282 return (in.header.opcode == FUSE_GETXATTR && 283 in.header.nodeid == ino && 284 0 == strcmp(attr, a)); 285 }, Eq(true)), 286 _) 287 ).WillOnce(Invoke(r)); 288 } 289 290 void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode, 291 uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid) 292 { 293 EXPECT_LOOKUP(FUSE_ROOT_ID, relpath) 294 .Times(times) 295 .WillRepeatedly(Invoke( 296 ReturnImmediate([=](auto in __unused, auto& out) { 297 SET_OUT_HEADER_LEN(out, entry); 298 out.body.entry.attr.mode = mode; 299 out.body.entry.nodeid = ino; 300 out.body.entry.attr.nlink = 1; 301 out.body.entry.attr_valid = attr_valid; 302 out.body.entry.attr.size = size; 303 out.body.entry.attr.uid = uid; 304 out.body.entry.attr.gid = gid; 305 }))); 306 } 307 308 void FuseTest::expect_lookup_7_8(const char *relpath, uint64_t ino, mode_t mode, 309 uint64_t size, int times, uint64_t attr_valid, uid_t uid, gid_t gid) 310 { 311 EXPECT_LOOKUP(FUSE_ROOT_ID, relpath) 312 .Times(times) 313 .WillRepeatedly(Invoke( 314 ReturnImmediate([=](auto in __unused, auto& out) { 315 SET_OUT_HEADER_LEN(out, entry_7_8); 316 out.body.entry.attr.mode = mode; 317 out.body.entry.nodeid = ino; 318 out.body.entry.attr.nlink = 1; 319 out.body.entry.attr_valid = attr_valid; 320 out.body.entry.attr.size = size; 321 out.body.entry.attr.uid = uid; 322 out.body.entry.attr.gid = gid; 323 }))); 324 } 325 326 void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times) 327 { 328 EXPECT_CALL(*m_mock, process( 329 ResultOf([=](auto in) { 330 return (in.header.opcode == FUSE_OPEN && 331 in.header.nodeid == ino); 332 }, Eq(true)), 333 _) 334 ).Times(times) 335 .WillRepeatedly(Invoke( 336 ReturnImmediate([=](auto in __unused, auto& out) { 337 out.header.len = sizeof(out.header); 338 SET_OUT_HEADER_LEN(out, open); 339 out.body.open.fh = FH; 340 out.body.open.open_flags = flags; 341 }))); 342 } 343 344 void FuseTest::expect_opendir(uint64_t ino) 345 { 346 /* opendir(3) calls fstatfs */ 347 EXPECT_CALL(*m_mock, process( 348 ResultOf([](auto in) { 349 return (in.header.opcode == FUSE_STATFS); 350 }, Eq(true)), 351 _) 352 ).WillRepeatedly(Invoke( 353 ReturnImmediate([=](auto i __unused, auto& out) { 354 SET_OUT_HEADER_LEN(out, statfs); 355 }))); 356 357 EXPECT_CALL(*m_mock, process( 358 ResultOf([=](auto in) { 359 return (in.header.opcode == FUSE_OPENDIR && 360 in.header.nodeid == ino); 361 }, Eq(true)), 362 _) 363 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 364 out.header.len = sizeof(out.header); 365 SET_OUT_HEADER_LEN(out, open); 366 out.body.open.fh = FH; 367 }))); 368 } 369 370 void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize, 371 uint64_t osize, const void *contents, int flags) 372 { 373 EXPECT_CALL(*m_mock, process( 374 ResultOf([=](auto in) { 375 return (in.header.opcode == FUSE_READ && 376 in.header.nodeid == ino && 377 in.body.read.fh == FH && 378 in.body.read.offset == offset && 379 in.body.read.size == isize && 380 flags == -1 ? 381 (in.body.read.flags == O_RDONLY || 382 in.body.read.flags == O_RDWR) 383 : in.body.read.flags == (uint32_t)flags); 384 }, Eq(true)), 385 _) 386 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 387 out.header.len = sizeof(struct fuse_out_header) + osize; 388 memmove(out.body.bytes, contents, osize); 389 }))).RetiresOnSaturation(); 390 } 391 392 void FuseTest::expect_readdir(uint64_t ino, uint64_t off, 393 std::vector<struct dirent> &ents) 394 { 395 EXPECT_CALL(*m_mock, process( 396 ResultOf([=](auto in) { 397 return (in.header.opcode == FUSE_READDIR && 398 in.header.nodeid == ino && 399 in.body.readdir.fh == FH && 400 in.body.readdir.offset == off); 401 }, Eq(true)), 402 _) 403 ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 404 struct fuse_dirent *fde = (struct fuse_dirent*)&(out.body); 405 int i = 0; 406 407 out.header.error = 0; 408 out.header.len = 0; 409 410 for (const auto& it: ents) { 411 size_t entlen, entsize; 412 413 fde->ino = it.d_fileno; 414 fde->off = it.d_off; 415 fde->type = it.d_type; 416 fde->namelen = it.d_namlen; 417 strncpy(fde->name, it.d_name, it.d_namlen); 418 entlen = FUSE_NAME_OFFSET + fde->namelen; 419 entsize = FUSE_DIRENT_SIZE(fde); 420 /* 421 * The FUSE protocol does not require zeroing out the 422 * unused portion of the name. But it's a good 423 * practice to prevent information disclosure to the 424 * FUSE client, even though the client is usually the 425 * kernel 426 */ 427 memset(fde->name + fde->namelen, 0, entsize - entlen); 428 if (out.header.len + entsize > in.body.read.size) { 429 printf("Overflow in readdir expectation: i=%d\n" 430 , i); 431 break; 432 } 433 out.header.len += entsize; 434 fde = (struct fuse_dirent*) 435 ((intmax_t*)fde + entsize / sizeof(intmax_t)); 436 i++; 437 } 438 out.header.len += sizeof(out.header); 439 }))); 440 441 } 442 void FuseTest::expect_release(uint64_t ino, uint64_t fh) 443 { 444 EXPECT_CALL(*m_mock, process( 445 ResultOf([=](auto in) { 446 return (in.header.opcode == FUSE_RELEASE && 447 in.header.nodeid == ino && 448 in.body.release.fh == fh); 449 }, Eq(true)), 450 _) 451 ).WillOnce(Invoke(ReturnErrno(0))); 452 } 453 454 void FuseTest::expect_releasedir(uint64_t ino, ProcessMockerT r) 455 { 456 EXPECT_CALL(*m_mock, process( 457 ResultOf([=](auto in) { 458 return (in.header.opcode == FUSE_RELEASEDIR && 459 in.header.nodeid == ino && 460 in.body.release.fh == FH); 461 }, Eq(true)), 462 _) 463 ).WillOnce(Invoke(r)); 464 } 465 466 void FuseTest::expect_unlink(uint64_t parent, const char *path, int error) 467 { 468 EXPECT_CALL(*m_mock, process( 469 ResultOf([=](auto in) { 470 return (in.header.opcode == FUSE_UNLINK && 471 0 == strcmp(path, in.body.unlink) && 472 in.header.nodeid == parent); 473 }, Eq(true)), 474 _) 475 ).WillOnce(Invoke(ReturnErrno(error))); 476 } 477 478 void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 479 uint64_t osize, uint32_t flags_set, uint32_t flags_unset, 480 const void *contents) 481 { 482 EXPECT_CALL(*m_mock, process( 483 ResultOf([=](auto in) { 484 const char *buf = (const char*)in.body.bytes + 485 sizeof(struct fuse_write_in); 486 bool pid_ok; 487 uint32_t wf = in.body.write.write_flags; 488 489 if (wf & FUSE_WRITE_CACHE) 490 pid_ok = true; 491 else 492 pid_ok = (pid_t)in.header.pid == getpid(); 493 494 return (in.header.opcode == FUSE_WRITE && 495 in.header.nodeid == ino && 496 in.body.write.fh == FH && 497 in.body.write.offset == offset && 498 in.body.write.size == isize && 499 pid_ok && 500 (wf & flags_set) == flags_set && 501 (wf & flags_unset) == 0 && 502 (in.body.write.flags == O_WRONLY || 503 in.body.write.flags == O_RDWR) && 504 0 == bcmp(buf, contents, isize)); 505 }, Eq(true)), 506 _) 507 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 508 SET_OUT_HEADER_LEN(out, write); 509 out.body.write.size = osize; 510 }))); 511 } 512 513 void FuseTest::expect_write_7_8(uint64_t ino, uint64_t offset, uint64_t isize, 514 uint64_t osize, const void *contents) 515 { 516 EXPECT_CALL(*m_mock, process( 517 ResultOf([=](auto in) { 518 const char *buf = (const char*)in.body.bytes + 519 FUSE_COMPAT_WRITE_IN_SIZE; 520 bool pid_ok = (pid_t)in.header.pid == getpid(); 521 return (in.header.opcode == FUSE_WRITE && 522 in.header.nodeid == ino && 523 in.body.write.fh == FH && 524 in.body.write.offset == offset && 525 in.body.write.size == isize && 526 pid_ok && 527 0 == bcmp(buf, contents, isize)); 528 }, Eq(true)), 529 _) 530 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 531 SET_OUT_HEADER_LEN(out, write); 532 out.body.write.size = osize; 533 }))); 534 } 535 536 void 537 get_unprivileged_id(uid_t *uid, gid_t *gid) 538 { 539 struct passwd *pw; 540 struct group *gr; 541 542 /* 543 * First try "tests", Kyua's default unprivileged user. XXX after 544 * GoogleTest gains a proper Kyua wrapper, get this with the Kyua API 545 */ 546 pw = getpwnam("tests"); 547 if (pw == NULL) { 548 /* Fall back to "nobody" */ 549 pw = getpwnam("nobody"); 550 } 551 if (pw == NULL) 552 GTEST_SKIP() << "Test requires an unprivileged user"; 553 /* Use group "nobody", which is Kyua's default unprivileged group */ 554 gr = getgrnam("nobody"); 555 if (gr == NULL) 556 GTEST_SKIP() << "Test requires an unprivileged group"; 557 *uid = pw->pw_uid; 558 *gid = gr->gr_gid; 559 } 560 561 void 562 FuseTest::fork(bool drop_privs, int *child_status, 563 std::function<void()> parent_func, 564 std::function<int()> child_func) 565 { 566 sem_t *sem; 567 int mprot = PROT_READ | PROT_WRITE; 568 int mflags = MAP_ANON | MAP_SHARED; 569 pid_t child; 570 uid_t uid; 571 gid_t gid; 572 573 if (drop_privs) { 574 get_unprivileged_id(&uid, &gid); 575 if (IsSkipped()) 576 return; 577 } 578 579 sem = (sem_t*)mmap(NULL, sizeof(*sem), mprot, mflags, -1, 0); 580 ASSERT_NE(MAP_FAILED, sem) << strerror(errno); 581 ASSERT_EQ(0, sem_init(sem, 1, 0)) << strerror(errno); 582 583 if ((child = ::fork()) == 0) { 584 /* In child */ 585 int err = 0; 586 587 if (sem_wait(sem)) { 588 perror("sem_wait"); 589 err = 1; 590 goto out; 591 } 592 593 if (drop_privs && 0 != setegid(gid)) { 594 perror("setegid"); 595 err = 1; 596 goto out; 597 } 598 if (drop_privs && 0 != setreuid(-1, uid)) { 599 perror("setreuid"); 600 err = 1; 601 goto out; 602 } 603 err = child_func(); 604 605 out: 606 sem_destroy(sem); 607 _exit(err); 608 } else if (child > 0) { 609 /* 610 * In parent. Cleanup must happen here, because it's still 611 * privileged. 612 */ 613 m_mock->m_child_pid = child; 614 ASSERT_NO_FATAL_FAILURE(parent_func()); 615 616 /* Signal the child process to go */ 617 ASSERT_EQ(0, sem_post(sem)) << strerror(errno); 618 619 ASSERT_LE(0, wait(child_status)) << strerror(errno); 620 } else { 621 FAIL() << strerror(errno); 622 } 623 munmap(sem, sizeof(*sem)); 624 return; 625 } 626 627 static void usage(char* progname) { 628 fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname); 629 exit(2); 630 } 631 632 int main(int argc, char **argv) { 633 int ch; 634 FuseEnv *fuse_env = new FuseEnv; 635 636 InitGoogleTest(&argc, argv); 637 AddGlobalTestEnvironment(fuse_env); 638 639 while ((ch = getopt(argc, argv, "v")) != -1) { 640 switch (ch) { 641 case 'v': 642 verbosity++; 643 break; 644 default: 645 usage(argv[0]); 646 break; 647 } 648 } 649 650 return (RUN_ALL_TESTS()); 651 } 652