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);
64 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
66 // TODO: With GoogleTest 1.8.2, use SKIP instead
68 FAIL() << "vfs.aio.enable_unsafe must be set for this test";
73 class AsyncRead: public AioRead {
74 virtual void SetUp() {
75 m_init_flags = FUSE_ASYNC_READ;
80 class ReadAhead: public Read, public WithParamInterface<uint32_t> {
81 virtual void SetUp() {
82 m_maxreadahead = GetParam();
87 /* AIO reads need to set the header's pid field correctly */
88 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
89 TEST_F(AioRead, aio_read)
91 const char FULLPATH[] = "mountpoint/some_file.txt";
92 const char RELPATH[] = "some_file.txt";
93 const char *CONTENTS = "abcdefgh";
96 ssize_t bufsize = strlen(CONTENTS);
98 struct aiocb iocb, *piocb;
100 expect_lookup(RELPATH, ino, bufsize);
101 expect_open(ino, 0, 1);
102 expect_getattr(ino, bufsize);
103 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
105 fd = open(FULLPATH, O_RDONLY);
106 ASSERT_LE(0, fd) << strerror(errno);
108 iocb.aio_nbytes = bufsize;
109 iocb.aio_fildes = fd;
112 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
113 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
114 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
115 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
116 /* Deliberately leak fd. close(2) will be tested in release.cc */
120 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
121 * is at most one outstanding read operation per file handle
123 TEST_F(AioRead, async_read_disabled)
125 const char FULLPATH[] = "mountpoint/some_file.txt";
126 const char RELPATH[] = "some_file.txt";
129 ssize_t bufsize = 50;
130 char buf0[bufsize], buf1[bufsize];
133 struct aiocb iocb0, iocb1;
135 expect_lookup(RELPATH, ino, bufsize);
136 expect_open(ino, 0, 1);
137 expect_getattr(ino, bufsize);
138 EXPECT_CALL(*m_mock, process(
139 ResultOf([=](auto in) {
140 return (in->header.opcode == FUSE_READ &&
141 in->header.nodeid == ino &&
142 in->body.read.fh == FH &&
143 in->body.read.offset == (uint64_t)off0 &&
144 in->body.read.size == bufsize);
147 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
148 /* Filesystem is slow to respond */
150 EXPECT_CALL(*m_mock, process(
151 ResultOf([=](auto in) {
152 return (in->header.opcode == FUSE_READ &&
153 in->header.nodeid == ino &&
154 in->body.read.fh == FH &&
155 in->body.read.offset == (uint64_t)off1 &&
156 in->body.read.size == bufsize);
161 fd = open(FULLPATH, O_RDONLY);
162 ASSERT_LE(0, fd) << strerror(errno);
165 * Submit two AIO read requests, and respond to neither. If the
166 * filesystem ever gets the second read request, then we failed to
167 * limit outstanding reads.
169 iocb0.aio_nbytes = bufsize;
170 iocb0.aio_fildes = fd;
171 iocb0.aio_buf = buf0;
172 iocb0.aio_offset = off0;
173 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
174 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
176 iocb1.aio_nbytes = bufsize;
177 iocb1.aio_fildes = fd;
178 iocb1.aio_buf = buf1;
179 iocb1.aio_offset = off1;
180 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
181 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
184 * Sleep for awhile to make sure the kernel has had a chance to issue
185 * the second read, even though the first has not yet returned
189 /* Deliberately leak iocbs */
190 /* Deliberately leak fd. close(2) will be tested in release.cc */
194 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
195 * simultaneous read requests on the same file handle.
198 * Disabled because we don't yet implement FUSE_ASYNC_READ. No bugzilla
199 * entry, because that's a feature request, not a bug.
201 TEST_F(AsyncRead, DISABLED_async_read)
203 const char FULLPATH[] = "mountpoint/some_file.txt";
204 const char RELPATH[] = "some_file.txt";
207 ssize_t bufsize = 50;
208 char buf0[bufsize], buf1[bufsize];
211 struct aiocb iocb0, iocb1;
213 expect_lookup(RELPATH, ino, bufsize);
214 expect_open(ino, 0, 1);
215 expect_getattr(ino, bufsize);
216 EXPECT_CALL(*m_mock, process(
217 ResultOf([=](auto in) {
218 return (in->header.opcode == FUSE_READ &&
219 in->header.nodeid == ino &&
220 in->body.read.fh == FH &&
221 in->body.read.offset == (uint64_t)off0 &&
222 in->body.read.size == bufsize);
225 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
226 /* Filesystem is slow to respond */
228 EXPECT_CALL(*m_mock, process(
229 ResultOf([=](auto in) {
230 return (in->header.opcode == FUSE_READ &&
231 in->header.nodeid == ino &&
232 in->body.read.fh == FH &&
233 in->body.read.offset == (uint64_t)off1 &&
234 in->body.read.size == bufsize);
237 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
238 /* Filesystem is slow to respond */
241 fd = open(FULLPATH, O_RDONLY);
242 ASSERT_LE(0, fd) << strerror(errno);
245 * Submit two AIO read requests, but respond to neither. Ensure that
248 iocb0.aio_nbytes = bufsize;
249 iocb0.aio_fildes = fd;
250 iocb0.aio_buf = buf0;
251 iocb0.aio_offset = off0;
252 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
253 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
255 iocb1.aio_nbytes = bufsize;
256 iocb1.aio_fildes = fd;
257 iocb1.aio_buf = buf1;
258 iocb1.aio_offset = off1;
259 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
260 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
263 * Sleep for awhile to make sure the kernel has had a chance to issue
268 /* Deliberately leak iocbs */
269 /* Deliberately leak fd. close(2) will be tested in release.cc */
272 /* 0-length reads shouldn't cause any confusion */
273 TEST_F(Read, direct_io_read_nothing)
275 const char FULLPATH[] = "mountpoint/some_file.txt";
276 const char RELPATH[] = "some_file.txt";
279 uint64_t offset = 100;
282 expect_lookup(RELPATH, ino, offset + 1000);
283 expect_open(ino, FOPEN_DIRECT_IO, 1);
284 expect_getattr(ino, offset + 1000);
286 fd = open(FULLPATH, O_RDONLY);
287 ASSERT_LE(0, fd) << strerror(errno);
289 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
290 /* Deliberately leak fd. close(2) will be tested in release.cc */
294 * With direct_io, reads should not fill the cache. They should go straight to
297 TEST_F(Read, direct_io_pread)
299 const char FULLPATH[] = "mountpoint/some_file.txt";
300 const char RELPATH[] = "some_file.txt";
301 const char *CONTENTS = "abcdefgh";
304 uint64_t offset = 100;
305 ssize_t bufsize = strlen(CONTENTS);
308 expect_lookup(RELPATH, ino, offset + bufsize);
309 expect_open(ino, FOPEN_DIRECT_IO, 1);
310 expect_getattr(ino, offset + bufsize);
311 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
313 fd = open(FULLPATH, O_RDONLY);
314 ASSERT_LE(0, fd) << strerror(errno);
316 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
317 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
318 /* Deliberately leak fd. close(2) will be tested in release.cc */
322 * With direct_io, filesystems are allowed to return less data than is
323 * requested. fuse(4) should return a short read to userland.
325 TEST_F(Read, direct_io_short_read)
327 const char FULLPATH[] = "mountpoint/some_file.txt";
328 const char RELPATH[] = "some_file.txt";
329 const char *CONTENTS = "abcdefghijklmnop";
332 uint64_t offset = 100;
333 ssize_t bufsize = strlen(CONTENTS);
334 ssize_t halfbufsize = bufsize / 2;
337 expect_lookup(RELPATH, ino, offset + bufsize);
338 expect_open(ino, FOPEN_DIRECT_IO, 1);
339 expect_getattr(ino, offset + bufsize);
340 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
342 fd = open(FULLPATH, O_RDONLY);
343 ASSERT_LE(0, fd) << strerror(errno);
345 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
347 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
348 /* Deliberately leak fd. close(2) will be tested in release.cc */
353 const char FULLPATH[] = "mountpoint/some_file.txt";
354 const char RELPATH[] = "some_file.txt";
355 const char *CONTENTS = "abcdefgh";
358 ssize_t bufsize = strlen(CONTENTS);
361 expect_lookup(RELPATH, ino, bufsize);
362 expect_open(ino, 0, 1);
363 expect_getattr(ino, bufsize);
364 EXPECT_CALL(*m_mock, process(
365 ResultOf([=](auto in) {
366 return (in->header.opcode == FUSE_READ);
369 ).WillOnce(Invoke(ReturnErrno(EIO)));
371 fd = open(FULLPATH, O_RDONLY);
372 ASSERT_LE(0, fd) << strerror(errno);
374 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
375 ASSERT_EQ(EIO, errno);
376 /* Deliberately leak fd. close(2) will be tested in release.cc */
380 * With the keep_cache option, the kernel may keep its read cache across
383 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236560 */
384 TEST_F(Read, DISABLED_keep_cache)
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 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
395 expect_open(ino, FOPEN_KEEP_CACHE, 1);
396 expect_getattr(ino, bufsize);
397 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
399 fd0 = open(FULLPATH, O_RDONLY);
400 ASSERT_LE(0, fd0) << strerror(errno);
401 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
403 fd1 = open(FULLPATH, O_RDONLY);
404 ASSERT_LE(0, fd1) << strerror(errno);
407 * This read should be serviced by cache, even though it's on the other
410 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
412 /* Deliberately leak fd0 and fd1. */
416 * Without the keep_cache option, the kernel should drop its read caches on
419 TEST_F(Read, keep_cache_disabled)
421 const char FULLPATH[] = "mountpoint/some_file.txt";
422 const char RELPATH[] = "some_file.txt";
423 const char *CONTENTS = "abcdefgh";
426 ssize_t bufsize = strlen(CONTENTS);
429 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
430 expect_open(ino, FOPEN_KEEP_CACHE, 1);
431 expect_getattr(ino, bufsize);
432 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
434 fd0 = open(FULLPATH, O_RDONLY);
435 ASSERT_LE(0, fd0) << strerror(errno);
436 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
438 fd1 = open(FULLPATH, O_RDONLY);
439 ASSERT_LE(0, fd1) << strerror(errno);
442 * This read should not be serviced by cache, even though it's on the
443 * original file descriptor
445 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
446 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
447 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
449 /* Deliberately leak fd0 and fd1. */
454 const char FULLPATH[] = "mountpoint/some_file.txt";
455 const char RELPATH[] = "some_file.txt";
456 const char *CONTENTS = "abcdefgh";
460 ssize_t bufsize = strlen(CONTENTS);
466 expect_lookup(RELPATH, ino, bufsize);
467 expect_open(ino, 0, 1);
468 expect_getattr(ino, bufsize);
469 /* mmap may legitimately try to read more data than is available */
470 EXPECT_CALL(*m_mock, process(
471 ResultOf([=](auto in) {
472 return (in->header.opcode == FUSE_READ &&
473 in->header.nodeid == ino &&
474 in->body.read.fh == Read::FH &&
475 in->body.read.offset == 0 &&
476 in->body.read.size >= bufsize);
479 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
480 out->header.len = sizeof(struct fuse_out_header) + bufsize;
481 memmove(out->body.bytes, CONTENTS, bufsize);
484 fd = open(FULLPATH, O_RDONLY);
485 ASSERT_LE(0, fd) << strerror(errno);
487 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
488 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
490 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
492 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
493 /* Deliberately leak fd. close(2) will be tested in release.cc */
497 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
498 * cache and to straight to the daemon
500 TEST_F(Read, o_direct)
502 const char FULLPATH[] = "mountpoint/some_file.txt";
503 const char RELPATH[] = "some_file.txt";
504 const char *CONTENTS = "abcdefgh";
507 ssize_t bufsize = strlen(CONTENTS);
510 expect_lookup(RELPATH, ino, bufsize);
511 expect_open(ino, 0, 1);
512 expect_getattr(ino, bufsize);
513 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
515 fd = open(FULLPATH, O_RDONLY);
516 ASSERT_LE(0, fd) << strerror(errno);
519 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
520 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
522 // Reads with o_direct should bypass the cache
523 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
524 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
525 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
526 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
527 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
529 /* Deliberately leak fd. close(2) will be tested in release.cc */
534 const char FULLPATH[] = "mountpoint/some_file.txt";
535 const char RELPATH[] = "some_file.txt";
536 const char *CONTENTS = "abcdefgh";
540 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
541 * to read from. Without this, the read might start at a lower offset.
543 uint64_t offset = m_maxbcachebuf;
544 ssize_t bufsize = strlen(CONTENTS);
547 expect_lookup(RELPATH, ino, offset + bufsize);
548 expect_open(ino, 0, 1);
549 expect_getattr(ino, offset + bufsize);
550 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
552 fd = open(FULLPATH, O_RDONLY);
553 ASSERT_LE(0, fd) << strerror(errno);
555 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
556 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
557 /* Deliberately leak fd. close(2) will be tested in release.cc */
562 const char FULLPATH[] = "mountpoint/some_file.txt";
563 const char RELPATH[] = "some_file.txt";
564 const char *CONTENTS = "abcdefgh";
567 ssize_t bufsize = strlen(CONTENTS);
570 expect_lookup(RELPATH, ino, bufsize);
571 expect_open(ino, 0, 1);
572 expect_getattr(ino, bufsize);
573 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
575 fd = open(FULLPATH, O_RDONLY);
576 ASSERT_LE(0, fd) << strerror(errno);
578 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
579 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
581 /* Deliberately leak fd. close(2) will be tested in release.cc */
584 /* If the filesystem allows it, the kernel should try to readahead */
585 TEST_F(Read, default_readahead)
587 const char FULLPATH[] = "mountpoint/some_file.txt";
588 const char RELPATH[] = "some_file.txt";
589 const char *CONTENTS0 = "abcdefghijklmnop";
593 /* hard-coded in fuse_internal.c */
594 size_t default_maxreadahead = 65536;
595 ssize_t filesize = default_maxreadahead * 2;
598 const char *contents1 = CONTENTS0 + bufsize;
600 contents = (char*)calloc(1, filesize);
601 ASSERT_NE(NULL, contents);
602 memmove(contents, CONTENTS0, strlen(CONTENTS0));
604 expect_lookup(RELPATH, ino, filesize);
605 expect_open(ino, 0, 1);
606 expect_getattr(ino, filesize);
607 expect_read(ino, 0, default_maxreadahead, default_maxreadahead,
610 fd = open(FULLPATH, O_RDONLY);
611 ASSERT_LE(0, fd) << strerror(errno);
613 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
614 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
616 /* A subsequent read should be serviced by cache */
617 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
618 ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
619 /* Deliberately leak fd. close(2) will be tested in release.cc */
622 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
623 TEST_F(Read, sendfile)
625 const char FULLPATH[] = "mountpoint/some_file.txt";
626 const char RELPATH[] = "some_file.txt";
627 const char *CONTENTS = "abcdefgh";
630 ssize_t bufsize = strlen(CONTENTS);
635 expect_lookup(RELPATH, ino, bufsize);
636 expect_open(ino, 0, 1);
637 expect_getattr(ino, bufsize);
638 /* Like mmap, sendfile may request more data than is available */
639 EXPECT_CALL(*m_mock, process(
640 ResultOf([=](auto in) {
641 return (in->header.opcode == FUSE_READ &&
642 in->header.nodeid == ino &&
643 in->body.read.fh == Read::FH &&
644 in->body.read.offset == 0 &&
645 in->body.read.size >= bufsize);
648 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
649 out->header.len = sizeof(struct fuse_out_header) + bufsize;
650 memmove(out->body.bytes, CONTENTS, bufsize);
653 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
655 fd = open(FULLPATH, O_RDONLY);
656 ASSERT_LE(0, fd) << strerror(errno);
658 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
660 ASSERT_EQ(bufsize, read(sp[0], buf, bufsize)) << strerror(errno);
661 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
665 /* Deliberately leak fd. close(2) will be tested in release.cc */
668 /* sendfile should fail gracefully if fuse declines the read */
669 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
670 TEST_F(Read, DISABLED_sendfile_eio)
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);
681 expect_lookup(RELPATH, ino, bufsize);
682 expect_open(ino, 0, 1);
683 expect_getattr(ino, bufsize);
684 EXPECT_CALL(*m_mock, process(
685 ResultOf([=](auto in) {
686 return (in->header.opcode == FUSE_READ);
689 ).WillOnce(Invoke(ReturnErrno(EIO)));
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_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
700 /* Deliberately leak fd. close(2) will be tested in release.cc */
703 /* fuse(4) should honor the filesystem's requested m_readahead parameter */
704 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236472 */
705 TEST_P(ReadAhead, DISABLED_readahead) {
706 const char FULLPATH[] = "mountpoint/some_file.txt";
707 const char RELPATH[] = "some_file.txt";
708 const char *CONTENTS0 = "abcdefghijklmnop";
712 ssize_t filesize = m_maxbcachebuf * 2;
716 ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf)
717 << "Test assumes that max_readahead < maxbcachebuf";
719 contents = (char*)calloc(1, filesize);
720 ASSERT_NE(NULL, contents);
721 memmove(contents, CONTENTS0, strlen(CONTENTS0));
723 expect_lookup(RELPATH, ino, filesize);
724 expect_open(ino, 0, 1);
725 expect_getattr(ino, filesize);
726 /* fuse(4) should only read ahead the allowed amount */
727 expect_read(ino, 0, GetParam(), GetParam(), contents);
729 fd = open(FULLPATH, O_RDONLY);
730 ASSERT_LE(0, fd) << strerror(errno);
732 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
733 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
735 /* Deliberately leak fd. close(2) will be tested in release.cc */
738 INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 2048u));