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 AioRead: public Read {
59 virtual void SetUp() {
60 const char *node = "vfs.aio.enable_unsafe";
62 size_t size = sizeof(val);
66 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
70 "vfs.aio.enable_unsafe must be set for this test";
74 class AsyncRead: public AioRead {
75 virtual void SetUp() {
76 m_init_flags = FUSE_ASYNC_READ;
81 class ReadAhead: public Read, public WithParamInterface<uint32_t> {
82 virtual void SetUp() {
83 m_maxreadahead = GetParam();
88 /* AIO reads need to set the header's pid field correctly */
89 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
90 TEST_F(AioRead, aio_read)
92 const char FULLPATH[] = "mountpoint/some_file.txt";
93 const char RELPATH[] = "some_file.txt";
94 const char *CONTENTS = "abcdefgh";
97 ssize_t bufsize = strlen(CONTENTS);
99 struct aiocb iocb, *piocb;
101 expect_lookup(RELPATH, ino, bufsize);
102 expect_open(ino, 0, 1);
103 expect_getattr(ino, bufsize);
104 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
106 fd = open(FULLPATH, O_RDONLY);
107 ASSERT_LE(0, fd) << strerror(errno);
109 iocb.aio_nbytes = bufsize;
110 iocb.aio_fildes = fd;
113 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
114 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
115 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
116 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
117 /* Deliberately leak fd. close(2) will be tested in release.cc */
121 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
122 * is at most one outstanding read operation per file handle
124 TEST_F(AioRead, async_read_disabled)
126 const char FULLPATH[] = "mountpoint/some_file.txt";
127 const char RELPATH[] = "some_file.txt";
130 ssize_t bufsize = 50;
131 char buf0[bufsize], buf1[bufsize];
134 struct aiocb iocb0, iocb1;
136 expect_lookup(RELPATH, ino, bufsize);
137 expect_open(ino, 0, 1);
138 expect_getattr(ino, bufsize);
139 EXPECT_CALL(*m_mock, process(
140 ResultOf([=](auto in) {
141 return (in->header.opcode == FUSE_READ &&
142 in->header.nodeid == ino &&
143 in->body.read.fh == FH &&
144 in->body.read.offset == (uint64_t)off0 &&
145 in->body.read.size == bufsize);
148 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
149 /* Filesystem is slow to respond */
151 EXPECT_CALL(*m_mock, process(
152 ResultOf([=](auto in) {
153 return (in->header.opcode == FUSE_READ &&
154 in->header.nodeid == ino &&
155 in->body.read.fh == FH &&
156 in->body.read.offset == (uint64_t)off1 &&
157 in->body.read.size == bufsize);
162 fd = open(FULLPATH, O_RDONLY);
163 ASSERT_LE(0, fd) << strerror(errno);
166 * Submit two AIO read requests, and respond to neither. If the
167 * filesystem ever gets the second read request, then we failed to
168 * limit outstanding reads.
170 iocb0.aio_nbytes = bufsize;
171 iocb0.aio_fildes = fd;
172 iocb0.aio_buf = buf0;
173 iocb0.aio_offset = off0;
174 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
175 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
177 iocb1.aio_nbytes = bufsize;
178 iocb1.aio_fildes = fd;
179 iocb1.aio_buf = buf1;
180 iocb1.aio_offset = off1;
181 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
182 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
185 * Sleep for awhile to make sure the kernel has had a chance to issue
186 * the second read, even though the first has not yet returned
190 /* Deliberately leak iocbs */
191 /* Deliberately leak fd. close(2) will be tested in release.cc */
195 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
196 * simultaneous read requests on the same file handle.
199 * Disabled because we don't yet implement FUSE_ASYNC_READ. No bugzilla
200 * entry, because that's a feature request, not a bug.
202 TEST_F(AsyncRead, DISABLED_async_read)
204 const char FULLPATH[] = "mountpoint/some_file.txt";
205 const char RELPATH[] = "some_file.txt";
208 ssize_t bufsize = 50;
209 char buf0[bufsize], buf1[bufsize];
212 struct aiocb iocb0, iocb1;
214 expect_lookup(RELPATH, ino, bufsize);
215 expect_open(ino, 0, 1);
216 expect_getattr(ino, bufsize);
217 EXPECT_CALL(*m_mock, process(
218 ResultOf([=](auto in) {
219 return (in->header.opcode == FUSE_READ &&
220 in->header.nodeid == ino &&
221 in->body.read.fh == FH &&
222 in->body.read.offset == (uint64_t)off0 &&
223 in->body.read.size == bufsize);
226 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
227 /* Filesystem is slow to respond */
229 EXPECT_CALL(*m_mock, process(
230 ResultOf([=](auto in) {
231 return (in->header.opcode == FUSE_READ &&
232 in->header.nodeid == ino &&
233 in->body.read.fh == FH &&
234 in->body.read.offset == (uint64_t)off1 &&
235 in->body.read.size == bufsize);
238 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
239 /* Filesystem is slow to respond */
242 fd = open(FULLPATH, O_RDONLY);
243 ASSERT_LE(0, fd) << strerror(errno);
246 * Submit two AIO read requests, but respond to neither. Ensure that
249 iocb0.aio_nbytes = bufsize;
250 iocb0.aio_fildes = fd;
251 iocb0.aio_buf = buf0;
252 iocb0.aio_offset = off0;
253 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
254 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
256 iocb1.aio_nbytes = bufsize;
257 iocb1.aio_fildes = fd;
258 iocb1.aio_buf = buf1;
259 iocb1.aio_offset = off1;
260 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
261 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
264 * Sleep for awhile to make sure the kernel has had a chance to issue
269 /* Deliberately leak iocbs */
270 /* Deliberately leak fd. close(2) will be tested in release.cc */
273 /* 0-length reads shouldn't cause any confusion */
274 TEST_F(Read, direct_io_read_nothing)
276 const char FULLPATH[] = "mountpoint/some_file.txt";
277 const char RELPATH[] = "some_file.txt";
280 uint64_t offset = 100;
283 expect_lookup(RELPATH, ino, offset + 1000);
284 expect_open(ino, FOPEN_DIRECT_IO, 1);
285 expect_getattr(ino, offset + 1000);
287 fd = open(FULLPATH, O_RDONLY);
288 ASSERT_LE(0, fd) << strerror(errno);
290 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
291 /* Deliberately leak fd. close(2) will be tested in release.cc */
295 * With direct_io, reads should not fill the cache. They should go straight to
298 TEST_F(Read, direct_io_pread)
300 const char FULLPATH[] = "mountpoint/some_file.txt";
301 const char RELPATH[] = "some_file.txt";
302 const char *CONTENTS = "abcdefgh";
305 uint64_t offset = 100;
306 ssize_t bufsize = strlen(CONTENTS);
309 expect_lookup(RELPATH, ino, offset + bufsize);
310 expect_open(ino, FOPEN_DIRECT_IO, 1);
311 expect_getattr(ino, offset + bufsize);
312 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
314 fd = open(FULLPATH, O_RDONLY);
315 ASSERT_LE(0, fd) << strerror(errno);
317 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
318 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
319 /* Deliberately leak fd. close(2) will be tested in release.cc */
323 * With direct_io, filesystems are allowed to return less data than is
324 * requested. fuse(4) should return a short read to userland.
326 TEST_F(Read, direct_io_short_read)
328 const char FULLPATH[] = "mountpoint/some_file.txt";
329 const char RELPATH[] = "some_file.txt";
330 const char *CONTENTS = "abcdefghijklmnop";
333 uint64_t offset = 100;
334 ssize_t bufsize = strlen(CONTENTS);
335 ssize_t halfbufsize = bufsize / 2;
338 expect_lookup(RELPATH, ino, offset + bufsize);
339 expect_open(ino, FOPEN_DIRECT_IO, 1);
340 expect_getattr(ino, offset + bufsize);
341 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
343 fd = open(FULLPATH, O_RDONLY);
344 ASSERT_LE(0, fd) << strerror(errno);
346 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
348 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
349 /* Deliberately leak fd. close(2) will be tested in release.cc */
354 const char FULLPATH[] = "mountpoint/some_file.txt";
355 const char RELPATH[] = "some_file.txt";
356 const char *CONTENTS = "abcdefgh";
359 ssize_t bufsize = strlen(CONTENTS);
362 expect_lookup(RELPATH, ino, bufsize);
363 expect_open(ino, 0, 1);
364 expect_getattr(ino, bufsize);
365 EXPECT_CALL(*m_mock, process(
366 ResultOf([=](auto in) {
367 return (in->header.opcode == FUSE_READ);
370 ).WillOnce(Invoke(ReturnErrno(EIO)));
372 fd = open(FULLPATH, O_RDONLY);
373 ASSERT_LE(0, fd) << strerror(errno);
375 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
376 ASSERT_EQ(EIO, errno);
377 /* Deliberately leak fd. close(2) will be tested in release.cc */
381 * With the keep_cache option, the kernel may keep its read cache across
384 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236560 */
385 TEST_F(Read, DISABLED_keep_cache)
387 const char FULLPATH[] = "mountpoint/some_file.txt";
388 const char RELPATH[] = "some_file.txt";
389 const char *CONTENTS = "abcdefgh";
392 ssize_t bufsize = strlen(CONTENTS);
395 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
396 expect_open(ino, FOPEN_KEEP_CACHE, 1);
397 expect_getattr(ino, bufsize);
398 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
400 fd0 = open(FULLPATH, O_RDONLY);
401 ASSERT_LE(0, fd0) << strerror(errno);
402 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
404 fd1 = open(FULLPATH, O_RDONLY);
405 ASSERT_LE(0, fd1) << strerror(errno);
408 * This read should be serviced by cache, even though it's on the other
411 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
413 /* Deliberately leak fd0 and fd1. */
417 * Without the keep_cache option, the kernel should drop its read caches on
420 TEST_F(Read, keep_cache_disabled)
422 const char FULLPATH[] = "mountpoint/some_file.txt";
423 const char RELPATH[] = "some_file.txt";
424 const char *CONTENTS = "abcdefgh";
427 ssize_t bufsize = strlen(CONTENTS);
430 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
431 expect_open(ino, FOPEN_KEEP_CACHE, 1);
432 expect_getattr(ino, bufsize);
433 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
435 fd0 = open(FULLPATH, O_RDONLY);
436 ASSERT_LE(0, fd0) << strerror(errno);
437 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
439 fd1 = open(FULLPATH, O_RDONLY);
440 ASSERT_LE(0, fd1) << strerror(errno);
443 * This read should not be serviced by cache, even though it's on the
444 * original file descriptor
446 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
447 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
448 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
450 /* Deliberately leak fd0 and fd1. */
455 const char FULLPATH[] = "mountpoint/some_file.txt";
456 const char RELPATH[] = "some_file.txt";
457 const char *CONTENTS = "abcdefgh";
461 ssize_t bufsize = strlen(CONTENTS);
467 expect_lookup(RELPATH, ino, bufsize);
468 expect_open(ino, 0, 1);
469 expect_getattr(ino, bufsize);
470 /* mmap may legitimately try to read more data than is available */
471 EXPECT_CALL(*m_mock, process(
472 ResultOf([=](auto in) {
473 return (in->header.opcode == FUSE_READ &&
474 in->header.nodeid == ino &&
475 in->body.read.fh == Read::FH &&
476 in->body.read.offset == 0 &&
477 in->body.read.size >= bufsize);
480 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
481 out->header.len = sizeof(struct fuse_out_header) + bufsize;
482 memmove(out->body.bytes, CONTENTS, bufsize);
485 fd = open(FULLPATH, O_RDONLY);
486 ASSERT_LE(0, fd) << strerror(errno);
488 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
489 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
491 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
493 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
494 /* Deliberately leak fd. close(2) will be tested in release.cc */
498 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
499 * cache and to straight to the daemon
501 TEST_F(Read, o_direct)
503 const char FULLPATH[] = "mountpoint/some_file.txt";
504 const char RELPATH[] = "some_file.txt";
505 const char *CONTENTS = "abcdefgh";
508 ssize_t bufsize = strlen(CONTENTS);
511 expect_lookup(RELPATH, ino, bufsize);
512 expect_open(ino, 0, 1);
513 expect_getattr(ino, bufsize);
514 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
516 fd = open(FULLPATH, O_RDONLY);
517 ASSERT_LE(0, fd) << strerror(errno);
520 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
521 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
523 // Reads with o_direct should bypass the cache
524 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
525 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
526 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
527 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
528 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
530 /* Deliberately leak fd. close(2) will be tested in release.cc */
535 const char FULLPATH[] = "mountpoint/some_file.txt";
536 const char RELPATH[] = "some_file.txt";
537 const char *CONTENTS = "abcdefgh";
541 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
542 * to read from. Without this, the read might start at a lower offset.
544 uint64_t offset = m_maxbcachebuf;
545 ssize_t bufsize = strlen(CONTENTS);
548 expect_lookup(RELPATH, ino, offset + bufsize);
549 expect_open(ino, 0, 1);
550 expect_getattr(ino, offset + bufsize);
551 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
553 fd = open(FULLPATH, O_RDONLY);
554 ASSERT_LE(0, fd) << strerror(errno);
556 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
557 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
558 /* Deliberately leak fd. close(2) will be tested in release.cc */
563 const char FULLPATH[] = "mountpoint/some_file.txt";
564 const char RELPATH[] = "some_file.txt";
565 const char *CONTENTS = "abcdefgh";
568 ssize_t bufsize = strlen(CONTENTS);
571 expect_lookup(RELPATH, ino, bufsize);
572 expect_open(ino, 0, 1);
573 expect_getattr(ino, bufsize);
574 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
576 fd = open(FULLPATH, O_RDONLY);
577 ASSERT_LE(0, fd) << strerror(errno);
579 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
580 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
582 /* Deliberately leak fd. close(2) will be tested in release.cc */
585 /* If the filesystem allows it, the kernel should try to readahead */
586 TEST_F(Read, default_readahead)
588 const char FULLPATH[] = "mountpoint/some_file.txt";
589 const char RELPATH[] = "some_file.txt";
590 const char *CONTENTS0 = "abcdefghijklmnop";
594 /* hard-coded in fuse_internal.c */
595 size_t default_maxreadahead = 65536;
596 ssize_t filesize = default_maxreadahead * 2;
599 const char *contents1 = CONTENTS0 + bufsize;
601 contents = (char*)calloc(1, filesize);
602 ASSERT_NE(NULL, contents);
603 memmove(contents, CONTENTS0, strlen(CONTENTS0));
605 expect_lookup(RELPATH, ino, filesize);
606 expect_open(ino, 0, 1);
607 expect_getattr(ino, filesize);
608 expect_read(ino, 0, default_maxreadahead, default_maxreadahead,
611 fd = open(FULLPATH, O_RDONLY);
612 ASSERT_LE(0, fd) << strerror(errno);
614 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
615 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
617 /* A subsequent read should be serviced by cache */
618 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
619 ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
620 /* Deliberately leak fd. close(2) will be tested in release.cc */
623 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
624 TEST_F(Read, sendfile)
626 const char FULLPATH[] = "mountpoint/some_file.txt";
627 const char RELPATH[] = "some_file.txt";
628 const char *CONTENTS = "abcdefgh";
631 ssize_t bufsize = strlen(CONTENTS);
636 expect_lookup(RELPATH, ino, bufsize);
637 expect_open(ino, 0, 1);
638 expect_getattr(ino, bufsize);
639 /* Like mmap, sendfile may request more data than is available */
640 EXPECT_CALL(*m_mock, process(
641 ResultOf([=](auto in) {
642 return (in->header.opcode == FUSE_READ &&
643 in->header.nodeid == ino &&
644 in->body.read.fh == Read::FH &&
645 in->body.read.offset == 0 &&
646 in->body.read.size >= bufsize);
649 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
650 out->header.len = sizeof(struct fuse_out_header) + bufsize;
651 memmove(out->body.bytes, CONTENTS, bufsize);
654 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
656 fd = open(FULLPATH, O_RDONLY);
657 ASSERT_LE(0, fd) << strerror(errno);
659 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
661 ASSERT_EQ(bufsize, read(sp[0], buf, bufsize)) << strerror(errno);
662 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
666 /* Deliberately leak fd. close(2) will be tested in release.cc */
669 /* sendfile should fail gracefully if fuse declines the read */
670 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
671 TEST_F(Read, DISABLED_sendfile_eio)
673 const char FULLPATH[] = "mountpoint/some_file.txt";
674 const char RELPATH[] = "some_file.txt";
675 const char *CONTENTS = "abcdefgh";
678 ssize_t bufsize = strlen(CONTENTS);
682 expect_lookup(RELPATH, ino, bufsize);
683 expect_open(ino, 0, 1);
684 expect_getattr(ino, bufsize);
685 EXPECT_CALL(*m_mock, process(
686 ResultOf([=](auto in) {
687 return (in->header.opcode == FUSE_READ);
690 ).WillOnce(Invoke(ReturnErrno(EIO)));
692 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
694 fd = open(FULLPATH, O_RDONLY);
695 ASSERT_LE(0, fd) << strerror(errno);
697 ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
701 /* Deliberately leak fd. close(2) will be tested in release.cc */
704 /* fuse(4) should honor the filesystem's requested m_readahead parameter */
705 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236472 */
706 TEST_P(ReadAhead, DISABLED_readahead) {
707 const char FULLPATH[] = "mountpoint/some_file.txt";
708 const char RELPATH[] = "some_file.txt";
709 const char *CONTENTS0 = "abcdefghijklmnop";
713 ssize_t filesize = m_maxbcachebuf * 2;
717 ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf)
718 << "Test assumes that max_readahead < maxbcachebuf";
720 contents = (char*)calloc(1, filesize);
721 ASSERT_NE(NULL, contents);
722 memmove(contents, CONTENTS0, strlen(CONTENTS0));
724 expect_lookup(RELPATH, ino, filesize);
725 expect_open(ino, 0, 1);
726 expect_getattr(ino, filesize);
727 /* fuse(4) should only read ahead the allowed amount */
728 expect_read(ino, 0, GetParam(), GetParam(), contents);
730 fd = open(FULLPATH, O_RDONLY);
731 ASSERT_LE(0, fd) << strerror(errno);
733 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
734 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
736 /* Deliberately leak fd. close(2) will be tested in release.cc */
739 INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 2048u));