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