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 Read_7_8: public FuseTest { 61 public: 62 virtual void SetUp() { 63 m_kernel_minor_version = 8; 64 FuseTest::SetUp(); 65 } 66 67 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 68 { 69 FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1); 70 } 71 }; 72 73 class AioRead: public Read { 74 public: 75 virtual void SetUp() { 76 if (!is_unsafe_aio_enabled()) 77 GTEST_SKIP() << 78 "vfs.aio.enable_unsafe must be set for this test"; 79 FuseTest::SetUp(); 80 } 81 }; 82 83 class AsyncRead: public AioRead { 84 virtual void SetUp() { 85 m_init_flags = FUSE_ASYNC_READ; 86 AioRead::SetUp(); 87 } 88 }; 89 90 class ReadAhead: public Read, 91 public WithParamInterface<tuple<bool, int>> 92 { 93 virtual void SetUp() { 94 int val; 95 const char *node = "vfs.maxbcachebuf"; 96 size_t size = sizeof(val); 97 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) 98 << strerror(errno); 99 100 m_maxreadahead = val * get<1>(GetParam()); 101 m_noclusterr = get<0>(GetParam()); 102 Read::SetUp(); 103 } 104 }; 105 106 class ReadNoatime: public Read { 107 virtual void SetUp() { 108 m_noatime = true; 109 Read::SetUp(); 110 } 111 }; 112 113 class ReadSigbus: public Read 114 { 115 public: 116 static jmp_buf s_jmpbuf; 117 static void *s_si_addr; 118 119 void TearDown() { 120 struct sigaction sa; 121 122 bzero(&sa, sizeof(sa)); 123 sa.sa_handler = SIG_DFL; 124 sigaction(SIGBUS, &sa, NULL); 125 126 FuseTest::TearDown(); 127 } 128 129 }; 130 131 static void 132 handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) { 133 ReadSigbus::s_si_addr = info->si_addr; 134 longjmp(ReadSigbus::s_jmpbuf, 1); 135 } 136 137 jmp_buf ReadSigbus::s_jmpbuf; 138 void *ReadSigbus::s_si_addr; 139 140 class TimeGran: public Read, public WithParamInterface<unsigned> { 141 public: 142 virtual void SetUp() { 143 m_time_gran = 1 << GetParam(); 144 Read::SetUp(); 145 } 146 }; 147 148 /* AIO reads need to set the header's pid field correctly */ 149 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ 150 TEST_F(AioRead, aio_read) 151 { 152 const char FULLPATH[] = "mountpoint/some_file.txt"; 153 const char RELPATH[] = "some_file.txt"; 154 const char *CONTENTS = "abcdefgh"; 155 uint64_t ino = 42; 156 int fd; 157 ssize_t bufsize = strlen(CONTENTS); 158 uint8_t buf[bufsize]; 159 struct aiocb iocb, *piocb; 160 161 expect_lookup(RELPATH, ino, bufsize); 162 expect_open(ino, 0, 1); 163 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 164 165 fd = open(FULLPATH, O_RDONLY); 166 ASSERT_LE(0, fd) << strerror(errno); 167 168 iocb.aio_nbytes = bufsize; 169 iocb.aio_fildes = fd; 170 iocb.aio_buf = buf; 171 iocb.aio_offset = 0; 172 iocb.aio_sigevent.sigev_notify = SIGEV_NONE; 173 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno); 174 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); 175 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 176 177 leak(fd); 178 } 179 180 /* 181 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there 182 * is at most one outstanding read operation per file handle 183 */ 184 TEST_F(AioRead, async_read_disabled) 185 { 186 const char FULLPATH[] = "mountpoint/some_file.txt"; 187 const char RELPATH[] = "some_file.txt"; 188 uint64_t ino = 42; 189 int fd; 190 ssize_t bufsize = 50; 191 char buf0[bufsize], buf1[bufsize]; 192 off_t off0 = 0; 193 off_t off1 = m_maxbcachebuf; 194 struct aiocb iocb0, iocb1; 195 volatile sig_atomic_t read_count = 0; 196 197 expect_lookup(RELPATH, ino, 131072); 198 expect_open(ino, 0, 1); 199 EXPECT_CALL(*m_mock, process( 200 ResultOf([=](auto in) { 201 return (in.header.opcode == FUSE_READ && 202 in.header.nodeid == ino && 203 in.body.read.fh == FH && 204 in.body.read.offset == (uint64_t)off0); 205 }, Eq(true)), 206 _) 207 ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) { 208 read_count++; 209 /* Filesystem is slow to respond */ 210 })); 211 EXPECT_CALL(*m_mock, process( 212 ResultOf([=](auto in) { 213 return (in.header.opcode == FUSE_READ && 214 in.header.nodeid == ino && 215 in.body.read.fh == FH && 216 in.body.read.offset == (uint64_t)off1); 217 }, Eq(true)), 218 _) 219 ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) { 220 read_count++; 221 /* Filesystem is slow to respond */ 222 })); 223 224 fd = open(FULLPATH, O_RDONLY); 225 ASSERT_LE(0, fd) << strerror(errno); 226 227 /* 228 * Submit two AIO read requests, and respond to neither. If the 229 * filesystem ever gets the second read request, then we failed to 230 * limit outstanding reads. 231 */ 232 iocb0.aio_nbytes = bufsize; 233 iocb0.aio_fildes = fd; 234 iocb0.aio_buf = buf0; 235 iocb0.aio_offset = off0; 236 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE; 237 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno); 238 239 iocb1.aio_nbytes = bufsize; 240 iocb1.aio_fildes = fd; 241 iocb1.aio_buf = buf1; 242 iocb1.aio_offset = off1; 243 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE; 244 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno); 245 246 /* 247 * Sleep for awhile to make sure the kernel has had a chance to issue 248 * the second read, even though the first has not yet returned 249 */ 250 nap(); 251 EXPECT_EQ(read_count, 1); 252 253 m_mock->kill_daemon(); 254 /* Wait for AIO activity to complete, but ignore errors */ 255 (void)aio_waitcomplete(NULL, NULL); 256 257 leak(fd); 258 } 259 260 /* 261 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple 262 * simultaneous read requests on the same file handle. 263 */ 264 TEST_F(AsyncRead, async_read) 265 { 266 const char FULLPATH[] = "mountpoint/some_file.txt"; 267 const char RELPATH[] = "some_file.txt"; 268 uint64_t ino = 42; 269 int fd; 270 ssize_t bufsize = 50; 271 char buf0[bufsize], buf1[bufsize]; 272 off_t off0 = 0; 273 off_t off1 = m_maxbcachebuf; 274 off_t fsize = 2 * m_maxbcachebuf; 275 struct aiocb iocb0, iocb1; 276 sem_t sem; 277 278 ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); 279 280 expect_lookup(RELPATH, ino, fsize); 281 expect_open(ino, 0, 1); 282 EXPECT_CALL(*m_mock, process( 283 ResultOf([=](auto in) { 284 return (in.header.opcode == FUSE_READ && 285 in.header.nodeid == ino && 286 in.body.read.fh == FH && 287 in.body.read.offset == (uint64_t)off0); 288 }, Eq(true)), 289 _) 290 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { 291 sem_post(&sem); 292 /* Filesystem is slow to respond */ 293 })); 294 EXPECT_CALL(*m_mock, process( 295 ResultOf([=](auto in) { 296 return (in.header.opcode == FUSE_READ && 297 in.header.nodeid == ino && 298 in.body.read.fh == FH && 299 in.body.read.offset == (uint64_t)off1); 300 }, Eq(true)), 301 _) 302 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) { 303 sem_post(&sem); 304 /* Filesystem is slow to respond */ 305 })); 306 307 fd = open(FULLPATH, O_RDONLY); 308 ASSERT_LE(0, fd) << strerror(errno); 309 310 /* 311 * Submit two AIO read requests, but respond to neither. Ensure that 312 * we received both. 313 */ 314 iocb0.aio_nbytes = bufsize; 315 iocb0.aio_fildes = fd; 316 iocb0.aio_buf = buf0; 317 iocb0.aio_offset = off0; 318 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE; 319 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno); 320 321 iocb1.aio_nbytes = bufsize; 322 iocb1.aio_fildes = fd; 323 iocb1.aio_buf = buf1; 324 iocb1.aio_offset = off1; 325 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE; 326 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno); 327 328 /* Wait until both reads have reached the daemon */ 329 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno); 330 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno); 331 332 m_mock->kill_daemon(); 333 /* Wait for AIO activity to complete, but ignore errors */ 334 (void)aio_waitcomplete(NULL, NULL); 335 336 leak(fd); 337 } 338 339 /* The kernel should update the cached atime attribute during a read */ 340 TEST_F(Read, atime) 341 { 342 const char FULLPATH[] = "mountpoint/some_file.txt"; 343 const char RELPATH[] = "some_file.txt"; 344 const char *CONTENTS = "abcdefgh"; 345 struct stat sb1, sb2; 346 uint64_t ino = 42; 347 int fd; 348 ssize_t bufsize = strlen(CONTENTS); 349 uint8_t buf[bufsize]; 350 351 expect_lookup(RELPATH, ino, bufsize); 352 expect_open(ino, 0, 1); 353 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 354 355 fd = open(FULLPATH, O_RDONLY); 356 ASSERT_LE(0, fd) << strerror(errno); 357 ASSERT_EQ(0, fstat(fd, &sb1)); 358 359 /* Ensure atime will be different than it was during lookup */ 360 nap(); 361 362 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 363 ASSERT_EQ(0, fstat(fd, &sb2)); 364 365 /* The kernel should automatically update atime during read */ 366 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <)); 367 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); 368 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); 369 370 leak(fd); 371 } 372 373 /* The kernel should update the cached atime attribute during a cached read */ 374 TEST_F(Read, atime_cached) 375 { 376 const char FULLPATH[] = "mountpoint/some_file.txt"; 377 const char RELPATH[] = "some_file.txt"; 378 const char *CONTENTS = "abcdefgh"; 379 struct stat sb1, sb2; 380 uint64_t ino = 42; 381 int fd; 382 ssize_t bufsize = strlen(CONTENTS); 383 uint8_t buf[bufsize]; 384 385 expect_lookup(RELPATH, ino, bufsize); 386 expect_open(ino, 0, 1); 387 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 388 389 fd = open(FULLPATH, O_RDONLY); 390 ASSERT_LE(0, fd) << strerror(errno); 391 392 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); 393 ASSERT_EQ(0, fstat(fd, &sb1)); 394 395 /* Ensure atime will be different than it was during the first read */ 396 nap(); 397 398 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); 399 ASSERT_EQ(0, fstat(fd, &sb2)); 400 401 /* The kernel should automatically update atime during read */ 402 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <)); 403 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); 404 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); 405 406 leak(fd); 407 } 408 409 /* dirty atime values should be flushed during close */ 410 TEST_F(Read, atime_during_close) 411 { 412 const char FULLPATH[] = "mountpoint/some_file.txt"; 413 const char RELPATH[] = "some_file.txt"; 414 const char *CONTENTS = "abcdefgh"; 415 struct stat sb; 416 uint64_t ino = 42; 417 const mode_t newmode = 0755; 418 int fd; 419 ssize_t bufsize = strlen(CONTENTS); 420 uint8_t buf[bufsize]; 421 422 expect_lookup(RELPATH, ino, bufsize); 423 expect_open(ino, 0, 1); 424 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 425 EXPECT_CALL(*m_mock, process( 426 ResultOf([&](auto in) { 427 uint32_t valid = FATTR_ATIME; 428 return (in.header.opcode == FUSE_SETATTR && 429 in.header.nodeid == ino && 430 in.body.setattr.valid == valid && 431 (time_t)in.body.setattr.atime == 432 sb.st_atim.tv_sec && 433 (long)in.body.setattr.atimensec == 434 sb.st_atim.tv_nsec); 435 }, Eq(true)), 436 _) 437 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 438 SET_OUT_HEADER_LEN(out, attr); 439 out.body.attr.attr.ino = ino; 440 out.body.attr.attr.mode = S_IFREG | newmode; 441 }))); 442 expect_flush(ino, 1, ReturnErrno(0)); 443 expect_release(ino, FuseTest::FH); 444 445 fd = open(FULLPATH, O_RDONLY); 446 ASSERT_LE(0, fd) << strerror(errno); 447 448 /* Ensure atime will be different than during lookup */ 449 nap(); 450 451 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 452 ASSERT_EQ(0, fstat(fd, &sb)); 453 454 close(fd); 455 } 456 457 /* A cached atime should be flushed during FUSE_SETATTR */ 458 TEST_F(Read, atime_during_setattr) 459 { 460 const char FULLPATH[] = "mountpoint/some_file.txt"; 461 const char RELPATH[] = "some_file.txt"; 462 const char *CONTENTS = "abcdefgh"; 463 struct stat sb; 464 uint64_t ino = 42; 465 const mode_t newmode = 0755; 466 int fd; 467 ssize_t bufsize = strlen(CONTENTS); 468 uint8_t buf[bufsize]; 469 470 expect_lookup(RELPATH, ino, bufsize); 471 expect_open(ino, 0, 1); 472 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 473 EXPECT_CALL(*m_mock, process( 474 ResultOf([&](auto in) { 475 uint32_t valid = FATTR_MODE | FATTR_ATIME; 476 return (in.header.opcode == FUSE_SETATTR && 477 in.header.nodeid == ino && 478 in.body.setattr.valid == valid && 479 (time_t)in.body.setattr.atime == 480 sb.st_atim.tv_sec && 481 (long)in.body.setattr.atimensec == 482 sb.st_atim.tv_nsec); 483 }, Eq(true)), 484 _) 485 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 486 SET_OUT_HEADER_LEN(out, attr); 487 out.body.attr.attr.ino = ino; 488 out.body.attr.attr.mode = S_IFREG | newmode; 489 }))); 490 491 fd = open(FULLPATH, O_RDONLY); 492 ASSERT_LE(0, fd) << strerror(errno); 493 494 /* Ensure atime will be different than during lookup */ 495 nap(); 496 497 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 498 ASSERT_EQ(0, fstat(fd, &sb)); 499 ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 500 501 leak(fd); 502 } 503 504 /* 0-length reads shouldn't cause any confusion */ 505 TEST_F(Read, direct_io_read_nothing) 506 { 507 const char FULLPATH[] = "mountpoint/some_file.txt"; 508 const char RELPATH[] = "some_file.txt"; 509 uint64_t ino = 42; 510 int fd; 511 uint64_t offset = 100; 512 char buf[80]; 513 514 expect_lookup(RELPATH, ino, offset + 1000); 515 expect_open(ino, FOPEN_DIRECT_IO, 1); 516 517 fd = open(FULLPATH, O_RDONLY); 518 ASSERT_LE(0, fd) << strerror(errno); 519 520 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno); 521 leak(fd); 522 } 523 524 /* 525 * With direct_io, reads should not fill the cache. They should go straight to 526 * the daemon 527 */ 528 TEST_F(Read, direct_io_pread) 529 { 530 const char FULLPATH[] = "mountpoint/some_file.txt"; 531 const char RELPATH[] = "some_file.txt"; 532 const char *CONTENTS = "abcdefgh"; 533 uint64_t ino = 42; 534 int fd; 535 uint64_t offset = 100; 536 ssize_t bufsize = strlen(CONTENTS); 537 uint8_t buf[bufsize]; 538 539 expect_lookup(RELPATH, ino, offset + bufsize); 540 expect_open(ino, FOPEN_DIRECT_IO, 1); 541 expect_read(ino, offset, bufsize, bufsize, CONTENTS); 542 543 fd = open(FULLPATH, O_RDONLY); 544 ASSERT_LE(0, fd) << strerror(errno); 545 546 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); 547 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 548 549 // With FOPEN_DIRECT_IO, the cache should be bypassed. The server will 550 // get a 2nd read request. 551 expect_read(ino, offset, bufsize, bufsize, CONTENTS); 552 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); 553 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 554 leak(fd); 555 } 556 557 /* 558 * With direct_io, filesystems are allowed to return less data than is 559 * requested. fuse(4) should return a short read to userland. 560 */ 561 TEST_F(Read, direct_io_short_read) 562 { 563 const char FULLPATH[] = "mountpoint/some_file.txt"; 564 const char RELPATH[] = "some_file.txt"; 565 const char *CONTENTS = "abcdefghijklmnop"; 566 uint64_t ino = 42; 567 int fd; 568 uint64_t offset = 100; 569 ssize_t bufsize = strlen(CONTENTS); 570 ssize_t halfbufsize = bufsize / 2; 571 uint8_t buf[bufsize]; 572 573 expect_lookup(RELPATH, ino, offset + bufsize); 574 expect_open(ino, FOPEN_DIRECT_IO, 1); 575 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS); 576 577 fd = open(FULLPATH, O_RDONLY); 578 ASSERT_LE(0, fd) << strerror(errno); 579 580 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset)) 581 << strerror(errno); 582 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize)); 583 leak(fd); 584 } 585 586 TEST_F(Read, eio) 587 { 588 const char FULLPATH[] = "mountpoint/some_file.txt"; 589 const char RELPATH[] = "some_file.txt"; 590 const char *CONTENTS = "abcdefgh"; 591 uint64_t ino = 42; 592 int fd; 593 ssize_t bufsize = strlen(CONTENTS); 594 uint8_t buf[bufsize]; 595 596 expect_lookup(RELPATH, ino, bufsize); 597 expect_open(ino, 0, 1); 598 EXPECT_CALL(*m_mock, process( 599 ResultOf([=](auto in) { 600 return (in.header.opcode == FUSE_READ); 601 }, Eq(true)), 602 _) 603 ).WillOnce(Invoke(ReturnErrno(EIO))); 604 605 fd = open(FULLPATH, O_RDONLY); 606 ASSERT_LE(0, fd) << strerror(errno); 607 608 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno); 609 ASSERT_EQ(EIO, errno); 610 leak(fd); 611 } 612 613 /* 614 * If the server returns a short read when direct io is not in use, that 615 * indicates EOF, because of a server-side truncation. We should invalidate 616 * all cached attributes. We may update the file size, 617 */ 618 TEST_F(Read, eof) 619 { 620 const char FULLPATH[] = "mountpoint/some_file.txt"; 621 const char RELPATH[] = "some_file.txt"; 622 const char *CONTENTS = "abcdefghijklmnop"; 623 uint64_t ino = 42; 624 int fd; 625 uint64_t offset = 100; 626 ssize_t bufsize = strlen(CONTENTS); 627 ssize_t partbufsize = 3 * bufsize / 4; 628 ssize_t r; 629 uint8_t buf[bufsize]; 630 struct stat sb; 631 632 expect_lookup(RELPATH, ino, offset + bufsize); 633 expect_open(ino, 0, 1); 634 expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS); 635 expect_getattr(ino, offset + partbufsize); 636 637 fd = open(FULLPATH, O_RDONLY); 638 ASSERT_LE(0, fd) << strerror(errno); 639 640 r = pread(fd, buf, bufsize, offset); 641 ASSERT_LE(0, r) << strerror(errno); 642 EXPECT_EQ(partbufsize, r) << strerror(errno); 643 ASSERT_EQ(0, fstat(fd, &sb)); 644 EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size); 645 leak(fd); 646 } 647 648 /* Like Read.eof, but causes an entire buffer to be invalidated */ 649 TEST_F(Read, eof_of_whole_buffer) 650 { 651 const char FULLPATH[] = "mountpoint/some_file.txt"; 652 const char RELPATH[] = "some_file.txt"; 653 const char *CONTENTS = "abcdefghijklmnop"; 654 uint64_t ino = 42; 655 int fd; 656 ssize_t bufsize = strlen(CONTENTS); 657 off_t old_filesize = m_maxbcachebuf * 2 + bufsize; 658 uint8_t buf[bufsize]; 659 struct stat sb; 660 661 expect_lookup(RELPATH, ino, old_filesize); 662 expect_open(ino, 0, 1); 663 expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS); 664 expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS); 665 expect_getattr(ino, m_maxbcachebuf); 666 667 fd = open(FULLPATH, O_RDONLY); 668 ASSERT_LE(0, fd) << strerror(errno); 669 670 /* Cache the third block */ 671 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, m_maxbcachebuf * 2)) 672 << strerror(errno); 673 /* Try to read the 2nd block, but it's past EOF */ 674 ASSERT_EQ(0, pread(fd, buf, bufsize, m_maxbcachebuf)) 675 << strerror(errno); 676 ASSERT_EQ(0, fstat(fd, &sb)); 677 EXPECT_EQ((off_t)(m_maxbcachebuf), sb.st_size); 678 leak(fd); 679 } 680 681 /* 682 * With the keep_cache option, the kernel may keep its read cache across 683 * multiple open(2)s. 684 */ 685 TEST_F(Read, keep_cache) 686 { 687 const char FULLPATH[] = "mountpoint/some_file.txt"; 688 const char RELPATH[] = "some_file.txt"; 689 const char *CONTENTS = "abcdefgh"; 690 uint64_t ino = 42; 691 int fd0, fd1; 692 ssize_t bufsize = strlen(CONTENTS); 693 uint8_t buf[bufsize]; 694 695 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2); 696 expect_open(ino, FOPEN_KEEP_CACHE, 2); 697 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 698 699 fd0 = open(FULLPATH, O_RDONLY); 700 ASSERT_LE(0, fd0) << strerror(errno); 701 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); 702 703 fd1 = open(FULLPATH, O_RDWR); 704 ASSERT_LE(0, fd1) << strerror(errno); 705 706 /* 707 * This read should be serviced by cache, even though it's on the other 708 * file descriptor 709 */ 710 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno); 711 712 leak(fd0); 713 leak(fd1); 714 } 715 716 /* 717 * Without the keep_cache option, the kernel should drop its read caches on 718 * every open 719 */ 720 TEST_F(Read, keep_cache_disabled) 721 { 722 const char FULLPATH[] = "mountpoint/some_file.txt"; 723 const char RELPATH[] = "some_file.txt"; 724 const char *CONTENTS = "abcdefgh"; 725 uint64_t ino = 42; 726 int fd0, fd1; 727 ssize_t bufsize = strlen(CONTENTS); 728 uint8_t buf[bufsize]; 729 730 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2); 731 expect_open(ino, 0, 2); 732 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 733 734 fd0 = open(FULLPATH, O_RDONLY); 735 ASSERT_LE(0, fd0) << strerror(errno); 736 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); 737 738 fd1 = open(FULLPATH, O_RDWR); 739 ASSERT_LE(0, fd1) << strerror(errno); 740 741 /* 742 * This read should not be serviced by cache, even though it's on the 743 * original file descriptor 744 */ 745 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 746 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno); 747 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno); 748 749 leak(fd0); 750 leak(fd1); 751 } 752 753 TEST_F(Read, mmap) 754 { 755 const char FULLPATH[] = "mountpoint/some_file.txt"; 756 const char RELPATH[] = "some_file.txt"; 757 const char *CONTENTS = "abcdefgh"; 758 uint64_t ino = 42; 759 int fd; 760 ssize_t len; 761 size_t bufsize = strlen(CONTENTS); 762 void *p; 763 764 len = getpagesize(); 765 766 expect_lookup(RELPATH, ino, bufsize); 767 expect_open(ino, 0, 1); 768 EXPECT_CALL(*m_mock, process( 769 ResultOf([=](auto in) { 770 return (in.header.opcode == FUSE_READ && 771 in.header.nodeid == ino && 772 in.body.read.fh == Read::FH && 773 in.body.read.offset == 0 && 774 in.body.read.size == bufsize); 775 }, Eq(true)), 776 _) 777 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 778 out.header.len = sizeof(struct fuse_out_header) + bufsize; 779 memmove(out.body.bytes, CONTENTS, bufsize); 780 }))); 781 782 fd = open(FULLPATH, O_RDONLY); 783 ASSERT_LE(0, fd) << strerror(errno); 784 785 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); 786 ASSERT_NE(MAP_FAILED, p) << strerror(errno); 787 788 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize)); 789 790 ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 791 leak(fd); 792 } 793 794 /* 795 * The kernel should not update the cached atime attribute during a read, if 796 * MNT_NOATIME is used. 797 */ 798 TEST_F(ReadNoatime, atime) 799 { 800 const char FULLPATH[] = "mountpoint/some_file.txt"; 801 const char RELPATH[] = "some_file.txt"; 802 const char *CONTENTS = "abcdefgh"; 803 struct stat sb1, sb2; 804 uint64_t ino = 42; 805 int fd; 806 ssize_t bufsize = strlen(CONTENTS); 807 uint8_t buf[bufsize]; 808 809 expect_lookup(RELPATH, ino, bufsize); 810 expect_open(ino, 0, 1); 811 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 812 813 fd = open(FULLPATH, O_RDONLY); 814 ASSERT_LE(0, fd) << strerror(errno); 815 ASSERT_EQ(0, fstat(fd, &sb1)); 816 817 nap(); 818 819 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 820 ASSERT_EQ(0, fstat(fd, &sb2)); 821 822 /* The kernel should not update atime during read */ 823 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==)); 824 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); 825 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); 826 827 leak(fd); 828 } 829 830 /* 831 * The kernel should not update the cached atime attribute during a cached 832 * read, if MNT_NOATIME is used. 833 */ 834 TEST_F(ReadNoatime, atime_cached) 835 { 836 const char FULLPATH[] = "mountpoint/some_file.txt"; 837 const char RELPATH[] = "some_file.txt"; 838 const char *CONTENTS = "abcdefgh"; 839 struct stat sb1, sb2; 840 uint64_t ino = 42; 841 int fd; 842 ssize_t bufsize = strlen(CONTENTS); 843 uint8_t buf[bufsize]; 844 845 expect_lookup(RELPATH, ino, bufsize); 846 expect_open(ino, 0, 1); 847 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 848 849 fd = open(FULLPATH, O_RDONLY); 850 ASSERT_LE(0, fd) << strerror(errno); 851 852 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); 853 ASSERT_EQ(0, fstat(fd, &sb1)); 854 855 nap(); 856 857 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno); 858 ASSERT_EQ(0, fstat(fd, &sb2)); 859 860 /* The kernel should automatically update atime during read */ 861 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==)); 862 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==)); 863 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==)); 864 865 leak(fd); 866 } 867 868 /* Read of an mmap()ed file fails */ 869 TEST_F(ReadSigbus, mmap_eio) 870 { 871 const char FULLPATH[] = "mountpoint/some_file.txt"; 872 const char RELPATH[] = "some_file.txt"; 873 const char *CONTENTS = "abcdefgh"; 874 struct sigaction sa; 875 uint64_t ino = 42; 876 int fd; 877 ssize_t len; 878 size_t bufsize = strlen(CONTENTS); 879 void *p; 880 881 len = getpagesize(); 882 883 expect_lookup(RELPATH, ino, bufsize); 884 expect_open(ino, 0, 1); 885 EXPECT_CALL(*m_mock, process( 886 ResultOf([=](auto in) { 887 return (in.header.opcode == FUSE_READ && 888 in.header.nodeid == ino && 889 in.body.read.fh == Read::FH); 890 }, Eq(true)), 891 _) 892 ).WillRepeatedly(Invoke(ReturnErrno(EIO))); 893 894 fd = open(FULLPATH, O_RDONLY); 895 ASSERT_LE(0, fd) << strerror(errno); 896 897 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); 898 ASSERT_NE(MAP_FAILED, p) << strerror(errno); 899 900 /* Accessing the mapped page should return SIGBUS. */ 901 902 bzero(&sa, sizeof(sa)); 903 sa.sa_handler = SIG_DFL; 904 sa.sa_sigaction = handle_sigbus; 905 sa.sa_flags = SA_RESETHAND | SA_SIGINFO; 906 ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno); 907 if (setjmp(ReadSigbus::s_jmpbuf) == 0) { 908 atomic_signal_fence(std::memory_order::memory_order_seq_cst); 909 volatile char x __unused = *(volatile char*)p; 910 FAIL() << "shouldn't get here"; 911 } 912 913 ASSERT_EQ(p, ReadSigbus::s_si_addr); 914 ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 915 leak(fd); 916 } 917 918 /* 919 * A read via mmap comes up short, indicating that the file was truncated 920 * server-side. 921 */ 922 TEST_F(Read, mmap_eof) 923 { 924 const char FULLPATH[] = "mountpoint/some_file.txt"; 925 const char RELPATH[] = "some_file.txt"; 926 const char *CONTENTS = "abcdefgh"; 927 uint64_t ino = 42; 928 int fd; 929 ssize_t len; 930 size_t bufsize = strlen(CONTENTS); 931 struct stat sb; 932 void *p; 933 934 len = getpagesize(); 935 936 expect_lookup(RELPATH, ino, m_maxbcachebuf); 937 expect_open(ino, 0, 1); 938 EXPECT_CALL(*m_mock, process( 939 ResultOf([=](auto in) { 940 return (in.header.opcode == FUSE_READ && 941 in.header.nodeid == ino && 942 in.body.read.fh == Read::FH && 943 in.body.read.offset == 0 && 944 in.body.read.size == (uint32_t)m_maxbcachebuf); 945 }, Eq(true)), 946 _) 947 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 948 out.header.len = sizeof(struct fuse_out_header) + bufsize; 949 memmove(out.body.bytes, CONTENTS, bufsize); 950 }))); 951 expect_getattr(ino, bufsize); 952 953 fd = open(FULLPATH, O_RDONLY); 954 ASSERT_LE(0, fd) << strerror(errno); 955 956 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); 957 ASSERT_NE(MAP_FAILED, p) << strerror(errno); 958 959 /* The file size should be automatically truncated */ 960 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize)); 961 ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 962 EXPECT_EQ((off_t)bufsize, sb.st_size); 963 964 ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 965 leak(fd); 966 } 967 968 /* 969 * During VOP_GETPAGES, the FUSE server fails a FUSE_GETATTR operation. This 970 * almost certainly indicates a buggy FUSE server, and our goal should be not 971 * to panic. Instead, generate SIGBUS. 972 */ 973 TEST_F(ReadSigbus, mmap_getblksz_fail) 974 { 975 const char FULLPATH[] = "mountpoint/some_file.txt"; 976 const char RELPATH[] = "some_file.txt"; 977 const char *CONTENTS = "abcdefgh"; 978 struct sigaction sa; 979 Sequence seq; 980 uint64_t ino = 42; 981 int fd; 982 ssize_t len; 983 size_t bufsize = strlen(CONTENTS); 984 mode_t mode = S_IFREG | 0644; 985 void *p; 986 987 len = getpagesize(); 988 989 FuseTest::expect_lookup(RELPATH, ino, mode, bufsize, 1, 0); 990 /* Expect two GETATTR calls that succeed, followed by one that fail. */ 991 EXPECT_CALL(*m_mock, process( 992 ResultOf([=](auto in) { 993 return (in.header.opcode == FUSE_GETATTR && 994 in.header.nodeid == ino); 995 }, Eq(true)), 996 _) 997 ).Times(2) 998 .InSequence(seq) 999 .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 1000 SET_OUT_HEADER_LEN(out, attr); 1001 out.body.attr.attr.ino = ino; 1002 out.body.attr.attr.mode = mode; 1003 out.body.attr.attr.size = bufsize; 1004 out.body.attr.attr_valid = 0; 1005 }))); 1006 EXPECT_CALL(*m_mock, process( 1007 ResultOf([=](auto in) { 1008 return (in.header.opcode == FUSE_GETATTR && 1009 in.header.nodeid == ino); 1010 }, Eq(true)), 1011 _) 1012 ).InSequence(seq) 1013 .WillRepeatedly(Invoke(ReturnErrno(EIO))); 1014 expect_open(ino, 0, 1); 1015 EXPECT_CALL(*m_mock, process( 1016 ResultOf([=](auto in) { 1017 return (in.header.opcode == FUSE_READ); 1018 }, Eq(true)), 1019 _) 1020 ).Times(0); 1021 1022 fd = open(FULLPATH, O_RDONLY); 1023 ASSERT_LE(0, fd) << strerror(errno); 1024 1025 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); 1026 ASSERT_NE(MAP_FAILED, p) << strerror(errno); 1027 1028 /* Accessing the mapped page should return SIGBUS. */ 1029 bzero(&sa, sizeof(sa)); 1030 sa.sa_handler = SIG_DFL; 1031 sa.sa_sigaction = handle_sigbus; 1032 sa.sa_flags = SA_RESETHAND | SA_SIGINFO; 1033 ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno); 1034 if (setjmp(ReadSigbus::s_jmpbuf) == 0) { 1035 atomic_signal_fence(std::memory_order::memory_order_seq_cst); 1036 volatile char x __unused = *(volatile char*)p; 1037 FAIL() << "shouldn't get here"; 1038 } 1039 1040 ASSERT_EQ(p, ReadSigbus::s_si_addr); 1041 ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 1042 leak(fd); 1043 } 1044 1045 /* 1046 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass 1047 * cache and to straight to the daemon 1048 */ 1049 TEST_F(Read, o_direct) 1050 { 1051 const char FULLPATH[] = "mountpoint/some_file.txt"; 1052 const char RELPATH[] = "some_file.txt"; 1053 const char *CONTENTS = "abcdefgh"; 1054 uint64_t ino = 42; 1055 int fd; 1056 ssize_t bufsize = strlen(CONTENTS); 1057 uint8_t buf[bufsize]; 1058 1059 expect_lookup(RELPATH, ino, bufsize); 1060 expect_open(ino, 0, 1); 1061 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1062 1063 fd = open(FULLPATH, O_RDONLY); 1064 ASSERT_LE(0, fd) << strerror(errno); 1065 1066 // Fill the cache 1067 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1068 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1069 1070 // Reads with o_direct should bypass the cache 1071 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1072 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 1073 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 1074 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1075 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1076 1077 leak(fd); 1078 } 1079 1080 TEST_F(Read, pread) 1081 { 1082 const char FULLPATH[] = "mountpoint/some_file.txt"; 1083 const char RELPATH[] = "some_file.txt"; 1084 const char *CONTENTS = "abcdefgh"; 1085 uint64_t ino = 42; 1086 int fd; 1087 /* 1088 * Set offset to a maxbcachebuf boundary so we'll be sure what offset 1089 * to read from. Without this, the read might start at a lower offset. 1090 */ 1091 uint64_t offset = m_maxbcachebuf; 1092 ssize_t bufsize = strlen(CONTENTS); 1093 uint8_t buf[bufsize]; 1094 1095 expect_lookup(RELPATH, ino, offset + bufsize); 1096 expect_open(ino, 0, 1); 1097 expect_read(ino, offset, bufsize, bufsize, CONTENTS); 1098 1099 fd = open(FULLPATH, O_RDONLY); 1100 ASSERT_LE(0, fd) << strerror(errno); 1101 1102 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno); 1103 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1104 leak(fd); 1105 } 1106 1107 TEST_F(Read, read) 1108 { 1109 const char FULLPATH[] = "mountpoint/some_file.txt"; 1110 const char RELPATH[] = "some_file.txt"; 1111 const char *CONTENTS = "abcdefgh"; 1112 uint64_t ino = 42; 1113 int fd; 1114 ssize_t bufsize = strlen(CONTENTS); 1115 uint8_t buf[bufsize]; 1116 1117 expect_lookup(RELPATH, ino, bufsize); 1118 expect_open(ino, 0, 1); 1119 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1120 1121 fd = open(FULLPATH, O_RDONLY); 1122 ASSERT_LE(0, fd) << strerror(errno); 1123 1124 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1125 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1126 1127 leak(fd); 1128 } 1129 1130 TEST_F(Read_7_8, read) 1131 { 1132 const char FULLPATH[] = "mountpoint/some_file.txt"; 1133 const char RELPATH[] = "some_file.txt"; 1134 const char *CONTENTS = "abcdefgh"; 1135 uint64_t ino = 42; 1136 int fd; 1137 ssize_t bufsize = strlen(CONTENTS); 1138 uint8_t buf[bufsize]; 1139 1140 expect_lookup(RELPATH, ino, bufsize); 1141 expect_open(ino, 0, 1); 1142 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1143 1144 fd = open(FULLPATH, O_RDONLY); 1145 ASSERT_LE(0, fd) << strerror(errno); 1146 1147 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1148 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1149 1150 leak(fd); 1151 } 1152 1153 /* 1154 * If cacheing is enabled, the kernel should try to read an entire cache block 1155 * at a time. 1156 */ 1157 TEST_F(Read, cache_block) 1158 { 1159 const char FULLPATH[] = "mountpoint/some_file.txt"; 1160 const char RELPATH[] = "some_file.txt"; 1161 const char *CONTENTS0 = "abcdefghijklmnop"; 1162 uint64_t ino = 42; 1163 int fd; 1164 ssize_t bufsize = 8; 1165 ssize_t filesize = m_maxbcachebuf * 2; 1166 char *contents; 1167 char buf[bufsize]; 1168 const char *contents1 = CONTENTS0 + bufsize; 1169 1170 contents = (char*)calloc(1, filesize); 1171 ASSERT_NE(nullptr, contents); 1172 memmove(contents, CONTENTS0, strlen(CONTENTS0)); 1173 1174 expect_lookup(RELPATH, ino, filesize); 1175 expect_open(ino, 0, 1); 1176 expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, 1177 contents); 1178 1179 fd = open(FULLPATH, O_RDONLY); 1180 ASSERT_LE(0, fd) << strerror(errno); 1181 1182 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1183 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize)); 1184 1185 /* A subsequent read should be serviced by cache */ 1186 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1187 ASSERT_EQ(0, memcmp(buf, contents1, bufsize)); 1188 leak(fd); 1189 free(contents); 1190 } 1191 1192 /* Reading with sendfile should work (though it obviously won't be 0-copy) */ 1193 TEST_F(Read, sendfile) 1194 { 1195 const char FULLPATH[] = "mountpoint/some_file.txt"; 1196 const char RELPATH[] = "some_file.txt"; 1197 const char *CONTENTS = "abcdefgh"; 1198 uint64_t ino = 42; 1199 int fd; 1200 size_t bufsize = strlen(CONTENTS); 1201 uint8_t buf[bufsize]; 1202 int sp[2]; 1203 off_t sbytes; 1204 1205 expect_lookup(RELPATH, ino, bufsize); 1206 expect_open(ino, 0, 1); 1207 EXPECT_CALL(*m_mock, process( 1208 ResultOf([=](auto in) { 1209 return (in.header.opcode == FUSE_READ && 1210 in.header.nodeid == ino && 1211 in.body.read.fh == Read::FH && 1212 in.body.read.offset == 0 && 1213 in.body.read.size == bufsize); 1214 }, Eq(true)), 1215 _) 1216 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1217 out.header.len = sizeof(struct fuse_out_header) + bufsize; 1218 memmove(out.body.bytes, CONTENTS, bufsize); 1219 }))); 1220 1221 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp)) 1222 << strerror(errno); 1223 fd = open(FULLPATH, O_RDONLY); 1224 ASSERT_LE(0, fd) << strerror(errno); 1225 1226 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0)) 1227 << strerror(errno); 1228 ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize)) 1229 << strerror(errno); 1230 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize)); 1231 1232 close(sp[1]); 1233 close(sp[0]); 1234 leak(fd); 1235 } 1236 1237 /* sendfile should fail gracefully if fuse declines the read */ 1238 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */ 1239 TEST_F(Read, sendfile_eio) 1240 { 1241 const char FULLPATH[] = "mountpoint/some_file.txt"; 1242 const char RELPATH[] = "some_file.txt"; 1243 const char *CONTENTS = "abcdefgh"; 1244 uint64_t ino = 42; 1245 int fd; 1246 ssize_t bufsize = strlen(CONTENTS); 1247 int sp[2]; 1248 off_t sbytes; 1249 1250 expect_lookup(RELPATH, ino, bufsize); 1251 expect_open(ino, 0, 1); 1252 EXPECT_CALL(*m_mock, process( 1253 ResultOf([=](auto in) { 1254 return (in.header.opcode == FUSE_READ); 1255 }, Eq(true)), 1256 _) 1257 ).WillOnce(Invoke(ReturnErrno(EIO))); 1258 1259 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp)) 1260 << strerror(errno); 1261 fd = open(FULLPATH, O_RDONLY); 1262 ASSERT_LE(0, fd) << strerror(errno); 1263 1264 ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0)); 1265 1266 close(sp[1]); 1267 close(sp[0]); 1268 leak(fd); 1269 } 1270 1271 /* 1272 * Sequential reads should use readahead. And if allowed, large reads should 1273 * be clustered. 1274 */ 1275 TEST_P(ReadAhead, readahead) { 1276 const char FULLPATH[] = "mountpoint/some_file.txt"; 1277 const char RELPATH[] = "some_file.txt"; 1278 uint64_t ino = 42; 1279 int fd, maxcontig, clustersize; 1280 ssize_t bufsize = 4 * m_maxbcachebuf; 1281 ssize_t filesize = bufsize; 1282 uint64_t len; 1283 char *rbuf, *contents; 1284 off_t offs; 1285 1286 contents = (char*)malloc(filesize); 1287 ASSERT_NE(nullptr, contents); 1288 memset(contents, 'X', filesize); 1289 rbuf = (char*)calloc(1, bufsize); 1290 1291 expect_lookup(RELPATH, ino, filesize); 1292 expect_open(ino, 0, 1); 1293 maxcontig = m_noclusterr ? m_maxbcachebuf : 1294 m_maxbcachebuf + m_maxreadahead; 1295 clustersize = MIN(maxcontig, m_maxphys); 1296 for (offs = 0; offs < bufsize; offs += clustersize) { 1297 len = std::min((size_t)clustersize, (size_t)(filesize - offs)); 1298 expect_read(ino, offs, len, len, contents + offs); 1299 } 1300 1301 fd = open(FULLPATH, O_RDONLY); 1302 ASSERT_LE(0, fd) << strerror(errno); 1303 1304 /* Set the internal readahead counter to a "large" value */ 1305 ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno); 1306 1307 ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno); 1308 ASSERT_EQ(0, memcmp(rbuf, contents, bufsize)); 1309 1310 leak(fd); 1311 free(rbuf); 1312 free(contents); 1313 } 1314 1315 INSTANTIATE_TEST_SUITE_P(RA, ReadAhead, 1316 Values(tuple<bool, int>(false, 0), 1317 tuple<bool, int>(false, 1), 1318 tuple<bool, int>(false, 2), 1319 tuple<bool, int>(false, 3), 1320 tuple<bool, int>(true, 0), 1321 tuple<bool, int>(true, 1), 1322 tuple<bool, int>(true, 2))); 1323 1324 /* fuse_init_out.time_gran controls the granularity of timestamps */ 1325 TEST_P(TimeGran, atime_during_setattr) 1326 { 1327 const char FULLPATH[] = "mountpoint/some_file.txt"; 1328 const char RELPATH[] = "some_file.txt"; 1329 const char *CONTENTS = "abcdefgh"; 1330 ssize_t bufsize = strlen(CONTENTS); 1331 uint8_t buf[bufsize]; 1332 uint64_t ino = 42; 1333 const mode_t newmode = 0755; 1334 int fd; 1335 1336 expect_lookup(RELPATH, ino, bufsize); 1337 expect_open(ino, 0, 1); 1338 expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1339 EXPECT_CALL(*m_mock, process( 1340 ResultOf([=](auto in) { 1341 uint32_t valid = FATTR_MODE | FATTR_ATIME; 1342 return (in.header.opcode == FUSE_SETATTR && 1343 in.header.nodeid == ino && 1344 in.body.setattr.valid == valid && 1345 in.body.setattr.atimensec % m_time_gran == 0); 1346 }, Eq(true)), 1347 _) 1348 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1349 SET_OUT_HEADER_LEN(out, attr); 1350 out.body.attr.attr.ino = ino; 1351 out.body.attr.attr.mode = S_IFREG | newmode; 1352 }))); 1353 1354 fd = open(FULLPATH, O_RDWR); 1355 ASSERT_LE(0, fd) << strerror(errno); 1356 1357 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno); 1358 ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 1359 1360 leak(fd); 1361 } 1362 1363 INSTANTIATE_TEST_SUITE_P(TG, TimeGran, Range(0u, 10u)); 1364