2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2019 The FreeBSD Foundation
6 * This software was developed by BFF Storage Systems, LLC under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/sysctl.h>
40 #include <semaphore.h>
47 using namespace testing;
49 class Read: public FuseTest {
52 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
54 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
58 class Read_7_8: public FuseTest {
60 virtual void SetUp() {
61 m_kernel_minor_version = 8;
65 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
67 FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
71 class AioRead: public Read {
73 virtual void SetUp() {
74 const char *node = "vfs.aio.enable_unsafe";
76 size_t size = sizeof(val);
80 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
84 "vfs.aio.enable_unsafe must be set for this test";
88 class AsyncRead: public AioRead {
89 virtual void SetUp() {
90 m_init_flags = FUSE_ASYNC_READ;
95 class ReadCacheable: public Read {
97 virtual void SetUp() {
98 const char *node = "vfs.fusefs.data_cache_mode";
100 size_t size = sizeof(val);
104 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
108 "fusefs data caching must be enabled for this test";
112 class ReadAhead: public ReadCacheable, public WithParamInterface<uint32_t> {
113 virtual void SetUp() {
114 m_maxreadahead = GetParam();
119 /* AIO reads need to set the header's pid field correctly */
120 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
121 TEST_F(AioRead, aio_read)
123 const char FULLPATH[] = "mountpoint/some_file.txt";
124 const char RELPATH[] = "some_file.txt";
125 const char *CONTENTS = "abcdefgh";
128 ssize_t bufsize = strlen(CONTENTS);
130 struct aiocb iocb, *piocb;
132 expect_lookup(RELPATH, ino, bufsize);
133 expect_open(ino, 0, 1);
134 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
136 fd = open(FULLPATH, O_RDONLY);
137 ASSERT_LE(0, fd) << strerror(errno);
139 iocb.aio_nbytes = bufsize;
140 iocb.aio_fildes = fd;
143 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
144 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
145 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
146 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
147 /* Deliberately leak fd. close(2) will be tested in release.cc */
151 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
152 * is at most one outstanding read operation per file handle
154 TEST_F(AioRead, async_read_disabled)
156 const char FULLPATH[] = "mountpoint/some_file.txt";
157 const char RELPATH[] = "some_file.txt";
160 ssize_t bufsize = 50;
161 char buf0[bufsize], buf1[bufsize];
164 struct aiocb iocb0, iocb1;
165 volatile sig_atomic_t read_count = 0;
167 expect_lookup(RELPATH, ino, 131072);
168 expect_open(ino, 0, 1);
169 EXPECT_CALL(*m_mock, process(
170 ResultOf([=](auto in) {
171 return (in->header.opcode == FUSE_READ &&
172 in->header.nodeid == ino &&
173 in->body.read.fh == FH &&
174 in->body.read.offset == (uint64_t)off0);
177 ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
179 /* Filesystem is slow to respond */
181 EXPECT_CALL(*m_mock, process(
182 ResultOf([=](auto in) {
183 return (in->header.opcode == FUSE_READ &&
184 in->header.nodeid == ino &&
185 in->body.read.fh == FH &&
186 in->body.read.offset == (uint64_t)off1);
189 ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
191 /* Filesystem is slow to respond */
194 fd = open(FULLPATH, O_RDONLY);
195 ASSERT_LE(0, fd) << strerror(errno);
198 * Submit two AIO read requests, and respond to neither. If the
199 * filesystem ever gets the second read request, then we failed to
200 * limit outstanding reads.
202 iocb0.aio_nbytes = bufsize;
203 iocb0.aio_fildes = fd;
204 iocb0.aio_buf = buf0;
205 iocb0.aio_offset = off0;
206 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
207 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
209 iocb1.aio_nbytes = bufsize;
210 iocb1.aio_fildes = fd;
211 iocb1.aio_buf = buf1;
212 iocb1.aio_offset = off1;
213 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
214 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
217 * Sleep for awhile to make sure the kernel has had a chance to issue
218 * the second read, even though the first has not yet returned
221 EXPECT_EQ(read_count, 1);
223 m_mock->kill_daemon();
224 /* Wait for AIO activity to complete, but ignore errors */
225 (void)aio_waitcomplete(NULL, NULL);
227 /* Deliberately leak fd. close(2) will be tested in release.cc */
231 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
232 * simultaneous read requests on the same file handle.
234 TEST_F(AsyncRead, async_read)
236 const char FULLPATH[] = "mountpoint/some_file.txt";
237 const char RELPATH[] = "some_file.txt";
240 ssize_t bufsize = 50;
241 char buf0[bufsize], buf1[bufsize];
244 struct aiocb iocb0, iocb1;
247 ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
249 expect_lookup(RELPATH, ino, 131072);
250 expect_open(ino, 0, 1);
251 EXPECT_CALL(*m_mock, process(
252 ResultOf([=](auto in) {
253 return (in->header.opcode == FUSE_READ &&
254 in->header.nodeid == ino &&
255 in->body.read.fh == FH &&
256 in->body.read.offset == (uint64_t)off0);
259 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
261 /* Filesystem is slow to respond */
263 EXPECT_CALL(*m_mock, process(
264 ResultOf([=](auto in) {
265 return (in->header.opcode == FUSE_READ &&
266 in->header.nodeid == ino &&
267 in->body.read.fh == FH &&
268 in->body.read.offset == (uint64_t)off1);
271 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
273 /* Filesystem is slow to respond */
276 fd = open(FULLPATH, O_RDONLY);
277 ASSERT_LE(0, fd) << strerror(errno);
280 * Submit two AIO read requests, but respond to neither. Ensure that
283 iocb0.aio_nbytes = bufsize;
284 iocb0.aio_fildes = fd;
285 iocb0.aio_buf = buf0;
286 iocb0.aio_offset = off0;
287 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
288 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
290 iocb1.aio_nbytes = bufsize;
291 iocb1.aio_fildes = fd;
292 iocb1.aio_buf = buf1;
293 iocb1.aio_offset = off1;
294 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
295 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
297 /* Wait until both reads have reached the daemon */
298 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
299 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
301 m_mock->kill_daemon();
302 /* Wait for AIO activity to complete, but ignore errors */
303 (void)aio_waitcomplete(NULL, NULL);
305 /* Deliberately leak fd. close(2) will be tested in release.cc */
308 /* 0-length reads shouldn't cause any confusion */
309 TEST_F(Read, direct_io_read_nothing)
311 const char FULLPATH[] = "mountpoint/some_file.txt";
312 const char RELPATH[] = "some_file.txt";
315 uint64_t offset = 100;
318 expect_lookup(RELPATH, ino, offset + 1000);
319 expect_open(ino, FOPEN_DIRECT_IO, 1);
321 fd = open(FULLPATH, O_RDONLY);
322 ASSERT_LE(0, fd) << strerror(errno);
324 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
325 /* Deliberately leak fd. close(2) will be tested in release.cc */
329 * With direct_io, reads should not fill the cache. They should go straight to
332 TEST_F(Read, direct_io_pread)
334 const char FULLPATH[] = "mountpoint/some_file.txt";
335 const char RELPATH[] = "some_file.txt";
336 const char *CONTENTS = "abcdefgh";
339 uint64_t offset = 100;
340 ssize_t bufsize = strlen(CONTENTS);
343 expect_lookup(RELPATH, ino, offset + bufsize);
344 expect_open(ino, FOPEN_DIRECT_IO, 1);
345 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
347 fd = open(FULLPATH, O_RDONLY);
348 ASSERT_LE(0, fd) << strerror(errno);
350 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
351 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
352 /* Deliberately leak fd. close(2) will be tested in release.cc */
356 * With direct_io, filesystems are allowed to return less data than is
357 * requested. fuse(4) should return a short read to userland.
359 TEST_F(Read, direct_io_short_read)
361 const char FULLPATH[] = "mountpoint/some_file.txt";
362 const char RELPATH[] = "some_file.txt";
363 const char *CONTENTS = "abcdefghijklmnop";
366 uint64_t offset = 100;
367 ssize_t bufsize = strlen(CONTENTS);
368 ssize_t halfbufsize = bufsize / 2;
371 expect_lookup(RELPATH, ino, offset + bufsize);
372 expect_open(ino, FOPEN_DIRECT_IO, 1);
373 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
375 fd = open(FULLPATH, O_RDONLY);
376 ASSERT_LE(0, fd) << strerror(errno);
378 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
380 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
381 /* Deliberately leak fd. close(2) will be tested in release.cc */
386 const char FULLPATH[] = "mountpoint/some_file.txt";
387 const char RELPATH[] = "some_file.txt";
388 const char *CONTENTS = "abcdefgh";
391 ssize_t bufsize = strlen(CONTENTS);
394 expect_lookup(RELPATH, ino, bufsize);
395 expect_open(ino, 0, 1);
396 EXPECT_CALL(*m_mock, process(
397 ResultOf([=](auto in) {
398 return (in->header.opcode == FUSE_READ);
401 ).WillOnce(Invoke(ReturnErrno(EIO)));
403 fd = open(FULLPATH, O_RDONLY);
404 ASSERT_LE(0, fd) << strerror(errno);
406 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
407 ASSERT_EQ(EIO, errno);
408 /* Deliberately leak fd. close(2) will be tested in release.cc */
412 * With the keep_cache option, the kernel may keep its read cache across
415 TEST_F(ReadCacheable, keep_cache)
417 const char FULLPATH[] = "mountpoint/some_file.txt";
418 const char RELPATH[] = "some_file.txt";
419 const char *CONTENTS = "abcdefgh";
422 ssize_t bufsize = strlen(CONTENTS);
425 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
426 expect_open(ino, FOPEN_KEEP_CACHE, 2);
427 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
429 fd0 = open(FULLPATH, O_RDONLY);
430 ASSERT_LE(0, fd0) << strerror(errno);
431 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
433 fd1 = open(FULLPATH, O_RDWR);
434 ASSERT_LE(0, fd1) << strerror(errno);
437 * This read should be serviced by cache, even though it's on the other
440 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
442 /* Deliberately leak fd0 and fd1. */
446 * Without the keep_cache option, the kernel should drop its read caches on
449 TEST_F(Read, keep_cache_disabled)
451 const char FULLPATH[] = "mountpoint/some_file.txt";
452 const char RELPATH[] = "some_file.txt";
453 const char *CONTENTS = "abcdefgh";
456 ssize_t bufsize = strlen(CONTENTS);
459 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
460 expect_open(ino, 0, 2);
461 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
463 fd0 = open(FULLPATH, O_RDONLY);
464 ASSERT_LE(0, fd0) << strerror(errno);
465 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
467 fd1 = open(FULLPATH, O_RDWR);
468 ASSERT_LE(0, fd1) << strerror(errno);
471 * This read should not be serviced by cache, even though it's on the
472 * original file descriptor
474 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
475 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
476 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
478 /* Deliberately leak fd0 and fd1. */
481 TEST_F(ReadCacheable, mmap)
483 const char FULLPATH[] = "mountpoint/some_file.txt";
484 const char RELPATH[] = "some_file.txt";
485 const char *CONTENTS = "abcdefgh";
489 ssize_t bufsize = strlen(CONTENTS);
495 expect_lookup(RELPATH, ino, bufsize);
496 expect_open(ino, 0, 1);
497 /* mmap may legitimately try to read more data than is available */
498 EXPECT_CALL(*m_mock, process(
499 ResultOf([=](auto in) {
500 return (in->header.opcode == FUSE_READ &&
501 in->header.nodeid == ino &&
502 in->body.read.fh == Read::FH &&
503 in->body.read.offset == 0 &&
504 in->body.read.size >= bufsize);
507 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
508 out->header.len = sizeof(struct fuse_out_header) + bufsize;
509 memmove(out->body.bytes, CONTENTS, bufsize);
512 fd = open(FULLPATH, O_RDONLY);
513 ASSERT_LE(0, fd) << strerror(errno);
515 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
516 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
518 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
520 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
521 /* Deliberately leak fd. close(2) will be tested in release.cc */
525 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
526 * cache and to straight to the daemon
528 TEST_F(Read, o_direct)
530 const char FULLPATH[] = "mountpoint/some_file.txt";
531 const char RELPATH[] = "some_file.txt";
532 const char *CONTENTS = "abcdefgh";
535 ssize_t bufsize = strlen(CONTENTS);
538 expect_lookup(RELPATH, ino, bufsize);
539 expect_open(ino, 0, 1);
540 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
542 fd = open(FULLPATH, O_RDONLY);
543 ASSERT_LE(0, fd) << strerror(errno);
546 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
547 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
549 // Reads with o_direct should bypass the cache
550 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
551 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
552 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
553 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
554 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
556 /* Deliberately leak fd. close(2) will be tested in release.cc */
561 const char FULLPATH[] = "mountpoint/some_file.txt";
562 const char RELPATH[] = "some_file.txt";
563 const char *CONTENTS = "abcdefgh";
567 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
568 * to read from. Without this, the read might start at a lower offset.
570 uint64_t offset = m_maxbcachebuf;
571 ssize_t bufsize = strlen(CONTENTS);
574 expect_lookup(RELPATH, ino, offset + bufsize);
575 expect_open(ino, 0, 1);
576 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
578 fd = open(FULLPATH, O_RDONLY);
579 ASSERT_LE(0, fd) << strerror(errno);
581 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
582 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
583 /* Deliberately leak fd. close(2) will be tested in release.cc */
588 const char FULLPATH[] = "mountpoint/some_file.txt";
589 const char RELPATH[] = "some_file.txt";
590 const char *CONTENTS = "abcdefgh";
593 ssize_t bufsize = strlen(CONTENTS);
596 expect_lookup(RELPATH, ino, bufsize);
597 expect_open(ino, 0, 1);
598 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
600 fd = open(FULLPATH, O_RDONLY);
601 ASSERT_LE(0, fd) << strerror(errno);
603 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
604 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
606 /* Deliberately leak fd. close(2) will be tested in release.cc */
609 TEST_F(Read_7_8, read)
611 const char FULLPATH[] = "mountpoint/some_file.txt";
612 const char RELPATH[] = "some_file.txt";
613 const char *CONTENTS = "abcdefgh";
616 ssize_t bufsize = strlen(CONTENTS);
619 expect_lookup(RELPATH, ino, bufsize);
620 expect_open(ino, 0, 1);
621 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
623 fd = open(FULLPATH, O_RDONLY);
624 ASSERT_LE(0, fd) << strerror(errno);
626 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
627 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
629 /* Deliberately leak fd. close(2) will be tested in release.cc */
632 /* If the filesystem allows it, the kernel should try to readahead */
633 TEST_F(ReadCacheable, default_readahead)
635 const char FULLPATH[] = "mountpoint/some_file.txt";
636 const char RELPATH[] = "some_file.txt";
637 const char *CONTENTS0 = "abcdefghijklmnop";
641 /* hard-coded in fuse_internal.c */
642 size_t default_maxreadahead = 65536;
643 ssize_t filesize = default_maxreadahead * 2;
646 const char *contents1 = CONTENTS0 + bufsize;
648 contents = (char*)calloc(1, filesize);
649 ASSERT_NE(NULL, contents);
650 memmove(contents, CONTENTS0, strlen(CONTENTS0));
652 expect_lookup(RELPATH, ino, filesize);
653 expect_open(ino, 0, 1);
654 expect_read(ino, 0, default_maxreadahead, default_maxreadahead,
657 fd = open(FULLPATH, O_RDONLY);
658 ASSERT_LE(0, fd) << strerror(errno);
660 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
661 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
663 /* A subsequent read should be serviced by cache */
664 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
665 ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
666 /* Deliberately leak fd. close(2) will be tested in release.cc */
669 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
670 TEST_F(ReadCacheable, sendfile)
672 const char FULLPATH[] = "mountpoint/some_file.txt";
673 const char RELPATH[] = "some_file.txt";
674 const char *CONTENTS = "abcdefgh";
677 ssize_t bufsize = strlen(CONTENTS);
682 expect_lookup(RELPATH, ino, bufsize);
683 expect_open(ino, 0, 1);
684 /* Like mmap, sendfile may request more data than is available */
685 EXPECT_CALL(*m_mock, process(
686 ResultOf([=](auto in) {
687 return (in->header.opcode == FUSE_READ &&
688 in->header.nodeid == ino &&
689 in->body.read.fh == Read::FH &&
690 in->body.read.offset == 0 &&
691 in->body.read.size >= bufsize);
694 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
695 out->header.len = sizeof(struct fuse_out_header) + bufsize;
696 memmove(out->body.bytes, CONTENTS, bufsize);
699 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
701 fd = open(FULLPATH, O_RDONLY);
702 ASSERT_LE(0, fd) << strerror(errno);
704 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
706 ASSERT_EQ(bufsize, read(sp[0], buf, bufsize)) << strerror(errno);
707 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
711 /* Deliberately leak fd. close(2) will be tested in release.cc */
714 /* sendfile should fail gracefully if fuse declines the read */
715 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
716 TEST_F(ReadCacheable, DISABLED_sendfile_eio)
718 const char FULLPATH[] = "mountpoint/some_file.txt";
719 const char RELPATH[] = "some_file.txt";
720 const char *CONTENTS = "abcdefgh";
723 ssize_t bufsize = strlen(CONTENTS);
727 expect_lookup(RELPATH, ino, bufsize);
728 expect_open(ino, 0, 1);
729 EXPECT_CALL(*m_mock, process(
730 ResultOf([=](auto in) {
731 return (in->header.opcode == FUSE_READ);
734 ).WillOnce(Invoke(ReturnErrno(EIO)));
736 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
738 fd = open(FULLPATH, O_RDONLY);
739 ASSERT_LE(0, fd) << strerror(errno);
741 ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
745 /* Deliberately leak fd. close(2) will be tested in release.cc */
748 /* fuse(4) should honor the filesystem's requested m_readahead parameter */
749 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236472 */
750 TEST_P(ReadAhead, DISABLED_readahead) {
751 const char FULLPATH[] = "mountpoint/some_file.txt";
752 const char RELPATH[] = "some_file.txt";
753 const char *CONTENTS0 = "abcdefghijklmnop";
757 ssize_t filesize = m_maxbcachebuf * 2;
761 ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf)
762 << "Test assumes that max_readahead < maxbcachebuf";
764 contents = (char*)calloc(1, filesize);
765 ASSERT_NE(NULL, contents);
766 memmove(contents, CONTENTS0, strlen(CONTENTS0));
768 expect_lookup(RELPATH, ino, filesize);
769 expect_open(ino, 0, 1);
770 /* fuse(4) should only read ahead the allowed amount */
771 expect_read(ino, 0, GetParam(), GetParam(), contents);
773 fd = open(FULLPATH, O_RDONLY);
774 ASSERT_LE(0, fd) << strerror(errno);
776 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
777 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
779 /* Deliberately leak fd. close(2) will be tested in release.cc */
782 INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 2048u));