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>
46 using namespace testing;
48 class Read: public FuseTest {
51 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
53 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
57 class Read_7_8: public FuseTest {
59 virtual void SetUp() {
60 m_kernel_minor_version = 8;
64 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
66 FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
70 class AioRead: public Read {
72 virtual void SetUp() {
73 const char *node = "vfs.aio.enable_unsafe";
75 size_t size = sizeof(val);
79 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
83 "vfs.aio.enable_unsafe must be set for this test";
87 class AsyncRead: public AioRead {
88 virtual void SetUp() {
89 m_init_flags = FUSE_ASYNC_READ;
94 class ReadCacheable: public Read {
96 virtual void SetUp() {
97 const char *node = "vfs.fusefs.data_cache_mode";
99 size_t size = sizeof(val);
103 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
107 "fusefs data caching must be enabled for this test";
111 class ReadAhead: public ReadCacheable, public WithParamInterface<uint32_t> {
112 virtual void SetUp() {
113 m_maxreadahead = GetParam();
118 /* AIO reads need to set the header's pid field correctly */
119 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
120 TEST_F(AioRead, aio_read)
122 const char FULLPATH[] = "mountpoint/some_file.txt";
123 const char RELPATH[] = "some_file.txt";
124 const char *CONTENTS = "abcdefgh";
127 ssize_t bufsize = strlen(CONTENTS);
129 struct aiocb iocb, *piocb;
131 expect_lookup(RELPATH, ino, bufsize);
132 expect_open(ino, 0, 1);
133 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
135 fd = open(FULLPATH, O_RDONLY);
136 ASSERT_LE(0, fd) << strerror(errno);
138 iocb.aio_nbytes = bufsize;
139 iocb.aio_fildes = fd;
142 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
143 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
144 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
145 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
146 /* Deliberately leak fd. close(2) will be tested in release.cc */
150 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
151 * is at most one outstanding read operation per file handle
153 TEST_F(AioRead, async_read_disabled)
155 const char FULLPATH[] = "mountpoint/some_file.txt";
156 const char RELPATH[] = "some_file.txt";
159 ssize_t bufsize = 50;
160 char buf0[bufsize], buf1[bufsize];
163 struct aiocb iocb0, iocb1;
165 expect_lookup(RELPATH, ino, bufsize);
166 expect_open(ino, 0, 1);
167 EXPECT_CALL(*m_mock, process(
168 ResultOf([=](auto in) {
169 return (in->header.opcode == FUSE_READ &&
170 in->header.nodeid == ino &&
171 in->body.read.fh == FH &&
172 in->body.read.offset == (uint64_t)off0 &&
173 in->body.read.size == bufsize);
176 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
177 /* Filesystem is slow to respond */
179 EXPECT_CALL(*m_mock, process(
180 ResultOf([=](auto in) {
181 return (in->header.opcode == FUSE_READ &&
182 in->header.nodeid == ino &&
183 in->body.read.fh == FH &&
184 in->body.read.offset == (uint64_t)off1 &&
185 in->body.read.size == bufsize);
190 fd = open(FULLPATH, O_RDONLY);
191 ASSERT_LE(0, fd) << strerror(errno);
194 * Submit two AIO read requests, and respond to neither. If the
195 * filesystem ever gets the second read request, then we failed to
196 * limit outstanding reads.
198 iocb0.aio_nbytes = bufsize;
199 iocb0.aio_fildes = fd;
200 iocb0.aio_buf = buf0;
201 iocb0.aio_offset = off0;
202 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
203 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
205 iocb1.aio_nbytes = bufsize;
206 iocb1.aio_fildes = fd;
207 iocb1.aio_buf = buf1;
208 iocb1.aio_offset = off1;
209 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
210 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
213 * Sleep for awhile to make sure the kernel has had a chance to issue
214 * the second read, even though the first has not yet returned
218 /* Deliberately leak iocbs */
219 /* Deliberately leak fd. close(2) will be tested in release.cc */
223 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
224 * simultaneous read requests on the same file handle.
227 * Disabled because we don't yet implement FUSE_ASYNC_READ. No bugzilla
228 * entry, because that's a feature request, not a bug.
230 TEST_F(AsyncRead, DISABLED_async_read)
232 const char FULLPATH[] = "mountpoint/some_file.txt";
233 const char RELPATH[] = "some_file.txt";
236 ssize_t bufsize = 50;
237 char buf0[bufsize], buf1[bufsize];
240 struct aiocb iocb0, iocb1;
242 expect_lookup(RELPATH, ino, bufsize);
243 expect_open(ino, 0, 1);
244 EXPECT_CALL(*m_mock, process(
245 ResultOf([=](auto in) {
246 return (in->header.opcode == FUSE_READ &&
247 in->header.nodeid == ino &&
248 in->body.read.fh == FH &&
249 in->body.read.offset == (uint64_t)off0 &&
250 in->body.read.size == bufsize);
253 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
254 /* Filesystem is slow to respond */
256 EXPECT_CALL(*m_mock, process(
257 ResultOf([=](auto in) {
258 return (in->header.opcode == FUSE_READ &&
259 in->header.nodeid == ino &&
260 in->body.read.fh == FH &&
261 in->body.read.offset == (uint64_t)off1 &&
262 in->body.read.size == bufsize);
265 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
266 /* Filesystem is slow to respond */
269 fd = open(FULLPATH, O_RDONLY);
270 ASSERT_LE(0, fd) << strerror(errno);
273 * Submit two AIO read requests, but respond to neither. Ensure that
276 iocb0.aio_nbytes = bufsize;
277 iocb0.aio_fildes = fd;
278 iocb0.aio_buf = buf0;
279 iocb0.aio_offset = off0;
280 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
281 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
283 iocb1.aio_nbytes = bufsize;
284 iocb1.aio_fildes = fd;
285 iocb1.aio_buf = buf1;
286 iocb1.aio_offset = off1;
287 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
288 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
291 * Sleep for awhile to make sure the kernel has had a chance to issue
296 /* Deliberately leak iocbs */
297 /* Deliberately leak fd. close(2) will be tested in release.cc */
300 /* 0-length reads shouldn't cause any confusion */
301 TEST_F(Read, direct_io_read_nothing)
303 const char FULLPATH[] = "mountpoint/some_file.txt";
304 const char RELPATH[] = "some_file.txt";
307 uint64_t offset = 100;
310 expect_lookup(RELPATH, ino, offset + 1000);
311 expect_open(ino, FOPEN_DIRECT_IO, 1);
313 fd = open(FULLPATH, O_RDONLY);
314 ASSERT_LE(0, fd) << strerror(errno);
316 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
317 /* Deliberately leak fd. close(2) will be tested in release.cc */
321 * With direct_io, reads should not fill the cache. They should go straight to
324 TEST_F(Read, direct_io_pread)
326 const char FULLPATH[] = "mountpoint/some_file.txt";
327 const char RELPATH[] = "some_file.txt";
328 const char *CONTENTS = "abcdefgh";
331 uint64_t offset = 100;
332 ssize_t bufsize = strlen(CONTENTS);
335 expect_lookup(RELPATH, ino, offset + bufsize);
336 expect_open(ino, FOPEN_DIRECT_IO, 1);
337 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
339 fd = open(FULLPATH, O_RDONLY);
340 ASSERT_LE(0, fd) << strerror(errno);
342 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
343 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
344 /* Deliberately leak fd. close(2) will be tested in release.cc */
348 * With direct_io, filesystems are allowed to return less data than is
349 * requested. fuse(4) should return a short read to userland.
351 TEST_F(Read, direct_io_short_read)
353 const char FULLPATH[] = "mountpoint/some_file.txt";
354 const char RELPATH[] = "some_file.txt";
355 const char *CONTENTS = "abcdefghijklmnop";
358 uint64_t offset = 100;
359 ssize_t bufsize = strlen(CONTENTS);
360 ssize_t halfbufsize = bufsize / 2;
363 expect_lookup(RELPATH, ino, offset + bufsize);
364 expect_open(ino, FOPEN_DIRECT_IO, 1);
365 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
367 fd = open(FULLPATH, O_RDONLY);
368 ASSERT_LE(0, fd) << strerror(errno);
370 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
372 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
373 /* Deliberately leak fd. close(2) will be tested in release.cc */
378 const char FULLPATH[] = "mountpoint/some_file.txt";
379 const char RELPATH[] = "some_file.txt";
380 const char *CONTENTS = "abcdefgh";
383 ssize_t bufsize = strlen(CONTENTS);
386 expect_lookup(RELPATH, ino, bufsize);
387 expect_open(ino, 0, 1);
388 EXPECT_CALL(*m_mock, process(
389 ResultOf([=](auto in) {
390 return (in->header.opcode == FUSE_READ);
393 ).WillOnce(Invoke(ReturnErrno(EIO)));
395 fd = open(FULLPATH, O_RDONLY);
396 ASSERT_LE(0, fd) << strerror(errno);
398 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
399 ASSERT_EQ(EIO, errno);
400 /* Deliberately leak fd. close(2) will be tested in release.cc */
404 * With the keep_cache option, the kernel may keep its read cache across
407 TEST_F(ReadCacheable, keep_cache)
409 const char FULLPATH[] = "mountpoint/some_file.txt";
410 const char RELPATH[] = "some_file.txt";
411 const char *CONTENTS = "abcdefgh";
414 ssize_t bufsize = strlen(CONTENTS);
417 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
418 expect_open(ino, FOPEN_KEEP_CACHE, 2);
419 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
421 fd0 = open(FULLPATH, O_RDONLY);
422 ASSERT_LE(0, fd0) << strerror(errno);
423 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
425 fd1 = open(FULLPATH, O_RDWR);
426 ASSERT_LE(0, fd1) << strerror(errno);
429 * This read should be serviced by cache, even though it's on the other
432 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
434 /* Deliberately leak fd0 and fd1. */
438 * Without the keep_cache option, the kernel should drop its read caches on
441 TEST_F(Read, keep_cache_disabled)
443 const char FULLPATH[] = "mountpoint/some_file.txt";
444 const char RELPATH[] = "some_file.txt";
445 const char *CONTENTS = "abcdefgh";
448 ssize_t bufsize = strlen(CONTENTS);
451 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
452 expect_open(ino, 0, 2);
453 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
455 fd0 = open(FULLPATH, O_RDONLY);
456 ASSERT_LE(0, fd0) << strerror(errno);
457 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
459 fd1 = open(FULLPATH, O_RDWR);
460 ASSERT_LE(0, fd1) << strerror(errno);
463 * This read should not be serviced by cache, even though it's on the
464 * original file descriptor
466 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
467 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
468 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
470 /* Deliberately leak fd0 and fd1. */
473 TEST_F(ReadCacheable, mmap)
475 const char FULLPATH[] = "mountpoint/some_file.txt";
476 const char RELPATH[] = "some_file.txt";
477 const char *CONTENTS = "abcdefgh";
481 ssize_t bufsize = strlen(CONTENTS);
487 expect_lookup(RELPATH, ino, bufsize);
488 expect_open(ino, 0, 1);
489 /* mmap may legitimately try to read more data than is available */
490 EXPECT_CALL(*m_mock, process(
491 ResultOf([=](auto in) {
492 return (in->header.opcode == FUSE_READ &&
493 in->header.nodeid == ino &&
494 in->body.read.fh == Read::FH &&
495 in->body.read.offset == 0 &&
496 in->body.read.size >= bufsize);
499 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
500 out->header.len = sizeof(struct fuse_out_header) + bufsize;
501 memmove(out->body.bytes, CONTENTS, bufsize);
504 fd = open(FULLPATH, O_RDONLY);
505 ASSERT_LE(0, fd) << strerror(errno);
507 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
508 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
510 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
512 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
513 /* Deliberately leak fd. close(2) will be tested in release.cc */
517 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
518 * cache and to straight to the daemon
520 TEST_F(Read, o_direct)
522 const char FULLPATH[] = "mountpoint/some_file.txt";
523 const char RELPATH[] = "some_file.txt";
524 const char *CONTENTS = "abcdefgh";
527 ssize_t bufsize = strlen(CONTENTS);
530 expect_lookup(RELPATH, ino, bufsize);
531 expect_open(ino, 0, 1);
532 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
534 fd = open(FULLPATH, O_RDONLY);
535 ASSERT_LE(0, fd) << strerror(errno);
538 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
539 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
541 // Reads with o_direct should bypass the cache
542 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
543 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
544 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
545 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
546 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
548 /* Deliberately leak fd. close(2) will be tested in release.cc */
553 const char FULLPATH[] = "mountpoint/some_file.txt";
554 const char RELPATH[] = "some_file.txt";
555 const char *CONTENTS = "abcdefgh";
559 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
560 * to read from. Without this, the read might start at a lower offset.
562 uint64_t offset = m_maxbcachebuf;
563 ssize_t bufsize = strlen(CONTENTS);
566 expect_lookup(RELPATH, ino, offset + bufsize);
567 expect_open(ino, 0, 1);
568 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
570 fd = open(FULLPATH, O_RDONLY);
571 ASSERT_LE(0, fd) << strerror(errno);
573 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
574 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
575 /* Deliberately leak fd. close(2) will be tested in release.cc */
580 const char FULLPATH[] = "mountpoint/some_file.txt";
581 const char RELPATH[] = "some_file.txt";
582 const char *CONTENTS = "abcdefgh";
585 ssize_t bufsize = strlen(CONTENTS);
588 expect_lookup(RELPATH, ino, bufsize);
589 expect_open(ino, 0, 1);
590 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
592 fd = open(FULLPATH, O_RDONLY);
593 ASSERT_LE(0, fd) << strerror(errno);
595 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
596 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
598 /* Deliberately leak fd. close(2) will be tested in release.cc */
601 TEST_F(Read_7_8, read)
603 const char FULLPATH[] = "mountpoint/some_file.txt";
604 const char RELPATH[] = "some_file.txt";
605 const char *CONTENTS = "abcdefgh";
608 ssize_t bufsize = strlen(CONTENTS);
611 expect_lookup(RELPATH, ino, bufsize);
612 expect_open(ino, 0, 1);
613 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
615 fd = open(FULLPATH, O_RDONLY);
616 ASSERT_LE(0, fd) << strerror(errno);
618 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
619 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
621 /* Deliberately leak fd. close(2) will be tested in release.cc */
624 /* If the filesystem allows it, the kernel should try to readahead */
625 TEST_F(ReadCacheable, default_readahead)
627 const char FULLPATH[] = "mountpoint/some_file.txt";
628 const char RELPATH[] = "some_file.txt";
629 const char *CONTENTS0 = "abcdefghijklmnop";
633 /* hard-coded in fuse_internal.c */
634 size_t default_maxreadahead = 65536;
635 ssize_t filesize = default_maxreadahead * 2;
638 const char *contents1 = CONTENTS0 + bufsize;
640 contents = (char*)calloc(1, filesize);
641 ASSERT_NE(NULL, contents);
642 memmove(contents, CONTENTS0, strlen(CONTENTS0));
644 expect_lookup(RELPATH, ino, filesize);
645 expect_open(ino, 0, 1);
646 expect_read(ino, 0, default_maxreadahead, default_maxreadahead,
649 fd = open(FULLPATH, O_RDONLY);
650 ASSERT_LE(0, fd) << strerror(errno);
652 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
653 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
655 /* A subsequent read should be serviced by cache */
656 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
657 ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
658 /* Deliberately leak fd. close(2) will be tested in release.cc */
661 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
662 TEST_F(ReadCacheable, sendfile)
664 const char FULLPATH[] = "mountpoint/some_file.txt";
665 const char RELPATH[] = "some_file.txt";
666 const char *CONTENTS = "abcdefgh";
669 ssize_t bufsize = strlen(CONTENTS);
674 expect_lookup(RELPATH, ino, bufsize);
675 expect_open(ino, 0, 1);
676 /* Like mmap, sendfile may request more data than is available */
677 EXPECT_CALL(*m_mock, process(
678 ResultOf([=](auto in) {
679 return (in->header.opcode == FUSE_READ &&
680 in->header.nodeid == ino &&
681 in->body.read.fh == Read::FH &&
682 in->body.read.offset == 0 &&
683 in->body.read.size >= bufsize);
686 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
687 out->header.len = sizeof(struct fuse_out_header) + bufsize;
688 memmove(out->body.bytes, CONTENTS, bufsize);
691 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
693 fd = open(FULLPATH, O_RDONLY);
694 ASSERT_LE(0, fd) << strerror(errno);
696 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
698 ASSERT_EQ(bufsize, read(sp[0], buf, bufsize)) << strerror(errno);
699 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
703 /* Deliberately leak fd. close(2) will be tested in release.cc */
706 /* sendfile should fail gracefully if fuse declines the read */
707 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
708 TEST_F(ReadCacheable, DISABLED_sendfile_eio)
710 const char FULLPATH[] = "mountpoint/some_file.txt";
711 const char RELPATH[] = "some_file.txt";
712 const char *CONTENTS = "abcdefgh";
715 ssize_t bufsize = strlen(CONTENTS);
719 expect_lookup(RELPATH, ino, bufsize);
720 expect_open(ino, 0, 1);
721 EXPECT_CALL(*m_mock, process(
722 ResultOf([=](auto in) {
723 return (in->header.opcode == FUSE_READ);
726 ).WillOnce(Invoke(ReturnErrno(EIO)));
728 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
730 fd = open(FULLPATH, O_RDONLY);
731 ASSERT_LE(0, fd) << strerror(errno);
733 ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
737 /* Deliberately leak fd. close(2) will be tested in release.cc */
740 /* fuse(4) should honor the filesystem's requested m_readahead parameter */
741 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236472 */
742 TEST_P(ReadAhead, DISABLED_readahead) {
743 const char FULLPATH[] = "mountpoint/some_file.txt";
744 const char RELPATH[] = "some_file.txt";
745 const char *CONTENTS0 = "abcdefghijklmnop";
749 ssize_t filesize = m_maxbcachebuf * 2;
753 ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf)
754 << "Test assumes that max_readahead < maxbcachebuf";
756 contents = (char*)calloc(1, filesize);
757 ASSERT_NE(NULL, contents);
758 memmove(contents, CONTENTS0, strlen(CONTENTS0));
760 expect_lookup(RELPATH, ino, filesize);
761 expect_open(ino, 0, 1);
762 /* fuse(4) should only read ahead the allowed amount */
763 expect_read(ino, 0, GetParam(), GetParam(), contents);
765 fd = open(FULLPATH, O_RDONLY);
766 ASSERT_LE(0, fd) << strerror(errno);
768 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
769 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
771 /* Deliberately leak fd. close(2) will be tested in release.cc */
774 INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 2048u));