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_read(ino, 0, bufsize, bufsize, CONTENTS);
122 fd = open(FULLPATH, O_RDONLY);
123 ASSERT_LE(0, fd) << strerror(errno);
125 iocb.aio_nbytes = bufsize;
126 iocb.aio_fildes = fd;
129 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
130 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
131 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
132 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
133 /* Deliberately leak fd. close(2) will be tested in release.cc */
137 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
138 * is at most one outstanding read operation per file handle
140 TEST_F(AioRead, async_read_disabled)
142 const char FULLPATH[] = "mountpoint/some_file.txt";
143 const char RELPATH[] = "some_file.txt";
146 ssize_t bufsize = 50;
147 char buf0[bufsize], buf1[bufsize];
150 struct aiocb iocb0, iocb1;
152 expect_lookup(RELPATH, ino, bufsize);
153 expect_open(ino, 0, 1);
154 EXPECT_CALL(*m_mock, process(
155 ResultOf([=](auto in) {
156 return (in->header.opcode == FUSE_READ &&
157 in->header.nodeid == ino &&
158 in->body.read.fh == FH &&
159 in->body.read.offset == (uint64_t)off0 &&
160 in->body.read.size == bufsize);
163 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
164 /* Filesystem is slow to respond */
166 EXPECT_CALL(*m_mock, process(
167 ResultOf([=](auto in) {
168 return (in->header.opcode == FUSE_READ &&
169 in->header.nodeid == ino &&
170 in->body.read.fh == FH &&
171 in->body.read.offset == (uint64_t)off1 &&
172 in->body.read.size == bufsize);
177 fd = open(FULLPATH, O_RDONLY);
178 ASSERT_LE(0, fd) << strerror(errno);
181 * Submit two AIO read requests, and respond to neither. If the
182 * filesystem ever gets the second read request, then we failed to
183 * limit outstanding reads.
185 iocb0.aio_nbytes = bufsize;
186 iocb0.aio_fildes = fd;
187 iocb0.aio_buf = buf0;
188 iocb0.aio_offset = off0;
189 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
190 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
192 iocb1.aio_nbytes = bufsize;
193 iocb1.aio_fildes = fd;
194 iocb1.aio_buf = buf1;
195 iocb1.aio_offset = off1;
196 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
197 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
200 * Sleep for awhile to make sure the kernel has had a chance to issue
201 * the second read, even though the first has not yet returned
205 /* Deliberately leak iocbs */
206 /* Deliberately leak fd. close(2) will be tested in release.cc */
210 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
211 * simultaneous read requests on the same file handle.
214 * Disabled because we don't yet implement FUSE_ASYNC_READ. No bugzilla
215 * entry, because that's a feature request, not a bug.
217 TEST_F(AsyncRead, DISABLED_async_read)
219 const char FULLPATH[] = "mountpoint/some_file.txt";
220 const char RELPATH[] = "some_file.txt";
223 ssize_t bufsize = 50;
224 char buf0[bufsize], buf1[bufsize];
227 struct aiocb iocb0, iocb1;
229 expect_lookup(RELPATH, ino, bufsize);
230 expect_open(ino, 0, 1);
231 EXPECT_CALL(*m_mock, process(
232 ResultOf([=](auto in) {
233 return (in->header.opcode == FUSE_READ &&
234 in->header.nodeid == ino &&
235 in->body.read.fh == FH &&
236 in->body.read.offset == (uint64_t)off0 &&
237 in->body.read.size == bufsize);
240 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
241 /* Filesystem is slow to respond */
243 EXPECT_CALL(*m_mock, process(
244 ResultOf([=](auto in) {
245 return (in->header.opcode == FUSE_READ &&
246 in->header.nodeid == ino &&
247 in->body.read.fh == FH &&
248 in->body.read.offset == (uint64_t)off1 &&
249 in->body.read.size == bufsize);
252 ).WillOnce(Invoke([](auto in __unused, auto &out __unused) {
253 /* Filesystem is slow to respond */
256 fd = open(FULLPATH, O_RDONLY);
257 ASSERT_LE(0, fd) << strerror(errno);
260 * Submit two AIO read requests, but respond to neither. Ensure that
263 iocb0.aio_nbytes = bufsize;
264 iocb0.aio_fildes = fd;
265 iocb0.aio_buf = buf0;
266 iocb0.aio_offset = off0;
267 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
268 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
270 iocb1.aio_nbytes = bufsize;
271 iocb1.aio_fildes = fd;
272 iocb1.aio_buf = buf1;
273 iocb1.aio_offset = off1;
274 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
275 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
278 * Sleep for awhile to make sure the kernel has had a chance to issue
283 /* Deliberately leak iocbs */
284 /* Deliberately leak fd. close(2) will be tested in release.cc */
287 /* 0-length reads shouldn't cause any confusion */
288 TEST_F(Read, direct_io_read_nothing)
290 const char FULLPATH[] = "mountpoint/some_file.txt";
291 const char RELPATH[] = "some_file.txt";
294 uint64_t offset = 100;
297 expect_lookup(RELPATH, ino, offset + 1000);
298 expect_open(ino, FOPEN_DIRECT_IO, 1);
300 fd = open(FULLPATH, O_RDONLY);
301 ASSERT_LE(0, fd) << strerror(errno);
303 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
304 /* Deliberately leak fd. close(2) will be tested in release.cc */
308 * With direct_io, reads should not fill the cache. They should go straight to
311 TEST_F(Read, direct_io_pread)
313 const char FULLPATH[] = "mountpoint/some_file.txt";
314 const char RELPATH[] = "some_file.txt";
315 const char *CONTENTS = "abcdefgh";
318 uint64_t offset = 100;
319 ssize_t bufsize = strlen(CONTENTS);
322 expect_lookup(RELPATH, ino, offset + bufsize);
323 expect_open(ino, FOPEN_DIRECT_IO, 1);
324 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
326 fd = open(FULLPATH, O_RDONLY);
327 ASSERT_LE(0, fd) << strerror(errno);
329 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
330 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
331 /* Deliberately leak fd. close(2) will be tested in release.cc */
335 * With direct_io, filesystems are allowed to return less data than is
336 * requested. fuse(4) should return a short read to userland.
338 TEST_F(Read, direct_io_short_read)
340 const char FULLPATH[] = "mountpoint/some_file.txt";
341 const char RELPATH[] = "some_file.txt";
342 const char *CONTENTS = "abcdefghijklmnop";
345 uint64_t offset = 100;
346 ssize_t bufsize = strlen(CONTENTS);
347 ssize_t halfbufsize = bufsize / 2;
350 expect_lookup(RELPATH, ino, offset + bufsize);
351 expect_open(ino, FOPEN_DIRECT_IO, 1);
352 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
354 fd = open(FULLPATH, O_RDONLY);
355 ASSERT_LE(0, fd) << strerror(errno);
357 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
359 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
360 /* Deliberately leak fd. close(2) will be tested in release.cc */
365 const char FULLPATH[] = "mountpoint/some_file.txt";
366 const char RELPATH[] = "some_file.txt";
367 const char *CONTENTS = "abcdefgh";
370 ssize_t bufsize = strlen(CONTENTS);
373 expect_lookup(RELPATH, ino, bufsize);
374 expect_open(ino, 0, 1);
375 EXPECT_CALL(*m_mock, process(
376 ResultOf([=](auto in) {
377 return (in->header.opcode == FUSE_READ);
380 ).WillOnce(Invoke(ReturnErrno(EIO)));
382 fd = open(FULLPATH, O_RDONLY);
383 ASSERT_LE(0, fd) << strerror(errno);
385 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
386 ASSERT_EQ(EIO, errno);
387 /* Deliberately leak fd. close(2) will be tested in release.cc */
391 * With the keep_cache option, the kernel may keep its read cache across
394 TEST_F(ReadCacheable, keep_cache)
396 const char FULLPATH[] = "mountpoint/some_file.txt";
397 const char RELPATH[] = "some_file.txt";
398 const char *CONTENTS = "abcdefgh";
401 ssize_t bufsize = strlen(CONTENTS);
404 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
405 expect_open(ino, FOPEN_KEEP_CACHE, 2);
406 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
408 fd0 = open(FULLPATH, O_RDONLY);
409 ASSERT_LE(0, fd0) << strerror(errno);
410 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
412 fd1 = open(FULLPATH, O_RDWR);
413 ASSERT_LE(0, fd1) << strerror(errno);
416 * This read should be serviced by cache, even though it's on the other
419 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
421 /* Deliberately leak fd0 and fd1. */
425 * Without the keep_cache option, the kernel should drop its read caches on
428 TEST_F(Read, keep_cache_disabled)
430 const char FULLPATH[] = "mountpoint/some_file.txt";
431 const char RELPATH[] = "some_file.txt";
432 const char *CONTENTS = "abcdefgh";
435 ssize_t bufsize = strlen(CONTENTS);
438 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
439 expect_open(ino, 0, 2);
440 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
442 fd0 = open(FULLPATH, O_RDONLY);
443 ASSERT_LE(0, fd0) << strerror(errno);
444 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
446 fd1 = open(FULLPATH, O_RDWR);
447 ASSERT_LE(0, fd1) << strerror(errno);
450 * This read should not be serviced by cache, even though it's on the
451 * original file descriptor
453 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
454 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
455 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
457 /* Deliberately leak fd0 and fd1. */
460 TEST_F(ReadCacheable, mmap)
462 const char FULLPATH[] = "mountpoint/some_file.txt";
463 const char RELPATH[] = "some_file.txt";
464 const char *CONTENTS = "abcdefgh";
468 ssize_t bufsize = strlen(CONTENTS);
474 expect_lookup(RELPATH, ino, bufsize);
475 expect_open(ino, 0, 1);
476 /* mmap may legitimately try to read more data than is available */
477 EXPECT_CALL(*m_mock, process(
478 ResultOf([=](auto in) {
479 return (in->header.opcode == FUSE_READ &&
480 in->header.nodeid == ino &&
481 in->body.read.fh == Read::FH &&
482 in->body.read.offset == 0 &&
483 in->body.read.size >= bufsize);
486 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
487 out->header.len = sizeof(struct fuse_out_header) + bufsize;
488 memmove(out->body.bytes, CONTENTS, bufsize);
491 fd = open(FULLPATH, O_RDONLY);
492 ASSERT_LE(0, fd) << strerror(errno);
494 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
495 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
497 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
499 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
500 /* Deliberately leak fd. close(2) will be tested in release.cc */
504 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
505 * cache and to straight to the daemon
507 TEST_F(Read, o_direct)
509 const char FULLPATH[] = "mountpoint/some_file.txt";
510 const char RELPATH[] = "some_file.txt";
511 const char *CONTENTS = "abcdefgh";
514 ssize_t bufsize = strlen(CONTENTS);
517 expect_lookup(RELPATH, ino, bufsize);
518 expect_open(ino, 0, 1);
519 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
521 fd = open(FULLPATH, O_RDONLY);
522 ASSERT_LE(0, fd) << strerror(errno);
525 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
526 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
528 // Reads with o_direct should bypass the cache
529 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
530 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
531 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
532 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
533 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
535 /* Deliberately leak fd. close(2) will be tested in release.cc */
540 const char FULLPATH[] = "mountpoint/some_file.txt";
541 const char RELPATH[] = "some_file.txt";
542 const char *CONTENTS = "abcdefgh";
546 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
547 * to read from. Without this, the read might start at a lower offset.
549 uint64_t offset = m_maxbcachebuf;
550 ssize_t bufsize = strlen(CONTENTS);
553 expect_lookup(RELPATH, ino, offset + bufsize);
554 expect_open(ino, 0, 1);
555 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
557 fd = open(FULLPATH, O_RDONLY);
558 ASSERT_LE(0, fd) << strerror(errno);
560 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
561 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
562 /* Deliberately leak fd. close(2) will be tested in release.cc */
567 const char FULLPATH[] = "mountpoint/some_file.txt";
568 const char RELPATH[] = "some_file.txt";
569 const char *CONTENTS = "abcdefgh";
572 ssize_t bufsize = strlen(CONTENTS);
575 expect_lookup(RELPATH, ino, bufsize);
576 expect_open(ino, 0, 1);
577 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
579 fd = open(FULLPATH, O_RDONLY);
580 ASSERT_LE(0, fd) << strerror(errno);
582 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
583 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
585 /* Deliberately leak fd. close(2) will be tested in release.cc */
588 /* If the filesystem allows it, the kernel should try to readahead */
589 TEST_F(ReadCacheable, default_readahead)
591 const char FULLPATH[] = "mountpoint/some_file.txt";
592 const char RELPATH[] = "some_file.txt";
593 const char *CONTENTS0 = "abcdefghijklmnop";
597 /* hard-coded in fuse_internal.c */
598 size_t default_maxreadahead = 65536;
599 ssize_t filesize = default_maxreadahead * 2;
602 const char *contents1 = CONTENTS0 + bufsize;
604 contents = (char*)calloc(1, filesize);
605 ASSERT_NE(NULL, contents);
606 memmove(contents, CONTENTS0, strlen(CONTENTS0));
608 expect_lookup(RELPATH, ino, filesize);
609 expect_open(ino, 0, 1);
610 expect_read(ino, 0, default_maxreadahead, default_maxreadahead,
613 fd = open(FULLPATH, O_RDONLY);
614 ASSERT_LE(0, fd) << strerror(errno);
616 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
617 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
619 /* A subsequent read should be serviced by cache */
620 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
621 ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
622 /* Deliberately leak fd. close(2) will be tested in release.cc */
625 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
626 TEST_F(ReadCacheable, sendfile)
628 const char FULLPATH[] = "mountpoint/some_file.txt";
629 const char RELPATH[] = "some_file.txt";
630 const char *CONTENTS = "abcdefgh";
633 ssize_t bufsize = strlen(CONTENTS);
638 expect_lookup(RELPATH, ino, bufsize);
639 expect_open(ino, 0, 1);
640 /* Like mmap, sendfile may request more data than is available */
641 EXPECT_CALL(*m_mock, process(
642 ResultOf([=](auto in) {
643 return (in->header.opcode == FUSE_READ &&
644 in->header.nodeid == ino &&
645 in->body.read.fh == Read::FH &&
646 in->body.read.offset == 0 &&
647 in->body.read.size >= bufsize);
650 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) {
651 out->header.len = sizeof(struct fuse_out_header) + bufsize;
652 memmove(out->body.bytes, CONTENTS, bufsize);
655 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
657 fd = open(FULLPATH, O_RDONLY);
658 ASSERT_LE(0, fd) << strerror(errno);
660 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
662 ASSERT_EQ(bufsize, read(sp[0], buf, bufsize)) << strerror(errno);
663 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
667 /* Deliberately leak fd. close(2) will be tested in release.cc */
670 /* sendfile should fail gracefully if fuse declines the read */
671 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
672 TEST_F(ReadCacheable, DISABLED_sendfile_eio)
674 const char FULLPATH[] = "mountpoint/some_file.txt";
675 const char RELPATH[] = "some_file.txt";
676 const char *CONTENTS = "abcdefgh";
679 ssize_t bufsize = strlen(CONTENTS);
683 expect_lookup(RELPATH, ino, bufsize);
684 expect_open(ino, 0, 1);
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 /* 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));