1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 The FreeBSD Foundation 5 * 6 * This software was developed by BFF Storage Systems, LLC under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 extern "C" { 32 #include <sys/param.h> 33 #include <sys/mman.h> 34 #include <sys/socket.h> 35 #include <sys/sysctl.h> 36 #include <sys/uio.h> 37 38 #include <aio.h> 39 #include <fcntl.h> 40 #include <semaphore.h> 41 #include <setjmp.h> 42 #include <signal.h> 43 #include <unistd.h> 44 } 45 46 #include "mockfs.hh" 47 #include "utils.hh" 48 49 using namespace testing; 50 51 class Read: public FuseTest { 52 53 public: 54 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 55 { 56 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); 57 } 58 }; 59 60 class RofsRead: public Read { 61 public: 62 virtual void SetUp() { 63 m_ro = true; 64 Read::SetUp(); 65 } 66 }; 67 68 class Read_7_8: public FuseTest { 69 public: 70 virtual void SetUp() { 71 m_kernel_minor_version = 8; 72 FuseTest::SetUp(); 73 } 74 75 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 76 { 77 FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1); 78 } 79 }; 80 81 class AioRead: public Read { 82 public: 83 virtual void SetUp() { 84 if (!is_unsafe_aio_enabled()) 85 GTEST_SKIP() << 86 "vfs.aio.enable_unsafe must be set for this test"; 87 FuseTest::SetUp(); 88 } 89 }; 90 91 class AsyncRead: public AioRead { 92 virtual void SetUp() { 93 m_init_flags = FUSE_ASYNC_READ; 94 AioRead::SetUp(); 95 } 96 }; 97 98 class AsyncReadNoAttrCache: public Read { 99 virtual void SetUp() { 100 m_init_flags = FUSE_ASYNC_READ; 101 Read::SetUp(); 102 } 103 public: 104 void expect_lookup(const char *relpath, uint64_t ino) 105 { 106 // Don't return size, and set attr_valid=0 107 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1, 0); 108 } 109 }; 110 111 class ReadAhead: public Read, 112 public WithParamInterface<tuple<bool, int>> 113 { 114 virtual void SetUp() { 115 int val; 116 const char *node = "vfs.maxbcachebuf"; 117 size_t size = sizeof(val); 118 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) 119 << strerror(errno); 120 121 m_maxreadahead = val * get<1>(GetParam()); 122 m_noclusterr = get<0>(GetParam()); 123 Read::SetUp(); 124 } 125 }; 126 127 class ReadMaxRead: public Read { 128 virtual void SetUp() { 129 m_maxread = 16384; 130 Read::SetUp(); 131 } 132 }; 133 134 class ReadNoatime: public Read { 135 virtual void SetUp() { 136 m_noatime = true; 137 Read::SetUp(); 138 } 139 }; 140 141 class ReadSigbus: public Read 142 { 143 public: 144 static jmp_buf s_jmpbuf; 145 static void *s_si_addr; 146 147 void TearDown() { 148 struct sigaction sa; 149 150 bzero(&sa, sizeof(sa)); 151 sa.sa_handler = SIG_DFL; 152 sigaction(SIGBUS, &sa, NULL); 153 154 FuseTest::TearDown(); 155 } 156 157 }; 158 159 static void 160 handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) { 161 ReadSigbus::s_si_addr = info->si_addr; 162 longjmp(ReadSigbus::s_jmpbuf, 1); 163 } 164 165 jmp_buf ReadSigbus::s_jmpbuf; 166 void *ReadSigbus::s_si_addr; 167 168 class TimeGran: public Read, public WithParamInterface<unsigned> { 169 public: 170 virtual void SetUp() { 171 m_time_gran = 1 << GetParam(); 172 Read::SetUp(); 173 } 174 }; 175 176 /* AIO reads need to set the header's pid field correctly */ 177 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ 178 TEST_F(AioRead, aio_read) 179 { 180 const char FULLPATH[] = "mountpoint/some_file.txt"; 181 const char RELPATH[] = "some_file.txt"; 182 const char *CONTENTS = "abcdefgh"; 183 uint64_t ino = 42; 184 int fd; 185 ssize_t bufsize = strlen(CONTENTS); 186 uint8_t buf[bufsize]; 187 struct aiocb iocb, *piocb; 188 189 expect_lookup(RELPATH, ino, bufsize); 190 expect_open(ino, 0, 1); 191 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 192 193 fd = open(FULLPATH, O_RDONLY); 194 ASSERT_LE(0, fd) << strerror(errno); 195 196 iocb.aio_nbytes = bufsize; 197 iocb.aio_fildes = fd; 198 iocb.aio_buf = buf; 199 iocb.aio_offset = 0; 200 iocb.aio_sigevent.sigev_notify = SIGEV_NONE; 201 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno); 202 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); 203 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 204 205 leak(fd); 206 } 207 208 /* 209 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there 210 * is at most one outstanding read operation per file handle 211 */ 212 TEST_F(AioRead, async_read_disabled) 213 { 214 const char FULLPATH[] = "mountpoint/some_file.txt"; 215 const char RELPATH[] = "some_file.txt"; 216 uint64_t ino = 42; 217 int fd; 218 ssize_t bufsize = 50; 219 char buf0[bufsize], buf1[bufsize]; 220 off_t off0 = 0; 221 off_t off1 = m_maxbcachebuf; 222 struct aiocb iocb0, iocb1; 223 volatile sig_atomic_t read_count = 0; 224 225 expect_lookup(RELPATH, ino, 131072); 226 expect_open(ino, 0, 1); 227 EXPECT_CALL(*m_mock, process( 228 ResultOf([=](auto in) { 229 return (in.header.opcode == FUSE_READ && 230 in.header.nodeid == ino && 231 in.body.read.fh == FH && 232 in.body.read.offset == (uint64_t)off0); 233 }, Eq(true)), 234 _) 235 ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) { 236 read_count++; 237 /* Filesystem is slow to respond */ 238 })); 239 EXPECT_CALL(*m_mock, process( 240 ResultOf([=](auto in) { 241 return (in.header.opcode == FUSE_READ && 242 in.header.nodeid == ino && 243 in.body.read.fh == FH && 244 in.body.read.offset == (uint64_t)off1); 245 }, Eq(true)), 246 _) 247 ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) { 248 read_count++; 249 /* Filesystem is slow to respond */ 250 })); 251 252 fd = open(FULLPATH, O_RDONLY); 253 ASSERT_LE(0, fd) << strerror(errno); 254 255 /* 256 * Submit two AIO read requests, and respond to neither. If the 257 * filesystem ever gets the second read request, then we failed to 258 * limit outstanding reads. 259 */ 260 iocb0.aio_nbytes = bufsize; 261 iocb0.aio_fildes = fd; 262 iocb0.aio_buf = buf0; 263 iocb0.aio_offset = off0; 264 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE; 265 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno); 266 267 iocb1.aio_nbytes = bufsize; 268 iocb1.aio_fildes = fd; 269 iocb1.aio_buf = buf1; 270 iocb1.aio_offset = off1; 271 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE; 272 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno); 273 274 /* 275 * Sleep for awhile to make sure the kernel has had a chance to issue 276 * the second read, even though the first has not yet returned 277 */ 278 nap(); 279 EXPECT_EQ(read_count, 1); 280 281 m_mock->kill_daemon(); 282 /* Wait for AIO activity to complete, but ignore errors */ 283 (void)aio_waitcomplete(NULL, NULL); 284 285 leak(fd); 286 } 287 288 /* 289 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple 290 * simultaneous read requests on the same file handle. 291 */ 292 TEST_F(AsyncRead, async_read) 293 { 294 const char FULLPATH[] = "mountpoint/some_file.txt"; 295 const char RELPATH[] = "some_file.txt"; 296 uint64_t ino = 42; 297 int fd; 298 ssize_t bufsize = 50; 299 char buf0[bufsize], buf1[bufsize]; 300 off_t off0 = 0; 301 off_t off1 = m_maxbcachebuf; 302 off_t fsize = 2 * m_maxbcachebuf; 303 struct aiocb iocb0, iocb1; 304 sem_t sem; 305 306 ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); 307 308 expect_lookup(RELPATH, ino, fsize); 309 expect_open(ino, 0, 1); 310 EXPECT_CALL(*m_mock, process( 311 ResultOf([=](auto in) { 312 return (in.header.opcode == FUSE_READ && 313 in.header.nodeid == ino && 314 in.body.read.fh == FH && 315 in.body.read.offset == (uint64_t)off0); 316 }, Eq(true)), 317 _) 318 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { 319 sem_post(&sem); 320 /* Filesystem is slow to respond */ 321 })); 322 EXPECT_CALL(*m_mock, process( 323 ResultOf([=](auto in) { 324 return (in.header.opcode == FUSE_READ && 325 in.header.nodeid == ino && 326 in.body.read.fh == FH && 327 in.body.read.offset == (uint64_t)off1); 328 }, Eq(true)), 329 _) 330 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { 331 sem_post(&sem); 332 /* Filesystem is slow to respond */ 333 })); 334 335 fd = open(FULLPATH, O_RDONLY); 336 ASSERT_LE(0, fd) << strerror(errno); 337 338 /* 339 * Submit two AIO read requests, but respond to neither. Ensure that 340 * we received both. 341 */ 342 iocb0.aio_nbytes = bufsize; 343 iocb0.aio_fildes = fd; 344 iocb0.aio_buf = buf0; 345 iocb0.aio_offset = off0; 346 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE; 347 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno); 348 349 iocb1.aio_nbytes = bufsize; 350 iocb1.aio_fildes = fd; 351 iocb1.aio_buf = buf1; 352 iocb1.aio_offset = off1; 353 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE; 354 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno); 355 356 /* Wait until both reads have reached the daemon */ 357 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno); 358 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno); 359 360 m_mock->kill_daemon(); 361 /* Wait for AIO activity to complete, but ignore errors */ 362 (void)aio_waitcomplete(NULL, NULL); 363 364 leak(fd); 365 } 366 367 /* 368 * Regression test for a VFS locking bug: as of 369 * 22bb70a6b3bb7799276ab480e40665b7d6e4ce25 (17-December-2024), fusefs did not 370 * obtain an exclusive vnode lock before attempting to clear the attr cache 371 * after an unexpected eof. The vnode lock would already be exclusive except 372 * when FUSE_ASYNC_READ is set. 373 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=283391 374 */ 375 TEST_F(AsyncRead, eof) 376 { 377 const char FULLPATH[] = "mountpoint/some_file.txt"; 378 const char RELPATH[] = "some_file.txt"; 379 const char *CONTENTS = "abcdefghijklmnop"; 380 uint64_t ino = 42; 381 int fd; 382 uint64_t offset = 100; 383 ssize_t bufsize = strlen(CONTENTS); 384 ssize_t partbufsize = 3 * bufsize / 4; 385 ssize_t r; 386 uint8_t buf[bufsize]; 387 struct stat sb; 388 389 expect_lookup(RELPATH, ino, offset + bufsize); 390 expect_open(ino, 0, 1); 391 expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS); 392 expect_getattr(ino, offset + partbufsize); 393 394 fd = open(FULLPATH, O_RDONLY); 395 ASSERT_LE(0, fd) << strerror(errno); 396 397 r = pread(fd, buf, bufsize, offset); 398 ASSERT_LE(0, r) << strerror(errno); 399 EXPECT_EQ(partbufsize, r) << strerror(errno); 400 ASSERT_EQ(0, fstat(fd, &sb)); 401 EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size); 402 leak(fd); 403 } 404 405 /* 406 * If the daemon disables the attribute cache (or if it has expired), then the 407 * kernel must fetch attributes during VOP_READ. If async reads are enabled, 408 * then fuse_internal_cache_attrs will be called without the vnode exclusively 409 * locked. Regression test for 410 * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=291064 411 */ 412 TEST_F(AsyncReadNoAttrCache, read) 413 { 414 const char FULLPATH[] = "mountpoint/some_file.txt"; 415 const char RELPATH[] = "some_file.txt"; 416 const char *CONTENTS = "abcdefgh"; 417 uint64_t ino = 42; 418 mode_t mode = S_IFREG | 0644; 419 int fd; 420 ssize_t bufsize = strlen(CONTENTS); 421 uint8_t buf[bufsize]; 422 423 expect_lookup(RELPATH, ino); 424 expect_open(ino, 0, 1); 425 EXPECT_CALL(*m_mock, process( 426 ResultOf([=](auto in) { 427 return (in.header.opcode == FUSE_GETATTR && 428 in.header.nodeid == ino); 429 }, Eq(true)), 430 _) 431 ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) 432 { 433 SET_OUT_HEADER_LEN(out, attr); 434 out.body.attr.attr.ino = ino; 435 out.body.attr.attr.mode = mode; 436 out.body.attr.attr.size = bufsize; 437 out.body.attr.attr_valid = 0; 438 }))); 439 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 440 441 fd = open(FULLPATH, O_RDONLY); 442 ASSERT_LE(0, fd) << strerror(errno); 443 444 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 445 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 446 447 leak(fd); 448 } 449 450 /* 451 * If the vnode's attr cache has expired before VOP_READ begins, the kernel 452 * will have to fetch the file's size from the server. If it has changed since 453 * our last query, we'll need to update the vnode pager. But we'll only have a 454 * shared vnode lock. 455 */ 456 TEST_F(AsyncReadNoAttrCache, read_sizechange) 457 { 458 const char FULLPATH[] = "mountpoint/some_file.txt"; 459 const char RELPATH[] = "some_file.txt"; 460 const char *CONTENTS = "abcdefgh"; 461 uint64_t ino = 42; 462 mode_t mode = S_IFREG | 0644; 463 int fd; 464 size_t bufsize = strlen(CONTENTS); 465 uint8_t buf[bufsize]; 466 size_t size1 = bufsize - 1; 467 size_t size2 = bufsize; 468 Sequence seq; 469 470 expect_lookup(RELPATH, ino); 471 expect_open(ino, 0, 1); 472 EXPECT_CALL(*m_mock, process( 473 ResultOf([=](auto in) { 474 return (in.header.opcode == FUSE_GETATTR && 475 in.header.nodeid == ino); 476 }, Eq(true)), 477 _) 478 ).Times(2) 479 .InSequence(seq) 480 .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) 481 { 482 SET_OUT_HEADER_LEN(out, attr); 483 out.body.attr.attr.ino = ino; 484 out.body.attr.attr.mode = mode; 485 out.body.attr.attr.size = size1; 486 out.body.attr.attr_valid = 0; 487 }))); 488 EXPECT_CALL(*m_mock, process( 489 ResultOf([=](auto in) { 490 return (in.header.opcode == FUSE_READ && 491 in.header.nodeid == ino && 492 in.body.read.offset == 0 && 493 in.body.read.size == size1); 494 }, Eq(true)), 495 _) 496 ).Times(1) 497 .InSequence(seq) 498 .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 499 out.header.len = sizeof(struct fuse_out_header) + size1; 500 memmove(out.body.bytes, CONTENTS, size1); 501 }))); 502 EXPECT_CALL(*m_mock, process( 503 ResultOf([=](auto in) { 504 return (in.header.opcode == FUSE_GETATTR && 505 in.header.nodeid == ino); 506 }, Eq(true)), 507 _) 508 ).InSequence(seq) 509 .WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) 510 { 511 SET_OUT_HEADER_LEN(out, attr); 512 out.body.attr.attr.ino = ino; 513 out.body.attr.attr.mode = mode; 514 out.body.attr.attr.size = size2; 515 out.body.attr.attr_valid = 0; 516 }))); 517 EXPECT_CALL(*m_mock, process( 518 ResultOf([=](auto in) { 519 return (in.header.opcode == FUSE_READ && 520 in.header.nodeid == ino && 521 in.body.read.offset == 0 && 522 in.body.read.size == size2); 523 }, Eq(true)), 524 _) 525 ).Times(1) 526 .InSequence(seq) 527 .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 528 out.header.len = sizeof(struct fuse_out_header) + size2; 529 memmove(out.body.bytes, CONTENTS, size2); 530 }))); 531 532 fd = open(FULLPATH, O_RDONLY); 533 ASSERT_LE(0, fd) << strerror(errno); 534 535 ASSERT_EQ(static_cast<ssize_t>(size1), read(fd, buf, bufsize)) << strerror(errno); 536 ASSERT_EQ(0, memcmp(buf, CONTENTS, size1)); 537 538 /* Read again, but this time the server has changed the file's size */ 539 bzero(buf, size2); 540 ASSERT_EQ(static_cast<ssize_t>(size2), pread(fd, buf, bufsize, 0)) << strerror(errno); 541 ASSERT_EQ(0, memcmp(buf, CONTENTS, size2)); 542 543 leak(fd); 544 } 545 546 /* The kernel should update the cached atime attribute during a read */ 547 TEST_F(Read, atime) 548 { 549 const char FULLPATH[] = "mountpoint/some_file.txt"; 550 const char RELPATH[] = "some_file.txt"; 551 const char *CONTENTS = "abcdefgh"; 552 struct stat sb1, sb2; 553 uint64_t ino = 42; 554 int fd; 555 ssize_t bufsize = strlen(CONTENTS); 556 uint8_t buf[bufsize]; 557 558 expect_lookup(RELPATH, ino, bufsize); 559 expect_open(ino, 0, 1); 560 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 561 562 fd = open(FULLPATH, O_RDONLY); 563 ASSERT_LE(0, fd) << strerror(errno); 564 ASSERT_EQ(0, fstat(fd, &sb1)); 565 566 /* Ensure atime will be different than it was during lookup */ 567 nap(); 568 569 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 570 ASSERT_EQ(0, fstat(fd, &sb2)); 571 572 /* The kernel should automatically update atime during read */ 573 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <)); 574 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); 575 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); 576 577 leak(fd); 578 } 579 580 /* The kernel should update the cached atime attribute during a cached read */ 581 TEST_F(Read, atime_cached) 582 { 583 const char FULLPATH[] = "mountpoint/some_file.txt"; 584 const char RELPATH[] = "some_file.txt"; 585 const char *CONTENTS = "abcdefgh"; 586 struct stat sb1, sb2; 587 uint64_t ino = 42; 588 int fd; 589 ssize_t bufsize = strlen(CONTENTS); 590 uint8_t buf[bufsize]; 591 592 expect_lookup(RELPATH, ino, bufsize); 593 expect_open(ino, 0, 1); 594 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 595 596 fd = open(FULLPATH, O_RDONLY); 597 ASSERT_LE(0, fd) << strerror(errno); 598 599 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); 600 ASSERT_EQ(0, fstat(fd, &sb1)); 601 602 /* Ensure atime will be different than it was during the first read */ 603 nap(); 604 605 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); 606 ASSERT_EQ(0, fstat(fd, &sb2)); 607 608 /* The kernel should automatically update atime during read */ 609 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <)); 610 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); 611 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); 612 613 leak(fd); 614 } 615 616 /* dirty atime values should be flushed during close */ 617 TEST_F(Read, atime_during_close) 618 { 619 const char FULLPATH[] = "mountpoint/some_file.txt"; 620 const char RELPATH[] = "some_file.txt"; 621 const char *CONTENTS = "abcdefgh"; 622 struct stat sb; 623 uint64_t ino = 42; 624 const mode_t newmode = 0755; 625 int fd; 626 ssize_t bufsize = strlen(CONTENTS); 627 uint8_t buf[bufsize]; 628 629 expect_lookup(RELPATH, ino, bufsize); 630 expect_open(ino, 0, 1); 631 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 632 EXPECT_CALL(*m_mock, process( 633 ResultOf([&](auto in) { 634 uint32_t valid = FATTR_ATIME; 635 return (in.header.opcode == FUSE_SETATTR && 636 in.header.nodeid == ino && 637 in.body.setattr.valid == valid && 638 (time_t)in.body.setattr.atime == 639 sb.st_atim.tv_sec && 640 (long)in.body.setattr.atimensec == 641 sb.st_atim.tv_nsec); 642 }, Eq(true)), 643 _) 644 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 645 SET_OUT_HEADER_LEN(out, attr); 646 out.body.attr.attr.ino = ino; 647 out.body.attr.attr.mode = S_IFREG | newmode; 648 }))); 649 expect_flush(ino, 1, ReturnErrno(0)); 650 expect_release(ino, FuseTest::FH); 651 652 fd = open(FULLPATH, O_RDONLY); 653 ASSERT_LE(0, fd) << strerror(errno); 654 655 /* Ensure atime will be different than during lookup */ 656 nap(); 657 658 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 659 ASSERT_EQ(0, fstat(fd, &sb)); 660 661 close(fd); 662 } 663 664 /* 665 * When not using -o default_permissions, the daemon may make its own decisions 666 * regarding access permissions, and these may be unpredictable. If it rejects 667 * our attempt to set atime, that should not cause close(2) to fail. 668 */ 669 TEST_F(Read, atime_during_close_eacces) 670 { 671 const char FULLPATH[] = "mountpoint/some_file.txt"; 672 const char RELPATH[] = "some_file.txt"; 673 const char *CONTENTS = "abcdefgh"; 674 uint64_t ino = 42; 675 int fd; 676 ssize_t bufsize = strlen(CONTENTS); 677 uint8_t buf[bufsize]; 678 679 expect_lookup(RELPATH, ino, bufsize); 680 expect_open(ino, 0, 1); 681 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 682 EXPECT_CALL(*m_mock, process( 683 ResultOf([&](auto in) { 684 uint32_t valid = FATTR_ATIME; 685 return (in.header.opcode == FUSE_SETATTR && 686 in.header.nodeid == ino && 687 in.body.setattr.valid == valid); 688 }, Eq(true)), 689 _) 690 ).WillOnce(Invoke(ReturnErrno(EACCES))); 691 expect_flush(ino, 1, ReturnErrno(0)); 692 expect_release(ino, FuseTest::FH); 693 694 fd = open(FULLPATH, O_RDONLY); 695 ASSERT_LE(0, fd) << strerror(errno); 696 697 /* Ensure atime will be different than during lookup */ 698 nap(); 699 700 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 701 702 ASSERT_EQ(0, close(fd)); 703 } 704 705 /* A cached atime should be flushed during FUSE_SETATTR */ 706 TEST_F(Read, atime_during_setattr) 707 { 708 const char FULLPATH[] = "mountpoint/some_file.txt"; 709 const char RELPATH[] = "some_file.txt"; 710 const char *CONTENTS = "abcdefgh"; 711 struct stat sb; 712 uint64_t ino = 42; 713 const mode_t newmode = 0755; 714 int fd; 715 ssize_t bufsize = strlen(CONTENTS); 716 uint8_t buf[bufsize]; 717 718 expect_lookup(RELPATH, ino, bufsize); 719 expect_open(ino, 0, 1); 720 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 721 EXPECT_CALL(*m_mock, process( 722 ResultOf([&](auto in) { 723 uint32_t valid = FATTR_MODE | FATTR_ATIME; 724 return (in.header.opcode == FUSE_SETATTR && 725 in.header.nodeid == ino && 726 in.body.setattr.valid == valid && 727 (time_t)in.body.setattr.atime == 728 sb.st_atim.tv_sec && 729 (long)in.body.setattr.atimensec == 730 sb.st_atim.tv_nsec); 731 }, Eq(true)), 732 _) 733 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 734 SET_OUT_HEADER_LEN(out, attr); 735 out.body.attr.attr.ino = ino; 736 out.body.attr.attr.mode = S_IFREG | newmode; 737 }))); 738 739 fd = open(FULLPATH, O_RDONLY); 740 ASSERT_LE(0, fd) << strerror(errno); 741 742 /* Ensure atime will be different than during lookup */ 743 nap(); 744 745 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 746 ASSERT_EQ(0, fstat(fd, &sb)); 747 ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 748 749 leak(fd); 750 } 751 752 /* 0-length reads shouldn't cause any confusion */ 753 TEST_F(Read, direct_io_read_nothing) 754 { 755 const char FULLPATH[] = "mountpoint/some_file.txt"; 756 const char RELPATH[] = "some_file.txt"; 757 uint64_t ino = 42; 758 int fd; 759 uint64_t offset = 100; 760 char buf[80]; 761 762 expect_lookup(RELPATH, ino, offset + 1000); 763 expect_open(ino, FOPEN_DIRECT_IO, 1); 764 765 fd = open(FULLPATH, O_RDONLY); 766 ASSERT_LE(0, fd) << strerror(errno); 767 768 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno); 769 leak(fd); 770 } 771 772 /* 773 * With direct_io, reads should not fill the cache. They should go straight to 774 * the daemon 775 */ 776 TEST_F(Read, direct_io_pread) 777 { 778 const char FULLPATH[] = "mountpoint/some_file.txt"; 779 const char RELPATH[] = "some_file.txt"; 780 const char *CONTENTS = "abcdefgh"; 781 uint64_t ino = 42; 782 int fd; 783 uint64_t offset = 100; 784 ssize_t bufsize = strlen(CONTENTS); 785 uint8_t buf[bufsize]; 786 787 expect_lookup(RELPATH, ino, offset + bufsize); 788 expect_open(ino, FOPEN_DIRECT_IO, 1); 789 expect_read(ino, offset, bufsize, bufsize, CONTENTS); 790 791 fd = open(FULLPATH, O_RDONLY); 792 ASSERT_LE(0, fd) << strerror(errno); 793 794 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); 795 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 796 797 // With FOPEN_DIRECT_IO, the cache should be bypassed. The server will 798 // get a 2nd read request. 799 expect_read(ino, offset, bufsize, bufsize, CONTENTS); 800 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); 801 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 802 leak(fd); 803 } 804 805 /* 806 * With direct_io, filesystems are allowed to return less data than is 807 * requested. fuse(4) should return a short read to userland. 808 */ 809 TEST_F(Read, direct_io_short_read) 810 { 811 const char FULLPATH[] = "mountpoint/some_file.txt"; 812 const char RELPATH[] = "some_file.txt"; 813 const char *CONTENTS = "abcdefghijklmnop"; 814 uint64_t ino = 42; 815 int fd; 816 uint64_t offset = 100; 817 ssize_t bufsize = strlen(CONTENTS); 818 ssize_t halfbufsize = bufsize / 2; 819 uint8_t buf[bufsize]; 820 821 expect_lookup(RELPATH, ino, offset + bufsize); 822 expect_open(ino, FOPEN_DIRECT_IO, 1); 823 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS); 824 825 fd = open(FULLPATH, O_RDONLY); 826 ASSERT_LE(0, fd) << strerror(errno); 827 828 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset)) 829 << strerror(errno); 830 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize)); 831 leak(fd); 832 } 833 834 TEST_F(Read, eio) 835 { 836 const char FULLPATH[] = "mountpoint/some_file.txt"; 837 const char RELPATH[] = "some_file.txt"; 838 const char *CONTENTS = "abcdefgh"; 839 uint64_t ino = 42; 840 int fd; 841 ssize_t bufsize = strlen(CONTENTS); 842 uint8_t buf[bufsize]; 843 844 expect_lookup(RELPATH, ino, bufsize); 845 expect_open(ino, 0, 1); 846 EXPECT_CALL(*m_mock, process( 847 ResultOf([=](auto in) { 848 return (in.header.opcode == FUSE_READ); 849 }, Eq(true)), 850 _) 851 ).WillOnce(Invoke(ReturnErrno(EIO))); 852 853 fd = open(FULLPATH, O_RDONLY); 854 ASSERT_LE(0, fd) << strerror(errno); 855 856 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno); 857 ASSERT_EQ(EIO, errno); 858 leak(fd); 859 } 860 861 /* 862 * If the server returns a short read when direct io is not in use, that 863 * indicates EOF, because of a server-side truncation. We should invalidate 864 * all cached attributes. We may update the file size, 865 */ 866 TEST_F(Read, eof) 867 { 868 const char FULLPATH[] = "mountpoint/some_file.txt"; 869 const char RELPATH[] = "some_file.txt"; 870 const char *CONTENTS = "abcdefghijklmnop"; 871 uint64_t ino = 42; 872 int fd; 873 uint64_t offset = 100; 874 ssize_t bufsize = strlen(CONTENTS); 875 ssize_t partbufsize = 3 * bufsize / 4; 876 ssize_t r; 877 uint8_t buf[bufsize]; 878 struct stat sb; 879 880 expect_lookup(RELPATH, ino, offset + bufsize); 881 expect_open(ino, 0, 1); 882 expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS); 883 expect_getattr(ino, offset + partbufsize); 884 885 fd = open(FULLPATH, O_RDONLY); 886 ASSERT_LE(0, fd) << strerror(errno); 887 888 r = pread(fd, buf, bufsize, offset); 889 ASSERT_LE(0, r) << strerror(errno); 890 EXPECT_EQ(partbufsize, r) << strerror(errno); 891 ASSERT_EQ(0, fstat(fd, &sb)); 892 EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size); 893 leak(fd); 894 } 895 896 /* Like Read.eof, but causes an entire buffer to be invalidated */ 897 TEST_F(Read, eof_of_whole_buffer) 898 { 899 const char FULLPATH[] = "mountpoint/some_file.txt"; 900 const char RELPATH[] = "some_file.txt"; 901 const char *CONTENTS = "abcdefghijklmnop"; 902 uint64_t ino = 42; 903 int fd; 904 ssize_t bufsize = strlen(CONTENTS); 905 off_t old_filesize = m_maxbcachebuf * 2 + bufsize; 906 uint8_t buf[bufsize]; 907 struct stat sb; 908 909 expect_lookup(RELPATH, ino, old_filesize); 910 expect_open(ino, 0, 1); 911 expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS); 912 expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS); 913 expect_getattr(ino, m_maxbcachebuf); 914 915 fd = open(FULLPATH, O_RDONLY); 916 ASSERT_LE(0, fd) << strerror(errno); 917 918 /* Cache the third block */ 919 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, m_maxbcachebuf * 2)) 920 << strerror(errno); 921 /* Try to read the 2nd block, but it's past EOF */ 922 ASSERT_EQ(0, pread(fd, buf, bufsize, m_maxbcachebuf)) 923 << strerror(errno); 924 ASSERT_EQ(0, fstat(fd, &sb)); 925 EXPECT_EQ((off_t)(m_maxbcachebuf), sb.st_size); 926 leak(fd); 927 } 928 929 /* 930 * With the keep_cache option, the kernel may keep its read cache across 931 * multiple open(2)s. 932 */ 933 TEST_F(Read, keep_cache) 934 { 935 const char FULLPATH[] = "mountpoint/some_file.txt"; 936 const char RELPATH[] = "some_file.txt"; 937 const char *CONTENTS = "abcdefgh"; 938 uint64_t ino = 42; 939 int fd0, fd1; 940 ssize_t bufsize = strlen(CONTENTS); 941 uint8_t buf[bufsize]; 942 943 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2); 944 expect_open(ino, FOPEN_KEEP_CACHE, 2); 945 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 946 947 fd0 = open(FULLPATH, O_RDONLY); 948 ASSERT_LE(0, fd0) << strerror(errno); 949 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); 950 951 fd1 = open(FULLPATH, O_RDWR); 952 ASSERT_LE(0, fd1) << strerror(errno); 953 954 /* 955 * This read should be serviced by cache, even though it's on the other 956 * file descriptor 957 */ 958 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno); 959 960 leak(fd0); 961 leak(fd1); 962 } 963 964 /* 965 * Without the keep_cache option, the kernel should drop its read caches on 966 * every open 967 */ 968 TEST_F(Read, keep_cache_disabled) 969 { 970 const char FULLPATH[] = "mountpoint/some_file.txt"; 971 const char RELPATH[] = "some_file.txt"; 972 const char *CONTENTS = "abcdefgh"; 973 uint64_t ino = 42; 974 int fd0, fd1; 975 ssize_t bufsize = strlen(CONTENTS); 976 uint8_t buf[bufsize]; 977 978 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2); 979 expect_open(ino, 0, 2); 980 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 981 982 fd0 = open(FULLPATH, O_RDONLY); 983 ASSERT_LE(0, fd0) << strerror(errno); 984 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); 985 986 fd1 = open(FULLPATH, O_RDWR); 987 ASSERT_LE(0, fd1) << strerror(errno); 988 989 /* 990 * This read should not be serviced by cache, even though it's on the 991 * original file descriptor 992 */ 993 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 994 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno); 995 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); 996 997 leak(fd0); 998 leak(fd1); 999 } 1000 1001 TEST_F(Read, mmap) 1002 { 1003 const char FULLPATH[] = "mountpoint/some_file.txt"; 1004 const char RELPATH[] = "some_file.txt"; 1005 const char *CONTENTS = "abcdefgh"; 1006 uint64_t ino = 42; 1007 int fd; 1008 ssize_t len; 1009 size_t bufsize = strlen(CONTENTS); 1010 void *p; 1011 1012 len = getpagesize(); 1013 1014 expect_lookup(RELPATH, ino, bufsize); 1015 expect_open(ino, 0, 1); 1016 EXPECT_CALL(*m_mock, process( 1017 ResultOf([=](auto in) { 1018 return (in.header.opcode == FUSE_READ && 1019 in.header.nodeid == ino && 1020 in.body.read.fh == Read::FH && 1021 in.body.read.offset == 0 && 1022 in.body.read.size == bufsize); 1023 }, Eq(true)), 1024 _) 1025 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1026 out.header.len = sizeof(struct fuse_out_header) + bufsize; 1027 memmove(out.body.bytes, CONTENTS, bufsize); 1028 }))); 1029 1030 fd = open(FULLPATH, O_RDONLY); 1031 ASSERT_LE(0, fd) << strerror(errno); 1032 1033 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); 1034 ASSERT_NE(MAP_FAILED, p) << strerror(errno); 1035 1036 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize)); 1037 1038 ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 1039 leak(fd); 1040 } 1041 1042 1043 /* When max_read is set, large reads will be split up as necessary */ 1044 TEST_F(ReadMaxRead, split) 1045 { 1046 const char FULLPATH[] = "mountpoint/some_file.txt"; 1047 const char RELPATH[] = "some_file.txt"; 1048 uint64_t ino = 42; 1049 int fd; 1050 ssize_t bufsize = 65536; 1051 ssize_t fragsize = bufsize / 4; 1052 char *rbuf, *frag0, *frag1, *frag2, *frag3; 1053 1054 rbuf = new char[bufsize](); 1055 frag0 = new char[fragsize](); 1056 frag1 = new char[fragsize](); 1057 frag2 = new char[fragsize](); 1058 frag3 = new char[fragsize](); 1059 memset(frag0, '0', fragsize); 1060 memset(frag1, '1', fragsize); 1061 memset(frag2, '2', fragsize); 1062 memset(frag3, '3', fragsize); 1063 1064 expect_lookup(RELPATH, ino, bufsize); 1065 expect_open(ino, 0, 1); 1066 expect_read(ino, 0, fragsize, fragsize, frag0); 1067 expect_read(ino, fragsize, fragsize, fragsize, frag1); 1068 expect_read(ino, 2 * fragsize, fragsize, fragsize, frag2); 1069 expect_read(ino, 3 * fragsize, fragsize, fragsize, frag3); 1070 1071 fd = open(FULLPATH, O_RDONLY); 1072 ASSERT_LE(0, fd) << strerror(errno); 1073 1074 ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno); 1075 ASSERT_EQ(0, memcmp(rbuf, frag0, fragsize)); 1076 ASSERT_EQ(0, memcmp(rbuf + fragsize, frag1, fragsize)); 1077 ASSERT_EQ(0, memcmp(rbuf + 2 * fragsize, frag2, fragsize)); 1078 ASSERT_EQ(0, memcmp(rbuf + 3 * fragsize, frag3, fragsize)); 1079 1080 delete[] frag3; 1081 delete[] frag2; 1082 delete[] frag1; 1083 delete[] frag0; 1084 delete[] rbuf; 1085 leak(fd); 1086 } 1087 1088 /* 1089 * The kernel should not update the cached atime attribute during a read, if 1090 * MNT_NOATIME is used. 1091 */ 1092 TEST_F(ReadNoatime, atime) 1093 { 1094 const char FULLPATH[] = "mountpoint/some_file.txt"; 1095 const char RELPATH[] = "some_file.txt"; 1096 const char *CONTENTS = "abcdefgh"; 1097 struct stat sb1, sb2; 1098 uint64_t ino = 42; 1099 int fd; 1100 ssize_t bufsize = strlen(CONTENTS); 1101 uint8_t buf[bufsize]; 1102 1103 expect_lookup(RELPATH, ino, bufsize); 1104 expect_open(ino, 0, 1); 1105 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1106 1107 fd = open(FULLPATH, O_RDONLY); 1108 ASSERT_LE(0, fd) << strerror(errno); 1109 ASSERT_EQ(0, fstat(fd, &sb1)); 1110 1111 nap(); 1112 1113 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1114 ASSERT_EQ(0, fstat(fd, &sb2)); 1115 1116 /* The kernel should not update atime during read */ 1117 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==)); 1118 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); 1119 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); 1120 1121 leak(fd); 1122 } 1123 1124 /* 1125 * The kernel should not update the cached atime attribute during a cached 1126 * read, if MNT_NOATIME is used. 1127 */ 1128 TEST_F(ReadNoatime, atime_cached) 1129 { 1130 const char FULLPATH[] = "mountpoint/some_file.txt"; 1131 const char RELPATH[] = "some_file.txt"; 1132 const char *CONTENTS = "abcdefgh"; 1133 struct stat sb1, sb2; 1134 uint64_t ino = 42; 1135 int fd; 1136 ssize_t bufsize = strlen(CONTENTS); 1137 uint8_t buf[bufsize]; 1138 1139 expect_lookup(RELPATH, ino, bufsize); 1140 expect_open(ino, 0, 1); 1141 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1142 1143 fd = open(FULLPATH, O_RDONLY); 1144 ASSERT_LE(0, fd) << strerror(errno); 1145 1146 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); 1147 ASSERT_EQ(0, fstat(fd, &sb1)); 1148 1149 nap(); 1150 1151 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); 1152 ASSERT_EQ(0, fstat(fd, &sb2)); 1153 1154 /* The kernel should automatically update atime during read */ 1155 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==)); 1156 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); 1157 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); 1158 1159 leak(fd); 1160 } 1161 1162 /* Read of an mmap()ed file fails */ 1163 TEST_F(ReadSigbus, mmap_eio) 1164 { 1165 const char FULLPATH[] = "mountpoint/some_file.txt"; 1166 const char RELPATH[] = "some_file.txt"; 1167 const char *CONTENTS = "abcdefgh"; 1168 struct sigaction sa; 1169 uint64_t ino = 42; 1170 int fd; 1171 ssize_t len; 1172 size_t bufsize = strlen(CONTENTS); 1173 void *p; 1174 1175 len = getpagesize(); 1176 1177 expect_lookup(RELPATH, ino, bufsize); 1178 expect_open(ino, 0, 1); 1179 EXPECT_CALL(*m_mock, process( 1180 ResultOf([=](auto in) { 1181 return (in.header.opcode == FUSE_READ && 1182 in.header.nodeid == ino && 1183 in.body.read.fh == Read::FH); 1184 }, Eq(true)), 1185 _) 1186 ).WillRepeatedly(Invoke(ReturnErrno(EIO))); 1187 1188 fd = open(FULLPATH, O_RDONLY); 1189 ASSERT_LE(0, fd) << strerror(errno); 1190 1191 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); 1192 ASSERT_NE(MAP_FAILED, p) << strerror(errno); 1193 1194 /* Accessing the mapped page should return SIGBUS. */ 1195 1196 bzero(&sa, sizeof(sa)); 1197 sa.sa_handler = SIG_DFL; 1198 sa.sa_sigaction = handle_sigbus; 1199 sa.sa_flags = SA_RESETHAND | SA_SIGINFO; 1200 ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno); 1201 if (setjmp(ReadSigbus::s_jmpbuf) == 0) { 1202 atomic_signal_fence(std::memory_order::memory_order_seq_cst); 1203 volatile char x __unused = *(volatile char*)p; 1204 FAIL() << "shouldn't get here"; 1205 } 1206 1207 ASSERT_EQ(p, ReadSigbus::s_si_addr); 1208 ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 1209 leak(fd); 1210 } 1211 1212 /* 1213 * A read via mmap comes up short, indicating that the file was truncated 1214 * server-side. 1215 */ 1216 TEST_F(Read, mmap_eof) 1217 { 1218 const char FULLPATH[] = "mountpoint/some_file.txt"; 1219 const char RELPATH[] = "some_file.txt"; 1220 const char *CONTENTS = "abcdefgh"; 1221 uint64_t ino = 42; 1222 int fd; 1223 ssize_t len; 1224 size_t bufsize = strlen(CONTENTS); 1225 struct stat sb; 1226 void *p; 1227 1228 len = getpagesize(); 1229 1230 expect_lookup(RELPATH, ino, m_maxbcachebuf); 1231 expect_open(ino, 0, 1); 1232 EXPECT_CALL(*m_mock, process( 1233 ResultOf([=](auto in) { 1234 return (in.header.opcode == FUSE_READ && 1235 in.header.nodeid == ino && 1236 in.body.read.fh == Read::FH && 1237 in.body.read.offset == 0 && 1238 in.body.read.size == (uint32_t)m_maxbcachebuf); 1239 }, Eq(true)), 1240 _) 1241 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1242 out.header.len = sizeof(struct fuse_out_header) + bufsize; 1243 memmove(out.body.bytes, CONTENTS, bufsize); 1244 }))); 1245 expect_getattr(ino, bufsize); 1246 1247 fd = open(FULLPATH, O_RDONLY); 1248 ASSERT_LE(0, fd) << strerror(errno); 1249 1250 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); 1251 ASSERT_NE(MAP_FAILED, p) << strerror(errno); 1252 1253 /* The file size should be automatically truncated */ 1254 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize)); 1255 ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1256 EXPECT_EQ((off_t)bufsize, sb.st_size); 1257 1258 ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 1259 leak(fd); 1260 } 1261 1262 /* 1263 * During VOP_GETPAGES, the FUSE server fails a FUSE_GETATTR operation. This 1264 * almost certainly indicates a buggy FUSE server, and our goal should be not 1265 * to panic. Instead, generate SIGBUS. 1266 */ 1267 TEST_F(ReadSigbus, mmap_getblksz_fail) 1268 { 1269 const char FULLPATH[] = "mountpoint/some_file.txt"; 1270 const char RELPATH[] = "some_file.txt"; 1271 const char *CONTENTS = "abcdefgh"; 1272 struct sigaction sa; 1273 Sequence seq; 1274 uint64_t ino = 42; 1275 int fd; 1276 ssize_t len; 1277 size_t bufsize = strlen(CONTENTS); 1278 mode_t mode = S_IFREG | 0644; 1279 void *p; 1280 1281 len = getpagesize(); 1282 1283 FuseTest::expect_lookup(RELPATH, ino, mode, bufsize, 1, 0); 1284 /* Expect two GETATTR calls that succeed, followed by one that fail. */ 1285 EXPECT_CALL(*m_mock, process( 1286 ResultOf([=](auto in) { 1287 return (in.header.opcode == FUSE_GETATTR && 1288 in.header.nodeid == ino); 1289 }, Eq(true)), 1290 _) 1291 ).Times(2) 1292 .InSequence(seq) 1293 .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 1294 SET_OUT_HEADER_LEN(out, attr); 1295 out.body.attr.attr.ino = ino; 1296 out.body.attr.attr.mode = mode; 1297 out.body.attr.attr.size = bufsize; 1298 out.body.attr.attr_valid = 0; 1299 }))); 1300 EXPECT_CALL(*m_mock, process( 1301 ResultOf([=](auto in) { 1302 return (in.header.opcode == FUSE_GETATTR && 1303 in.header.nodeid == ino); 1304 }, Eq(true)), 1305 _) 1306 ).InSequence(seq) 1307 .WillRepeatedly(Invoke(ReturnErrno(EIO))); 1308 expect_open(ino, 0, 1); 1309 EXPECT_CALL(*m_mock, process( 1310 ResultOf([=](auto in) { 1311 return (in.header.opcode == FUSE_READ); 1312 }, Eq(true)), 1313 _) 1314 ).Times(0); 1315 1316 fd = open(FULLPATH, O_RDONLY); 1317 ASSERT_LE(0, fd) << strerror(errno); 1318 1319 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); 1320 ASSERT_NE(MAP_FAILED, p) << strerror(errno); 1321 1322 /* Accessing the mapped page should return SIGBUS. */ 1323 bzero(&sa, sizeof(sa)); 1324 sa.sa_handler = SIG_DFL; 1325 sa.sa_sigaction = handle_sigbus; 1326 sa.sa_flags = SA_RESETHAND | SA_SIGINFO; 1327 ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno); 1328 if (setjmp(ReadSigbus::s_jmpbuf) == 0) { 1329 atomic_signal_fence(std::memory_order::memory_order_seq_cst); 1330 volatile char x __unused = *(volatile char*)p; 1331 FAIL() << "shouldn't get here"; 1332 } 1333 1334 ASSERT_EQ(p, ReadSigbus::s_si_addr); 1335 ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 1336 leak(fd); 1337 } 1338 1339 /* 1340 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass 1341 * cache and to straight to the daemon 1342 */ 1343 TEST_F(Read, o_direct) 1344 { 1345 const char FULLPATH[] = "mountpoint/some_file.txt"; 1346 const char RELPATH[] = "some_file.txt"; 1347 const char *CONTENTS = "abcdefgh"; 1348 uint64_t ino = 42; 1349 int fd; 1350 ssize_t bufsize = strlen(CONTENTS); 1351 uint8_t buf[bufsize]; 1352 1353 expect_lookup(RELPATH, ino, bufsize); 1354 expect_open(ino, 0, 1); 1355 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1356 1357 fd = open(FULLPATH, O_RDONLY); 1358 ASSERT_LE(0, fd) << strerror(errno); 1359 1360 // Fill the cache 1361 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1362 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1363 1364 // Reads with o_direct should bypass the cache 1365 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1366 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 1367 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 1368 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1369 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1370 1371 leak(fd); 1372 } 1373 1374 TEST_F(Read, pread) 1375 { 1376 const char FULLPATH[] = "mountpoint/some_file.txt"; 1377 const char RELPATH[] = "some_file.txt"; 1378 const char *CONTENTS = "abcdefgh"; 1379 uint64_t ino = 42; 1380 int fd; 1381 /* 1382 * Set offset to a maxbcachebuf boundary so we'll be sure what offset 1383 * to read from. Without this, the read might start at a lower offset. 1384 */ 1385 uint64_t offset = m_maxbcachebuf; 1386 ssize_t bufsize = strlen(CONTENTS); 1387 uint8_t buf[bufsize]; 1388 1389 expect_lookup(RELPATH, ino, offset + bufsize); 1390 expect_open(ino, 0, 1); 1391 expect_read(ino, offset, bufsize, bufsize, CONTENTS); 1392 1393 fd = open(FULLPATH, O_RDONLY); 1394 ASSERT_LE(0, fd) << strerror(errno); 1395 1396 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); 1397 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1398 leak(fd); 1399 } 1400 1401 TEST_F(Read, read) 1402 { 1403 const char FULLPATH[] = "mountpoint/some_file.txt"; 1404 const char RELPATH[] = "some_file.txt"; 1405 const char *CONTENTS = "abcdefgh"; 1406 uint64_t ino = 42; 1407 int fd; 1408 ssize_t bufsize = strlen(CONTENTS); 1409 uint8_t buf[bufsize]; 1410 1411 expect_lookup(RELPATH, ino, bufsize); 1412 expect_open(ino, 0, 1); 1413 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1414 1415 fd = open(FULLPATH, O_RDONLY); 1416 ASSERT_LE(0, fd) << strerror(errno); 1417 1418 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1419 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1420 1421 leak(fd); 1422 } 1423 1424 TEST_F(Read_7_8, read) 1425 { 1426 const char FULLPATH[] = "mountpoint/some_file.txt"; 1427 const char RELPATH[] = "some_file.txt"; 1428 const char *CONTENTS = "abcdefgh"; 1429 uint64_t ino = 42; 1430 int fd; 1431 ssize_t bufsize = strlen(CONTENTS); 1432 uint8_t buf[bufsize]; 1433 1434 expect_lookup(RELPATH, ino, bufsize); 1435 expect_open(ino, 0, 1); 1436 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1437 1438 fd = open(FULLPATH, O_RDONLY); 1439 ASSERT_LE(0, fd) << strerror(errno); 1440 1441 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1442 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1443 1444 leak(fd); 1445 } 1446 1447 /* 1448 * If cacheing is enabled, the kernel should try to read an entire cache block 1449 * at a time. 1450 */ 1451 TEST_F(Read, cache_block) 1452 { 1453 const char FULLPATH[] = "mountpoint/some_file.txt"; 1454 const char RELPATH[] = "some_file.txt"; 1455 const char *CONTENTS0 = "abcdefghijklmnop"; 1456 uint64_t ino = 42; 1457 int fd; 1458 ssize_t bufsize = 8; 1459 ssize_t filesize = m_maxbcachebuf * 2; 1460 char *contents; 1461 char buf[bufsize]; 1462 const char *contents1 = CONTENTS0 + bufsize; 1463 1464 contents = new char[filesize](); 1465 memmove(contents, CONTENTS0, strlen(CONTENTS0)); 1466 1467 expect_lookup(RELPATH, ino, filesize); 1468 expect_open(ino, 0, 1); 1469 expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, 1470 contents); 1471 1472 fd = open(FULLPATH, O_RDONLY); 1473 ASSERT_LE(0, fd) << strerror(errno); 1474 1475 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1476 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize)); 1477 1478 /* A subsequent read should be serviced by cache */ 1479 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1480 ASSERT_EQ(0, memcmp(buf, contents1, bufsize)); 1481 leak(fd); 1482 delete[] contents; 1483 } 1484 1485 /* Reading with sendfile should work (though it obviously won't be 0-copy) */ 1486 TEST_F(Read, sendfile) 1487 { 1488 const char FULLPATH[] = "mountpoint/some_file.txt"; 1489 const char RELPATH[] = "some_file.txt"; 1490 const char *CONTENTS = "abcdefgh"; 1491 uint64_t ino = 42; 1492 int fd; 1493 size_t bufsize = strlen(CONTENTS); 1494 uint8_t buf[bufsize]; 1495 int sp[2]; 1496 off_t sbytes; 1497 1498 expect_lookup(RELPATH, ino, bufsize); 1499 expect_open(ino, 0, 1); 1500 EXPECT_CALL(*m_mock, process( 1501 ResultOf([=](auto in) { 1502 return (in.header.opcode == FUSE_READ && 1503 in.header.nodeid == ino && 1504 in.body.read.fh == Read::FH && 1505 in.body.read.offset == 0 && 1506 in.body.read.size == bufsize); 1507 }, Eq(true)), 1508 _) 1509 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1510 out.header.len = sizeof(struct fuse_out_header) + bufsize; 1511 memmove(out.body.bytes, CONTENTS, bufsize); 1512 }))); 1513 1514 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp)) 1515 << strerror(errno); 1516 fd = open(FULLPATH, O_RDONLY); 1517 ASSERT_LE(0, fd) << strerror(errno); 1518 1519 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0)) 1520 << strerror(errno); 1521 ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize)) 1522 << strerror(errno); 1523 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1524 1525 close(sp[1]); 1526 close(sp[0]); 1527 leak(fd); 1528 } 1529 1530 /* sendfile should fail gracefully if fuse declines the read */ 1531 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */ 1532 TEST_F(Read, sendfile_eio) 1533 { 1534 const char FULLPATH[] = "mountpoint/some_file.txt"; 1535 const char RELPATH[] = "some_file.txt"; 1536 const char *CONTENTS = "abcdefgh"; 1537 uint64_t ino = 42; 1538 int fd; 1539 ssize_t bufsize = strlen(CONTENTS); 1540 int sp[2]; 1541 off_t sbytes; 1542 1543 expect_lookup(RELPATH, ino, bufsize); 1544 expect_open(ino, 0, 1); 1545 EXPECT_CALL(*m_mock, process( 1546 ResultOf([=](auto in) { 1547 return (in.header.opcode == FUSE_READ); 1548 }, Eq(true)), 1549 _) 1550 ).WillOnce(Invoke(ReturnErrno(EIO))); 1551 1552 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp)) 1553 << strerror(errno); 1554 fd = open(FULLPATH, O_RDONLY); 1555 ASSERT_LE(0, fd) << strerror(errno); 1556 1557 ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0)); 1558 1559 close(sp[1]); 1560 close(sp[0]); 1561 leak(fd); 1562 } 1563 1564 /* 1565 * Sequential reads should use readahead. And if allowed, large reads should 1566 * be clustered. 1567 */ 1568 TEST_P(ReadAhead, readahead) { 1569 const char FULLPATH[] = "mountpoint/some_file.txt"; 1570 const char RELPATH[] = "some_file.txt"; 1571 uint64_t ino = 42; 1572 int fd, maxcontig, clustersize; 1573 ssize_t bufsize = 4 * m_maxbcachebuf; 1574 ssize_t filesize = bufsize; 1575 uint64_t len; 1576 char *rbuf, *contents; 1577 off_t offs; 1578 1579 contents = new char[filesize]; 1580 memset(contents, 'X', filesize); 1581 rbuf = new char[bufsize](); 1582 1583 expect_lookup(RELPATH, ino, filesize); 1584 expect_open(ino, 0, 1); 1585 maxcontig = m_noclusterr ? m_maxbcachebuf : 1586 m_maxbcachebuf + m_maxreadahead; 1587 clustersize = MIN((unsigned long )maxcontig, m_maxphys); 1588 for (offs = 0; offs < bufsize; offs += clustersize) { 1589 len = std::min((size_t)clustersize, (size_t)(filesize - offs)); 1590 expect_read(ino, offs, len, len, contents + offs); 1591 } 1592 1593 fd = open(FULLPATH, O_RDONLY); 1594 ASSERT_LE(0, fd) << strerror(errno); 1595 1596 /* Set the internal readahead counter to a "large" value */ 1597 ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno); 1598 1599 ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno); 1600 ASSERT_EQ(0, memcmp(rbuf, contents, bufsize)); 1601 1602 leak(fd); 1603 delete[] rbuf; 1604 delete[] contents; 1605 } 1606 1607 INSTANTIATE_TEST_SUITE_P(RA, ReadAhead, 1608 Values(tuple<bool, int>(false, 0), 1609 tuple<bool, int>(false, 1), 1610 tuple<bool, int>(false, 2), 1611 tuple<bool, int>(false, 3), 1612 tuple<bool, int>(true, 0), 1613 tuple<bool, int>(true, 1), 1614 tuple<bool, int>(true, 2))); 1615 1616 /* With read-only mounts, fuse should never update atime during close */ 1617 TEST_F(RofsRead, atime_during_close) 1618 { 1619 const char FULLPATH[] = "mountpoint/some_file.txt"; 1620 const char RELPATH[] = "some_file.txt"; 1621 const char *CONTENTS = "abcdefgh"; 1622 uint64_t ino = 42; 1623 int fd; 1624 ssize_t bufsize = strlen(CONTENTS); 1625 uint8_t buf[bufsize]; 1626 1627 expect_lookup(RELPATH, ino, bufsize); 1628 expect_open(ino, 0, 1); 1629 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1630 EXPECT_CALL(*m_mock, process( 1631 ResultOf([&](auto in) { 1632 return (in.header.opcode == FUSE_SETATTR); 1633 }, Eq(true)), 1634 _) 1635 ).Times(0); 1636 expect_flush(ino, 1, ReturnErrno(0)); 1637 expect_release(ino, FuseTest::FH); 1638 1639 fd = open(FULLPATH, O_RDONLY); 1640 ASSERT_LE(0, fd) << strerror(errno); 1641 1642 /* Ensure atime will be different than during lookup */ 1643 nap(); 1644 1645 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1646 1647 close(fd); 1648 } 1649 1650 /* fuse_init_out.time_gran controls the granularity of timestamps */ 1651 TEST_P(TimeGran, atime_during_setattr) 1652 { 1653 const char FULLPATH[] = "mountpoint/some_file.txt"; 1654 const char RELPATH[] = "some_file.txt"; 1655 const char *CONTENTS = "abcdefgh"; 1656 ssize_t bufsize = strlen(CONTENTS); 1657 uint8_t buf[bufsize]; 1658 uint64_t ino = 42; 1659 const mode_t newmode = 0755; 1660 int fd; 1661 1662 expect_lookup(RELPATH, ino, bufsize); 1663 expect_open(ino, 0, 1); 1664 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1665 EXPECT_CALL(*m_mock, process( 1666 ResultOf([=](auto in) { 1667 uint32_t valid = FATTR_MODE | FATTR_ATIME; 1668 return (in.header.opcode == FUSE_SETATTR && 1669 in.header.nodeid == ino && 1670 in.body.setattr.valid == valid && 1671 in.body.setattr.atimensec % m_time_gran == 0); 1672 }, Eq(true)), 1673 _) 1674 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1675 SET_OUT_HEADER_LEN(out, attr); 1676 out.body.attr.attr.ino = ino; 1677 out.body.attr.attr.mode = S_IFREG | newmode; 1678 }))); 1679 1680 fd = open(FULLPATH, O_RDWR); 1681 ASSERT_LE(0, fd) << strerror(errno); 1682 1683 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1684 ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 1685 1686 leak(fd); 1687 } 1688 1689 INSTANTIATE_TEST_SUITE_P(TG, TimeGran, Range(0u, 10u)); 1690