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 ReadCacheable: public Read {
83 virtual void SetUp() {
84 const char *node = "vfs.fusefs.data_cache_mode";
86 size_t size = sizeof(val);
90 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
94 "fusefs data caching must be enabled for this test";
98 class ReadAhead: public ReadCacheable, public WithParamInterface<uint32_t> {
99 virtual void SetUp() {
100 m_maxreadahead = GetParam();
105 /* AIO reads need to set the header's pid field correctly */
106 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
107 TEST_F(AioRead, aio_read)
109 const char FULLPATH[] = "mountpoint/some_file.txt";
110 const char RELPATH[] = "some_file.txt";
111 const char *CONTENTS = "abcdefgh";
114 ssize_t bufsize = strlen(CONTENTS);
116 struct aiocb iocb, *piocb;
118 expect_lookup(RELPATH, ino, bufsize);
119 expect_open(ino, 0, 1);
120 expect_getattr(ino, bufsize);
121 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
123 fd = open(FULLPATH, O_RDONLY);
124 ASSERT_LE(0, fd) << strerror(errno);
126 iocb.aio_nbytes = bufsize;
127 iocb.aio_fildes = fd;
130 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
131 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
132 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
133 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
134 /* Deliberately leak fd. close(2) will be tested in release.cc */
138 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
139 * is at most one outstanding read operation per file handle
141 TEST_F(AioRead, async_read_disabled)
143 const char FULLPATH[] = "mountpoint/some_file.txt";
144 const char RELPATH[] = "some_file.txt";
147 ssize_t bufsize = 50;
148 char buf0[bufsize], buf1[bufsize];
151 struct aiocb iocb0, iocb1;
153 expect_lookup(RELPATH, ino, bufsize);
154 expect_open(ino, 0, 1);
155 expect_getattr(ino, bufsize);
156 EXPECT_CALL(*m_mock, process(
157 ResultOf([=](auto in) {
158 return (in->header.opcode == FUSE_READ &&
159 in->header.nodeid == ino &&
160 in->body.read.fh == FH &&
161 in->body.read.offset == (uint64_t)off0 &&
162 in->body.read.size == bufsize);
165 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
166 /* Filesystem is slow to respond */
168 EXPECT_CALL(*m_mock, process(
169 ResultOf([=](auto in) {
170 return (in->header.opcode == FUSE_READ &&
171 in->header.nodeid == ino &&
172 in->body.read.fh == FH &&
173 in->body.read.offset == (uint64_t)off1 &&
174 in->body.read.size == bufsize);
179 fd = open(FULLPATH, O_RDONLY);
180 ASSERT_LE(0, fd) << strerror(errno);
183 * Submit two AIO read requests, and respond to neither. If the
184 * filesystem ever gets the second read request, then we failed to
185 * limit outstanding reads.
187 iocb0.aio_nbytes = bufsize;
188 iocb0.aio_fildes = fd;
189 iocb0.aio_buf = buf0;
190 iocb0.aio_offset = off0;
191 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
192 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
194 iocb1.aio_nbytes = bufsize;
195 iocb1.aio_fildes = fd;
196 iocb1.aio_buf = buf1;
197 iocb1.aio_offset = off1;
198 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
199 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
202 * Sleep for awhile to make sure the kernel has had a chance to issue
203 * the second read, even though the first has not yet returned
207 /* Deliberately leak iocbs */
208 /* Deliberately leak fd. close(2) will be tested in release.cc */
212 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
213 * simultaneous read requests on the same file handle.
216 * Disabled because we don't yet implement FUSE_ASYNC_READ. No bugzilla
217 * entry, because that's a feature request, not a bug.
219 TEST_F(AsyncRead, DISABLED_async_read)
221 const char FULLPATH[] = "mountpoint/some_file.txt";
222 const char RELPATH[] = "some_file.txt";
225 ssize_t bufsize = 50;
226 char buf0[bufsize], buf1[bufsize];
229 struct aiocb iocb0, iocb1;
231 expect_lookup(RELPATH, ino, bufsize);
232 expect_open(ino, 0, 1);
233 expect_getattr(ino, bufsize);
234 EXPECT_CALL(*m_mock, process(
235 ResultOf([=](auto in) {
236 return (in->header.opcode == FUSE_READ &&
237 in->header.nodeid == ino &&
238 in->body.read.fh == FH &&
239 in->body.read.offset == (uint64_t)off0 &&
240 in->body.read.size == bufsize);
243 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
244 /* Filesystem is slow to respond */
246 EXPECT_CALL(*m_mock, process(
247 ResultOf([=](auto in) {
248 return (in->header.opcode == FUSE_READ &&
249 in->header.nodeid == ino &&
250 in->body.read.fh == FH &&
251 in->body.read.offset == (uint64_t)off1 &&
252 in->body.read.size == bufsize);
255 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
256 /* Filesystem is slow to respond */
259 fd = open(FULLPATH, O_RDONLY);
260 ASSERT_LE(0, fd) << strerror(errno);
263 * Submit two AIO read requests, but respond to neither. Ensure that
266 iocb0.aio_nbytes = bufsize;
267 iocb0.aio_fildes = fd;
268 iocb0.aio_buf = buf0;
269 iocb0.aio_offset = off0;
270 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
271 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
273 iocb1.aio_nbytes = bufsize;
274 iocb1.aio_fildes = fd;
275 iocb1.aio_buf = buf1;
276 iocb1.aio_offset = off1;
277 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
278 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
281 * Sleep for awhile to make sure the kernel has had a chance to issue
286 /* Deliberately leak iocbs */
287 /* Deliberately leak fd. close(2) will be tested in release.cc */
290 /* 0-length reads shouldn't cause any confusion */
291 TEST_F(Read, direct_io_read_nothing)
293 const char FULLPATH[] = "mountpoint/some_file.txt";
294 const char RELPATH[] = "some_file.txt";
297 uint64_t offset = 100;
300 expect_lookup(RELPATH, ino, offset + 1000);
301 expect_open(ino, FOPEN_DIRECT_IO, 1);
302 expect_getattr(ino, offset + 1000);
304 fd = open(FULLPATH, O_RDONLY);
305 ASSERT_LE(0, fd) << strerror(errno);
307 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
308 /* Deliberately leak fd. close(2) will be tested in release.cc */
312 * With direct_io, reads should not fill the cache. They should go straight to
315 TEST_F(Read, direct_io_pread)
317 const char FULLPATH[] = "mountpoint/some_file.txt";
318 const char RELPATH[] = "some_file.txt";
319 const char *CONTENTS = "abcdefgh";
322 uint64_t offset = 100;
323 ssize_t bufsize = strlen(CONTENTS);
326 expect_lookup(RELPATH, ino, offset + bufsize);
327 expect_open(ino, FOPEN_DIRECT_IO, 1);
328 expect_getattr(ino, offset + bufsize);
329 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
331 fd = open(FULLPATH, O_RDONLY);
332 ASSERT_LE(0, fd) << strerror(errno);
334 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
335 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
336 /* Deliberately leak fd. close(2) will be tested in release.cc */
340 * With direct_io, filesystems are allowed to return less data than is
341 * requested. fuse(4) should return a short read to userland.
343 TEST_F(Read, direct_io_short_read)
345 const char FULLPATH[] = "mountpoint/some_file.txt";
346 const char RELPATH[] = "some_file.txt";
347 const char *CONTENTS = "abcdefghijklmnop";
350 uint64_t offset = 100;
351 ssize_t bufsize = strlen(CONTENTS);
352 ssize_t halfbufsize = bufsize / 2;
355 expect_lookup(RELPATH, ino, offset + bufsize);
356 expect_open(ino, FOPEN_DIRECT_IO, 1);
357 expect_getattr(ino, offset + bufsize);
358 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
360 fd = open(FULLPATH, O_RDONLY);
361 ASSERT_LE(0, fd) << strerror(errno);
363 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
365 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
366 /* Deliberately leak fd. close(2) will be tested in release.cc */
371 const char FULLPATH[] = "mountpoint/some_file.txt";
372 const char RELPATH[] = "some_file.txt";
373 const char *CONTENTS = "abcdefgh";
376 ssize_t bufsize = strlen(CONTENTS);
379 expect_lookup(RELPATH, ino, bufsize);
380 expect_open(ino, 0, 1);
381 expect_getattr(ino, bufsize);
382 EXPECT_CALL(*m_mock, process(
383 ResultOf([=](auto in) {
384 return (in->header.opcode == FUSE_READ);
387 ).WillOnce(Invoke(ReturnErrno(EIO)));
389 fd = open(FULLPATH, O_RDONLY);
390 ASSERT_LE(0, fd) << strerror(errno);
392 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
393 ASSERT_EQ(EIO, errno);
394 /* Deliberately leak fd. close(2) will be tested in release.cc */
398 * With the keep_cache option, the kernel may keep its read cache across
401 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236560 */
402 TEST_F(Read, DISABLED_keep_cache)
404 const char FULLPATH[] = "mountpoint/some_file.txt";
405 const char RELPATH[] = "some_file.txt";
406 const char *CONTENTS = "abcdefgh";
409 ssize_t bufsize = strlen(CONTENTS);
412 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
413 expect_open(ino, FOPEN_KEEP_CACHE, 1);
414 expect_getattr(ino, bufsize);
415 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
417 fd0 = open(FULLPATH, O_RDONLY);
418 ASSERT_LE(0, fd0) << strerror(errno);
419 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
421 fd1 = open(FULLPATH, O_RDONLY);
422 ASSERT_LE(0, fd1) << strerror(errno);
425 * This read should be serviced by cache, even though it's on the other
428 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
430 /* Deliberately leak fd0 and fd1. */
434 * Without the keep_cache option, the kernel should drop its read caches on
437 TEST_F(Read, keep_cache_disabled)
439 const char FULLPATH[] = "mountpoint/some_file.txt";
440 const char RELPATH[] = "some_file.txt";
441 const char *CONTENTS = "abcdefgh";
444 ssize_t bufsize = strlen(CONTENTS);
447 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
448 expect_open(ino, FOPEN_KEEP_CACHE, 1);
449 expect_getattr(ino, bufsize);
450 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
452 fd0 = open(FULLPATH, O_RDONLY);
453 ASSERT_LE(0, fd0) << strerror(errno);
454 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
456 fd1 = open(FULLPATH, O_RDONLY);
457 ASSERT_LE(0, fd1) << strerror(errno);
460 * This read should not be serviced by cache, even though it's on the
461 * original file descriptor
463 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
464 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
465 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
467 /* Deliberately leak fd0 and fd1. */
470 TEST_F(ReadCacheable, mmap)
472 const char FULLPATH[] = "mountpoint/some_file.txt";
473 const char RELPATH[] = "some_file.txt";
474 const char *CONTENTS = "abcdefgh";
478 ssize_t bufsize = strlen(CONTENTS);
484 expect_lookup(RELPATH, ino, bufsize);
485 expect_open(ino, 0, 1);
486 expect_getattr(ino, bufsize);
487 /* mmap may legitimately try to read more data than is available */
488 EXPECT_CALL(*m_mock, process(
489 ResultOf([=](auto in) {
490 return (in->header.opcode == FUSE_READ &&
491 in->header.nodeid == ino &&
492 in->body.read.fh == Read::FH &&
493 in->body.read.offset == 0 &&
494 in->body.read.size >= bufsize);
497 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
498 out->header.len = sizeof(struct fuse_out_header) + bufsize;
499 memmove(out->body.bytes, CONTENTS, bufsize);
502 fd = open(FULLPATH, O_RDONLY);
503 ASSERT_LE(0, fd) << strerror(errno);
505 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
506 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
508 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
510 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
511 /* Deliberately leak fd. close(2) will be tested in release.cc */
515 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
516 * cache and to straight to the daemon
518 TEST_F(Read, o_direct)
520 const char FULLPATH[] = "mountpoint/some_file.txt";
521 const char RELPATH[] = "some_file.txt";
522 const char *CONTENTS = "abcdefgh";
525 ssize_t bufsize = strlen(CONTENTS);
528 expect_lookup(RELPATH, ino, bufsize);
529 expect_open(ino, 0, 1);
530 expect_getattr(ino, bufsize);
531 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
533 fd = open(FULLPATH, O_RDONLY);
534 ASSERT_LE(0, fd) << strerror(errno);
537 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
538 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
540 // Reads with o_direct should bypass the cache
541 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
542 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
543 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
544 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
545 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
547 /* Deliberately leak fd. close(2) will be tested in release.cc */
552 const char FULLPATH[] = "mountpoint/some_file.txt";
553 const char RELPATH[] = "some_file.txt";
554 const char *CONTENTS = "abcdefgh";
558 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
559 * to read from. Without this, the read might start at a lower offset.
561 uint64_t offset = m_maxbcachebuf;
562 ssize_t bufsize = strlen(CONTENTS);
565 expect_lookup(RELPATH, ino, offset + bufsize);
566 expect_open(ino, 0, 1);
567 expect_getattr(ino, offset + bufsize);
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_getattr(ino, bufsize);
591 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
593 fd = open(FULLPATH, O_RDONLY);
594 ASSERT_LE(0, fd) << strerror(errno);
596 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
597 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
599 /* Deliberately leak fd. close(2) will be tested in release.cc */
602 /* If the filesystem allows it, the kernel should try to readahead */
603 TEST_F(ReadCacheable, default_readahead)
605 const char FULLPATH[] = "mountpoint/some_file.txt";
606 const char RELPATH[] = "some_file.txt";
607 const char *CONTENTS0 = "abcdefghijklmnop";
611 /* hard-coded in fuse_internal.c */
612 size_t default_maxreadahead = 65536;
613 ssize_t filesize = default_maxreadahead * 2;
616 const char *contents1 = CONTENTS0 + bufsize;
618 contents = (char*)calloc(1, filesize);
619 ASSERT_NE(NULL, contents);
620 memmove(contents, CONTENTS0, strlen(CONTENTS0));
622 expect_lookup(RELPATH, ino, filesize);
623 expect_open(ino, 0, 1);
624 expect_getattr(ino, filesize);
625 expect_read(ino, 0, default_maxreadahead, default_maxreadahead,
628 fd = open(FULLPATH, O_RDONLY);
629 ASSERT_LE(0, fd) << strerror(errno);
631 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
632 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
634 /* A subsequent read should be serviced by cache */
635 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
636 ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
637 /* Deliberately leak fd. close(2) will be tested in release.cc */
640 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
641 TEST_F(ReadCacheable, sendfile)
643 const char FULLPATH[] = "mountpoint/some_file.txt";
644 const char RELPATH[] = "some_file.txt";
645 const char *CONTENTS = "abcdefgh";
648 ssize_t bufsize = strlen(CONTENTS);
653 expect_lookup(RELPATH, ino, bufsize);
654 expect_open(ino, 0, 1);
655 expect_getattr(ino, bufsize);
656 /* Like mmap, sendfile may request more data than is available */
657 EXPECT_CALL(*m_mock, process(
658 ResultOf([=](auto in) {
659 return (in->header.opcode == FUSE_READ &&
660 in->header.nodeid == ino &&
661 in->body.read.fh == Read::FH &&
662 in->body.read.offset == 0 &&
663 in->body.read.size >= bufsize);
666 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
667 out->header.len = sizeof(struct fuse_out_header) + bufsize;
668 memmove(out->body.bytes, CONTENTS, bufsize);
671 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
673 fd = open(FULLPATH, O_RDONLY);
674 ASSERT_LE(0, fd) << strerror(errno);
676 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
678 ASSERT_EQ(bufsize, read(sp[0], buf, bufsize)) << strerror(errno);
679 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
683 /* Deliberately leak fd. close(2) will be tested in release.cc */
686 /* sendfile should fail gracefully if fuse declines the read */
687 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
688 TEST_F(ReadCacheable, DISABLED_sendfile_eio)
690 const char FULLPATH[] = "mountpoint/some_file.txt";
691 const char RELPATH[] = "some_file.txt";
692 const char *CONTENTS = "abcdefgh";
695 ssize_t bufsize = strlen(CONTENTS);
699 expect_lookup(RELPATH, ino, bufsize);
700 expect_open(ino, 0, 1);
701 expect_getattr(ino, bufsize);
702 EXPECT_CALL(*m_mock, process(
703 ResultOf([=](auto in) {
704 return (in->header.opcode == FUSE_READ);
707 ).WillOnce(Invoke(ReturnErrno(EIO)));
709 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
711 fd = open(FULLPATH, O_RDONLY);
712 ASSERT_LE(0, fd) << strerror(errno);
714 ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
718 /* Deliberately leak fd. close(2) will be tested in release.cc */
721 /* fuse(4) should honor the filesystem's requested m_readahead parameter */
722 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236472 */
723 TEST_P(ReadAhead, DISABLED_readahead) {
724 const char FULLPATH[] = "mountpoint/some_file.txt";
725 const char RELPATH[] = "some_file.txt";
726 const char *CONTENTS0 = "abcdefghijklmnop";
730 ssize_t filesize = m_maxbcachebuf * 2;
734 ASSERT_TRUE(GetParam() < (uint32_t)m_maxbcachebuf)
735 << "Test assumes that max_readahead < maxbcachebuf";
737 contents = (char*)calloc(1, filesize);
738 ASSERT_NE(NULL, contents);
739 memmove(contents, CONTENTS0, strlen(CONTENTS0));
741 expect_lookup(RELPATH, ino, filesize);
742 expect_open(ino, 0, 1);
743 expect_getattr(ino, filesize);
744 /* fuse(4) should only read ahead the allowed amount */
745 expect_read(ino, 0, GetParam(), GetParam(), contents);
747 fd = open(FULLPATH, O_RDONLY);
748 ASSERT_LE(0, fd) << strerror(errno);
750 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
751 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
753 /* Deliberately leak fd. close(2) will be tested in release.cc */
756 INSTANTIATE_TEST_CASE_P(RA, ReadAhead, ::testing::Values(0u, 2048u));