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
34 #include <sys/param.h>
36 #include <sys/socket.h>
37 #include <sys/sysctl.h>
42 #include <semaphore.h>
51 using namespace testing;
53 class Read: public FuseTest {
56 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
58 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
62 class Read_7_8: public FuseTest {
64 virtual void SetUp() {
65 m_kernel_minor_version = 8;
69 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
71 FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
75 class AioRead: public Read {
77 virtual void SetUp() {
78 if (!is_unsafe_aio_enabled())
80 "vfs.aio.enable_unsafe must be set for this test";
85 class AsyncRead: public AioRead {
86 virtual void SetUp() {
87 m_init_flags = FUSE_ASYNC_READ;
92 class ReadAhead: public Read,
93 public WithParamInterface<tuple<bool, int>>
95 virtual void SetUp() {
97 const char *node = "vfs.maxbcachebuf";
98 size_t size = sizeof(val);
99 ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
102 m_maxreadahead = val * get<1>(GetParam());
103 m_noclusterr = get<0>(GetParam());
108 class ReadNoatime: public Read {
109 virtual void SetUp() {
115 class ReadSigbus: public Read
118 static jmp_buf s_jmpbuf;
119 static void *s_si_addr;
124 bzero(&sa, sizeof(sa));
125 sa.sa_handler = SIG_DFL;
126 sigaction(SIGBUS, &sa, NULL);
128 FuseTest::TearDown();
134 handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) {
135 ReadSigbus::s_si_addr = info->si_addr;
136 longjmp(ReadSigbus::s_jmpbuf, 1);
139 jmp_buf ReadSigbus::s_jmpbuf;
140 void *ReadSigbus::s_si_addr;
142 class TimeGran: public Read, public WithParamInterface<unsigned> {
144 virtual void SetUp() {
145 m_time_gran = 1 << GetParam();
150 /* AIO reads need to set the header's pid field correctly */
151 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
152 TEST_F(AioRead, aio_read)
154 const char FULLPATH[] = "mountpoint/some_file.txt";
155 const char RELPATH[] = "some_file.txt";
156 const char *CONTENTS = "abcdefgh";
159 ssize_t bufsize = strlen(CONTENTS);
160 uint8_t buf[bufsize];
161 struct aiocb iocb, *piocb;
163 expect_lookup(RELPATH, ino, bufsize);
164 expect_open(ino, 0, 1);
165 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
167 fd = open(FULLPATH, O_RDONLY);
168 ASSERT_LE(0, fd) << strerror(errno);
170 iocb.aio_nbytes = bufsize;
171 iocb.aio_fildes = fd;
174 iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
175 ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
176 ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
177 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
183 * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
184 * is at most one outstanding read operation per file handle
186 TEST_F(AioRead, async_read_disabled)
188 const char FULLPATH[] = "mountpoint/some_file.txt";
189 const char RELPATH[] = "some_file.txt";
192 ssize_t bufsize = 50;
193 char buf0[bufsize], buf1[bufsize];
195 off_t off1 = m_maxbcachebuf;
196 struct aiocb iocb0, iocb1;
197 volatile sig_atomic_t read_count = 0;
199 expect_lookup(RELPATH, ino, 131072);
200 expect_open(ino, 0, 1);
201 EXPECT_CALL(*m_mock, process(
202 ResultOf([=](auto in) {
203 return (in.header.opcode == FUSE_READ &&
204 in.header.nodeid == ino &&
205 in.body.read.fh == FH &&
206 in.body.read.offset == (uint64_t)off0);
209 ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
211 /* Filesystem is slow to respond */
213 EXPECT_CALL(*m_mock, process(
214 ResultOf([=](auto in) {
215 return (in.header.opcode == FUSE_READ &&
216 in.header.nodeid == ino &&
217 in.body.read.fh == FH &&
218 in.body.read.offset == (uint64_t)off1);
221 ).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
223 /* Filesystem is slow to respond */
226 fd = open(FULLPATH, O_RDONLY);
227 ASSERT_LE(0, fd) << strerror(errno);
230 * Submit two AIO read requests, and respond to neither. If the
231 * filesystem ever gets the second read request, then we failed to
232 * limit outstanding reads.
234 iocb0.aio_nbytes = bufsize;
235 iocb0.aio_fildes = fd;
236 iocb0.aio_buf = buf0;
237 iocb0.aio_offset = off0;
238 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
239 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
241 iocb1.aio_nbytes = bufsize;
242 iocb1.aio_fildes = fd;
243 iocb1.aio_buf = buf1;
244 iocb1.aio_offset = off1;
245 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
246 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
249 * Sleep for awhile to make sure the kernel has had a chance to issue
250 * the second read, even though the first has not yet returned
253 EXPECT_EQ(read_count, 1);
255 m_mock->kill_daemon();
256 /* Wait for AIO activity to complete, but ignore errors */
257 (void)aio_waitcomplete(NULL, NULL);
263 * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
264 * simultaneous read requests on the same file handle.
266 TEST_F(AsyncRead, async_read)
268 const char FULLPATH[] = "mountpoint/some_file.txt";
269 const char RELPATH[] = "some_file.txt";
272 ssize_t bufsize = 50;
273 char buf0[bufsize], buf1[bufsize];
275 off_t off1 = m_maxbcachebuf;
276 off_t fsize = 2 * m_maxbcachebuf;
277 struct aiocb iocb0, iocb1;
280 ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
282 expect_lookup(RELPATH, ino, fsize);
283 expect_open(ino, 0, 1);
284 EXPECT_CALL(*m_mock, process(
285 ResultOf([=](auto in) {
286 return (in.header.opcode == FUSE_READ &&
287 in.header.nodeid == ino &&
288 in.body.read.fh == FH &&
289 in.body.read.offset == (uint64_t)off0);
292 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
294 /* Filesystem is slow to respond */
296 EXPECT_CALL(*m_mock, process(
297 ResultOf([=](auto in) {
298 return (in.header.opcode == FUSE_READ &&
299 in.header.nodeid == ino &&
300 in.body.read.fh == FH &&
301 in.body.read.offset == (uint64_t)off1);
304 ).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
306 /* Filesystem is slow to respond */
309 fd = open(FULLPATH, O_RDONLY);
310 ASSERT_LE(0, fd) << strerror(errno);
313 * Submit two AIO read requests, but respond to neither. Ensure that
316 iocb0.aio_nbytes = bufsize;
317 iocb0.aio_fildes = fd;
318 iocb0.aio_buf = buf0;
319 iocb0.aio_offset = off0;
320 iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
321 ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
323 iocb1.aio_nbytes = bufsize;
324 iocb1.aio_fildes = fd;
325 iocb1.aio_buf = buf1;
326 iocb1.aio_offset = off1;
327 iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
328 ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
330 /* Wait until both reads have reached the daemon */
331 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
332 ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
334 m_mock->kill_daemon();
335 /* Wait for AIO activity to complete, but ignore errors */
336 (void)aio_waitcomplete(NULL, NULL);
341 /* The kernel should update the cached atime attribute during a read */
344 const char FULLPATH[] = "mountpoint/some_file.txt";
345 const char RELPATH[] = "some_file.txt";
346 const char *CONTENTS = "abcdefgh";
347 struct stat sb1, sb2;
350 ssize_t bufsize = strlen(CONTENTS);
351 uint8_t buf[bufsize];
353 expect_lookup(RELPATH, ino, bufsize);
354 expect_open(ino, 0, 1);
355 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
357 fd = open(FULLPATH, O_RDONLY);
358 ASSERT_LE(0, fd) << strerror(errno);
359 ASSERT_EQ(0, fstat(fd, &sb1));
361 /* Ensure atime will be different than it was during lookup */
364 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
365 ASSERT_EQ(0, fstat(fd, &sb2));
367 /* The kernel should automatically update atime during read */
368 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <));
369 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
370 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
375 /* The kernel should update the cached atime attribute during a cached read */
376 TEST_F(Read, atime_cached)
378 const char FULLPATH[] = "mountpoint/some_file.txt";
379 const char RELPATH[] = "some_file.txt";
380 const char *CONTENTS = "abcdefgh";
381 struct stat sb1, sb2;
384 ssize_t bufsize = strlen(CONTENTS);
385 uint8_t buf[bufsize];
387 expect_lookup(RELPATH, ino, bufsize);
388 expect_open(ino, 0, 1);
389 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
391 fd = open(FULLPATH, O_RDONLY);
392 ASSERT_LE(0, fd) << strerror(errno);
394 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
395 ASSERT_EQ(0, fstat(fd, &sb1));
397 /* Ensure atime will be different than it was during the first read */
400 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
401 ASSERT_EQ(0, fstat(fd, &sb2));
403 /* The kernel should automatically update atime during read */
404 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <));
405 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
406 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
411 /* dirty atime values should be flushed during close */
412 TEST_F(Read, atime_during_close)
414 const char FULLPATH[] = "mountpoint/some_file.txt";
415 const char RELPATH[] = "some_file.txt";
416 const char *CONTENTS = "abcdefgh";
419 const mode_t newmode = 0755;
421 ssize_t bufsize = strlen(CONTENTS);
422 uint8_t buf[bufsize];
424 expect_lookup(RELPATH, ino, bufsize);
425 expect_open(ino, 0, 1);
426 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
427 EXPECT_CALL(*m_mock, process(
428 ResultOf([&](auto in) {
429 uint32_t valid = FATTR_ATIME;
430 return (in.header.opcode == FUSE_SETATTR &&
431 in.header.nodeid == ino &&
432 in.body.setattr.valid == valid &&
433 (time_t)in.body.setattr.atime ==
435 (long)in.body.setattr.atimensec ==
439 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
440 SET_OUT_HEADER_LEN(out, attr);
441 out.body.attr.attr.ino = ino;
442 out.body.attr.attr.mode = S_IFREG | newmode;
444 expect_flush(ino, 1, ReturnErrno(0));
445 expect_release(ino, FuseTest::FH);
447 fd = open(FULLPATH, O_RDONLY);
448 ASSERT_LE(0, fd) << strerror(errno);
450 /* Ensure atime will be different than during lookup */
453 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
454 ASSERT_EQ(0, fstat(fd, &sb));
459 /* A cached atime should be flushed during FUSE_SETATTR */
460 TEST_F(Read, atime_during_setattr)
462 const char FULLPATH[] = "mountpoint/some_file.txt";
463 const char RELPATH[] = "some_file.txt";
464 const char *CONTENTS = "abcdefgh";
467 const mode_t newmode = 0755;
469 ssize_t bufsize = strlen(CONTENTS);
470 uint8_t buf[bufsize];
472 expect_lookup(RELPATH, ino, bufsize);
473 expect_open(ino, 0, 1);
474 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
475 EXPECT_CALL(*m_mock, process(
476 ResultOf([&](auto in) {
477 uint32_t valid = FATTR_MODE | FATTR_ATIME;
478 return (in.header.opcode == FUSE_SETATTR &&
479 in.header.nodeid == ino &&
480 in.body.setattr.valid == valid &&
481 (time_t)in.body.setattr.atime ==
483 (long)in.body.setattr.atimensec ==
487 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
488 SET_OUT_HEADER_LEN(out, attr);
489 out.body.attr.attr.ino = ino;
490 out.body.attr.attr.mode = S_IFREG | newmode;
493 fd = open(FULLPATH, O_RDONLY);
494 ASSERT_LE(0, fd) << strerror(errno);
496 /* Ensure atime will be different than during lookup */
499 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
500 ASSERT_EQ(0, fstat(fd, &sb));
501 ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
506 /* 0-length reads shouldn't cause any confusion */
507 TEST_F(Read, direct_io_read_nothing)
509 const char FULLPATH[] = "mountpoint/some_file.txt";
510 const char RELPATH[] = "some_file.txt";
513 uint64_t offset = 100;
516 expect_lookup(RELPATH, ino, offset + 1000);
517 expect_open(ino, FOPEN_DIRECT_IO, 1);
519 fd = open(FULLPATH, O_RDONLY);
520 ASSERT_LE(0, fd) << strerror(errno);
522 ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
527 * With direct_io, reads should not fill the cache. They should go straight to
530 TEST_F(Read, direct_io_pread)
532 const char FULLPATH[] = "mountpoint/some_file.txt";
533 const char RELPATH[] = "some_file.txt";
534 const char *CONTENTS = "abcdefgh";
537 uint64_t offset = 100;
538 ssize_t bufsize = strlen(CONTENTS);
539 uint8_t buf[bufsize];
541 expect_lookup(RELPATH, ino, offset + bufsize);
542 expect_open(ino, FOPEN_DIRECT_IO, 1);
543 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
545 fd = open(FULLPATH, O_RDONLY);
546 ASSERT_LE(0, fd) << strerror(errno);
548 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
549 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
551 // With FOPEN_DIRECT_IO, the cache should be bypassed. The server will
552 // get a 2nd read request.
553 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
554 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
555 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
560 * With direct_io, filesystems are allowed to return less data than is
561 * requested. fuse(4) should return a short read to userland.
563 TEST_F(Read, direct_io_short_read)
565 const char FULLPATH[] = "mountpoint/some_file.txt";
566 const char RELPATH[] = "some_file.txt";
567 const char *CONTENTS = "abcdefghijklmnop";
570 uint64_t offset = 100;
571 ssize_t bufsize = strlen(CONTENTS);
572 ssize_t halfbufsize = bufsize / 2;
573 uint8_t buf[bufsize];
575 expect_lookup(RELPATH, ino, offset + bufsize);
576 expect_open(ino, FOPEN_DIRECT_IO, 1);
577 expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
579 fd = open(FULLPATH, O_RDONLY);
580 ASSERT_LE(0, fd) << strerror(errno);
582 ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
584 ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
590 const char FULLPATH[] = "mountpoint/some_file.txt";
591 const char RELPATH[] = "some_file.txt";
592 const char *CONTENTS = "abcdefgh";
595 ssize_t bufsize = strlen(CONTENTS);
596 uint8_t buf[bufsize];
598 expect_lookup(RELPATH, ino, bufsize);
599 expect_open(ino, 0, 1);
600 EXPECT_CALL(*m_mock, process(
601 ResultOf([=](auto in) {
602 return (in.header.opcode == FUSE_READ);
605 ).WillOnce(Invoke(ReturnErrno(EIO)));
607 fd = open(FULLPATH, O_RDONLY);
608 ASSERT_LE(0, fd) << strerror(errno);
610 ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
611 ASSERT_EQ(EIO, errno);
616 * If the server returns a short read when direct io is not in use, that
617 * indicates EOF, because of a server-side truncation. We should invalidate
618 * all cached attributes. We may update the file size,
622 const char FULLPATH[] = "mountpoint/some_file.txt";
623 const char RELPATH[] = "some_file.txt";
624 const char *CONTENTS = "abcdefghijklmnop";
627 uint64_t offset = 100;
628 ssize_t bufsize = strlen(CONTENTS);
629 ssize_t partbufsize = 3 * bufsize / 4;
631 uint8_t buf[bufsize];
634 expect_lookup(RELPATH, ino, offset + bufsize);
635 expect_open(ino, 0, 1);
636 expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS);
637 expect_getattr(ino, offset + partbufsize);
639 fd = open(FULLPATH, O_RDONLY);
640 ASSERT_LE(0, fd) << strerror(errno);
642 r = pread(fd, buf, bufsize, offset);
643 ASSERT_LE(0, r) << strerror(errno);
644 EXPECT_EQ(partbufsize, r) << strerror(errno);
645 ASSERT_EQ(0, fstat(fd, &sb));
646 EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size);
650 /* Like Read.eof, but causes an entire buffer to be invalidated */
651 TEST_F(Read, eof_of_whole_buffer)
653 const char FULLPATH[] = "mountpoint/some_file.txt";
654 const char RELPATH[] = "some_file.txt";
655 const char *CONTENTS = "abcdefghijklmnop";
658 ssize_t bufsize = strlen(CONTENTS);
659 off_t old_filesize = m_maxbcachebuf * 2 + bufsize;
660 uint8_t buf[bufsize];
663 expect_lookup(RELPATH, ino, old_filesize);
664 expect_open(ino, 0, 1);
665 expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS);
666 expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS);
667 expect_getattr(ino, m_maxbcachebuf);
669 fd = open(FULLPATH, O_RDONLY);
670 ASSERT_LE(0, fd) << strerror(errno);
672 /* Cache the third block */
673 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, m_maxbcachebuf * 2))
675 /* Try to read the 2nd block, but it's past EOF */
676 ASSERT_EQ(0, pread(fd, buf, bufsize, m_maxbcachebuf))
678 ASSERT_EQ(0, fstat(fd, &sb));
679 EXPECT_EQ((off_t)(m_maxbcachebuf), sb.st_size);
684 * With the keep_cache option, the kernel may keep its read cache across
687 TEST_F(Read, keep_cache)
689 const char FULLPATH[] = "mountpoint/some_file.txt";
690 const char RELPATH[] = "some_file.txt";
691 const char *CONTENTS = "abcdefgh";
694 ssize_t bufsize = strlen(CONTENTS);
695 uint8_t buf[bufsize];
697 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
698 expect_open(ino, FOPEN_KEEP_CACHE, 2);
699 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
701 fd0 = open(FULLPATH, O_RDONLY);
702 ASSERT_LE(0, fd0) << strerror(errno);
703 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
705 fd1 = open(FULLPATH, O_RDWR);
706 ASSERT_LE(0, fd1) << strerror(errno);
709 * This read should be serviced by cache, even though it's on the other
712 ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
719 * Without the keep_cache option, the kernel should drop its read caches on
722 TEST_F(Read, keep_cache_disabled)
724 const char FULLPATH[] = "mountpoint/some_file.txt";
725 const char RELPATH[] = "some_file.txt";
726 const char *CONTENTS = "abcdefgh";
729 ssize_t bufsize = strlen(CONTENTS);
730 uint8_t buf[bufsize];
732 FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
733 expect_open(ino, 0, 2);
734 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
736 fd0 = open(FULLPATH, O_RDONLY);
737 ASSERT_LE(0, fd0) << strerror(errno);
738 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
740 fd1 = open(FULLPATH, O_RDWR);
741 ASSERT_LE(0, fd1) << strerror(errno);
744 * This read should not be serviced by cache, even though it's on the
745 * original file descriptor
747 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
748 ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
749 ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
757 const char FULLPATH[] = "mountpoint/some_file.txt";
758 const char RELPATH[] = "some_file.txt";
759 const char *CONTENTS = "abcdefgh";
763 size_t bufsize = strlen(CONTENTS);
768 expect_lookup(RELPATH, ino, bufsize);
769 expect_open(ino, 0, 1);
770 EXPECT_CALL(*m_mock, process(
771 ResultOf([=](auto in) {
772 return (in.header.opcode == FUSE_READ &&
773 in.header.nodeid == ino &&
774 in.body.read.fh == Read::FH &&
775 in.body.read.offset == 0 &&
776 in.body.read.size == bufsize);
779 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
780 out.header.len = sizeof(struct fuse_out_header) + bufsize;
781 memmove(out.body.bytes, CONTENTS, bufsize);
784 fd = open(FULLPATH, O_RDONLY);
785 ASSERT_LE(0, fd) << strerror(errno);
787 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
788 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
790 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
792 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
797 * The kernel should not update the cached atime attribute during a read, if
798 * MNT_NOATIME is used.
800 TEST_F(ReadNoatime, atime)
802 const char FULLPATH[] = "mountpoint/some_file.txt";
803 const char RELPATH[] = "some_file.txt";
804 const char *CONTENTS = "abcdefgh";
805 struct stat sb1, sb2;
808 ssize_t bufsize = strlen(CONTENTS);
809 uint8_t buf[bufsize];
811 expect_lookup(RELPATH, ino, bufsize);
812 expect_open(ino, 0, 1);
813 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
815 fd = open(FULLPATH, O_RDONLY);
816 ASSERT_LE(0, fd) << strerror(errno);
817 ASSERT_EQ(0, fstat(fd, &sb1));
821 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
822 ASSERT_EQ(0, fstat(fd, &sb2));
824 /* The kernel should not update atime during read */
825 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==));
826 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
827 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
833 * The kernel should not update the cached atime attribute during a cached
834 * read, if MNT_NOATIME is used.
836 TEST_F(ReadNoatime, atime_cached)
838 const char FULLPATH[] = "mountpoint/some_file.txt";
839 const char RELPATH[] = "some_file.txt";
840 const char *CONTENTS = "abcdefgh";
841 struct stat sb1, sb2;
844 ssize_t bufsize = strlen(CONTENTS);
845 uint8_t buf[bufsize];
847 expect_lookup(RELPATH, ino, bufsize);
848 expect_open(ino, 0, 1);
849 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
851 fd = open(FULLPATH, O_RDONLY);
852 ASSERT_LE(0, fd) << strerror(errno);
854 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
855 ASSERT_EQ(0, fstat(fd, &sb1));
859 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
860 ASSERT_EQ(0, fstat(fd, &sb2));
862 /* The kernel should automatically update atime during read */
863 EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==));
864 EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
865 EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
870 /* Read of an mmap()ed file fails */
871 TEST_F(ReadSigbus, mmap_eio)
873 const char FULLPATH[] = "mountpoint/some_file.txt";
874 const char RELPATH[] = "some_file.txt";
875 const char *CONTENTS = "abcdefgh";
880 size_t bufsize = strlen(CONTENTS);
885 expect_lookup(RELPATH, ino, bufsize);
886 expect_open(ino, 0, 1);
887 EXPECT_CALL(*m_mock, process(
888 ResultOf([=](auto in) {
889 return (in.header.opcode == FUSE_READ &&
890 in.header.nodeid == ino &&
891 in.body.read.fh == Read::FH);
894 ).WillRepeatedly(Invoke(ReturnErrno(EIO)));
896 fd = open(FULLPATH, O_RDONLY);
897 ASSERT_LE(0, fd) << strerror(errno);
899 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
900 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
902 /* Accessing the mapped page should return SIGBUS. */
904 bzero(&sa, sizeof(sa));
905 sa.sa_handler = SIG_DFL;
906 sa.sa_sigaction = handle_sigbus;
907 sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
908 ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
909 if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
910 atomic_signal_fence(std::memory_order::memory_order_seq_cst);
911 volatile char x __unused = *(volatile char*)p;
912 FAIL() << "shouldn't get here";
915 ASSERT_EQ(p, ReadSigbus::s_si_addr);
916 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
921 * A read via mmap comes up short, indicating that the file was truncated
924 TEST_F(Read, mmap_eof)
926 const char FULLPATH[] = "mountpoint/some_file.txt";
927 const char RELPATH[] = "some_file.txt";
928 const char *CONTENTS = "abcdefgh";
932 size_t bufsize = strlen(CONTENTS);
938 expect_lookup(RELPATH, ino, m_maxbcachebuf);
939 expect_open(ino, 0, 1);
940 EXPECT_CALL(*m_mock, process(
941 ResultOf([=](auto in) {
942 return (in.header.opcode == FUSE_READ &&
943 in.header.nodeid == ino &&
944 in.body.read.fh == Read::FH &&
945 in.body.read.offset == 0 &&
946 in.body.read.size == (uint32_t)m_maxbcachebuf);
949 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
950 out.header.len = sizeof(struct fuse_out_header) + bufsize;
951 memmove(out.body.bytes, CONTENTS, bufsize);
953 expect_getattr(ino, bufsize);
955 fd = open(FULLPATH, O_RDONLY);
956 ASSERT_LE(0, fd) << strerror(errno);
958 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
959 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
961 /* The file size should be automatically truncated */
962 ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
963 ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
964 EXPECT_EQ((off_t)bufsize, sb.st_size);
966 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
971 * During VOP_GETPAGES, the FUSE server fails a FUSE_GETATTR operation. This
972 * almost certainly indicates a buggy FUSE server, and our goal should be not
973 * to panic. Instead, generate SIGBUS.
975 TEST_F(ReadSigbus, mmap_getblksz_fail)
977 const char FULLPATH[] = "mountpoint/some_file.txt";
978 const char RELPATH[] = "some_file.txt";
979 const char *CONTENTS = "abcdefgh";
985 size_t bufsize = strlen(CONTENTS);
986 mode_t mode = S_IFREG | 0644;
991 FuseTest::expect_lookup(RELPATH, ino, mode, bufsize, 1, 0);
992 /* Expect two GETATTR calls that succeed, followed by one that fail. */
993 EXPECT_CALL(*m_mock, process(
994 ResultOf([=](auto in) {
995 return (in.header.opcode == FUSE_GETATTR &&
996 in.header.nodeid == ino);
1001 .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
1002 SET_OUT_HEADER_LEN(out, attr);
1003 out.body.attr.attr.ino = ino;
1004 out.body.attr.attr.mode = mode;
1005 out.body.attr.attr.size = bufsize;
1006 out.body.attr.attr_valid = 0;
1008 EXPECT_CALL(*m_mock, process(
1009 ResultOf([=](auto in) {
1010 return (in.header.opcode == FUSE_GETATTR &&
1011 in.header.nodeid == ino);
1015 .WillRepeatedly(Invoke(ReturnErrno(EIO)));
1016 expect_open(ino, 0, 1);
1017 EXPECT_CALL(*m_mock, process(
1018 ResultOf([=](auto in) {
1019 return (in.header.opcode == FUSE_READ);
1024 fd = open(FULLPATH, O_RDONLY);
1025 ASSERT_LE(0, fd) << strerror(errno);
1027 p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
1028 ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1030 /* Accessing the mapped page should return SIGBUS. */
1031 bzero(&sa, sizeof(sa));
1032 sa.sa_handler = SIG_DFL;
1033 sa.sa_sigaction = handle_sigbus;
1034 sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
1035 ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
1036 if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
1037 atomic_signal_fence(std::memory_order::memory_order_seq_cst);
1038 volatile char x __unused = *(volatile char*)p;
1039 FAIL() << "shouldn't get here";
1042 ASSERT_EQ(p, ReadSigbus::s_si_addr);
1043 ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
1048 * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
1049 * cache and to straight to the daemon
1051 TEST_F(Read, o_direct)
1053 const char FULLPATH[] = "mountpoint/some_file.txt";
1054 const char RELPATH[] = "some_file.txt";
1055 const char *CONTENTS = "abcdefgh";
1058 ssize_t bufsize = strlen(CONTENTS);
1059 uint8_t buf[bufsize];
1061 expect_lookup(RELPATH, ino, bufsize);
1062 expect_open(ino, 0, 1);
1063 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1065 fd = open(FULLPATH, O_RDONLY);
1066 ASSERT_LE(0, fd) << strerror(errno);
1069 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1070 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1072 // Reads with o_direct should bypass the cache
1073 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1074 ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1075 ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1076 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1077 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1084 const char FULLPATH[] = "mountpoint/some_file.txt";
1085 const char RELPATH[] = "some_file.txt";
1086 const char *CONTENTS = "abcdefgh";
1090 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
1091 * to read from. Without this, the read might start at a lower offset.
1093 uint64_t offset = m_maxbcachebuf;
1094 ssize_t bufsize = strlen(CONTENTS);
1095 uint8_t buf[bufsize];
1097 expect_lookup(RELPATH, ino, offset + bufsize);
1098 expect_open(ino, 0, 1);
1099 expect_read(ino, offset, bufsize, bufsize, CONTENTS);
1101 fd = open(FULLPATH, O_RDONLY);
1102 ASSERT_LE(0, fd) << strerror(errno);
1104 ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
1105 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1111 const char FULLPATH[] = "mountpoint/some_file.txt";
1112 const char RELPATH[] = "some_file.txt";
1113 const char *CONTENTS = "abcdefgh";
1116 ssize_t bufsize = strlen(CONTENTS);
1117 uint8_t buf[bufsize];
1119 expect_lookup(RELPATH, ino, bufsize);
1120 expect_open(ino, 0, 1);
1121 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1123 fd = open(FULLPATH, O_RDONLY);
1124 ASSERT_LE(0, fd) << strerror(errno);
1126 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1127 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1132 TEST_F(Read_7_8, read)
1134 const char FULLPATH[] = "mountpoint/some_file.txt";
1135 const char RELPATH[] = "some_file.txt";
1136 const char *CONTENTS = "abcdefgh";
1139 ssize_t bufsize = strlen(CONTENTS);
1140 uint8_t buf[bufsize];
1142 expect_lookup(RELPATH, ino, bufsize);
1143 expect_open(ino, 0, 1);
1144 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1146 fd = open(FULLPATH, O_RDONLY);
1147 ASSERT_LE(0, fd) << strerror(errno);
1149 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1150 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1156 * If cacheing is enabled, the kernel should try to read an entire cache block
1159 TEST_F(Read, cache_block)
1161 const char FULLPATH[] = "mountpoint/some_file.txt";
1162 const char RELPATH[] = "some_file.txt";
1163 const char *CONTENTS0 = "abcdefghijklmnop";
1166 ssize_t bufsize = 8;
1167 ssize_t filesize = m_maxbcachebuf * 2;
1170 const char *contents1 = CONTENTS0 + bufsize;
1172 contents = (char*)calloc(1, filesize);
1173 ASSERT_NE(nullptr, contents);
1174 memmove(contents, CONTENTS0, strlen(CONTENTS0));
1176 expect_lookup(RELPATH, ino, filesize);
1177 expect_open(ino, 0, 1);
1178 expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf,
1181 fd = open(FULLPATH, O_RDONLY);
1182 ASSERT_LE(0, fd) << strerror(errno);
1184 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1185 ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
1187 /* A subsequent read should be serviced by cache */
1188 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1189 ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
1194 /* Reading with sendfile should work (though it obviously won't be 0-copy) */
1195 TEST_F(Read, sendfile)
1197 const char FULLPATH[] = "mountpoint/some_file.txt";
1198 const char RELPATH[] = "some_file.txt";
1199 const char *CONTENTS = "abcdefgh";
1202 size_t bufsize = strlen(CONTENTS);
1203 uint8_t buf[bufsize];
1207 expect_lookup(RELPATH, ino, bufsize);
1208 expect_open(ino, 0, 1);
1209 EXPECT_CALL(*m_mock, process(
1210 ResultOf([=](auto in) {
1211 return (in.header.opcode == FUSE_READ &&
1212 in.header.nodeid == ino &&
1213 in.body.read.fh == Read::FH &&
1214 in.body.read.offset == 0 &&
1215 in.body.read.size == bufsize);
1218 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1219 out.header.len = sizeof(struct fuse_out_header) + bufsize;
1220 memmove(out.body.bytes, CONTENTS, bufsize);
1223 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
1225 fd = open(FULLPATH, O_RDONLY);
1226 ASSERT_LE(0, fd) << strerror(errno);
1228 ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
1230 ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize))
1232 ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1239 /* sendfile should fail gracefully if fuse declines the read */
1240 /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
1241 TEST_F(Read, sendfile_eio)
1243 const char FULLPATH[] = "mountpoint/some_file.txt";
1244 const char RELPATH[] = "some_file.txt";
1245 const char *CONTENTS = "abcdefgh";
1248 ssize_t bufsize = strlen(CONTENTS);
1252 expect_lookup(RELPATH, ino, bufsize);
1253 expect_open(ino, 0, 1);
1254 EXPECT_CALL(*m_mock, process(
1255 ResultOf([=](auto in) {
1256 return (in.header.opcode == FUSE_READ);
1259 ).WillOnce(Invoke(ReturnErrno(EIO)));
1261 ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
1263 fd = open(FULLPATH, O_RDONLY);
1264 ASSERT_LE(0, fd) << strerror(errno);
1266 ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
1274 * Sequential reads should use readahead. And if allowed, large reads should
1277 TEST_P(ReadAhead, readahead) {
1278 const char FULLPATH[] = "mountpoint/some_file.txt";
1279 const char RELPATH[] = "some_file.txt";
1281 int fd, maxcontig, clustersize;
1282 ssize_t bufsize = 4 * m_maxbcachebuf;
1283 ssize_t filesize = bufsize;
1285 char *rbuf, *contents;
1288 contents = (char*)malloc(filesize);
1289 ASSERT_NE(nullptr, contents);
1290 memset(contents, 'X', filesize);
1291 rbuf = (char*)calloc(1, bufsize);
1293 expect_lookup(RELPATH, ino, filesize);
1294 expect_open(ino, 0, 1);
1295 maxcontig = m_noclusterr ? m_maxbcachebuf :
1296 m_maxbcachebuf + m_maxreadahead;
1297 clustersize = MIN(maxcontig, m_maxphys);
1298 for (offs = 0; offs < bufsize; offs += clustersize) {
1299 len = std::min((size_t)clustersize, (size_t)(filesize - offs));
1300 expect_read(ino, offs, len, len, contents + offs);
1303 fd = open(FULLPATH, O_RDONLY);
1304 ASSERT_LE(0, fd) << strerror(errno);
1306 /* Set the internal readahead counter to a "large" value */
1307 ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno);
1309 ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
1310 ASSERT_EQ(0, memcmp(rbuf, contents, bufsize));
1317 INSTANTIATE_TEST_CASE_P(RA, ReadAhead,
1318 Values(tuple<bool, int>(false, 0),
1319 tuple<bool, int>(false, 1),
1320 tuple<bool, int>(false, 2),
1321 tuple<bool, int>(false, 3),
1322 tuple<bool, int>(true, 0),
1323 tuple<bool, int>(true, 1),
1324 tuple<bool, int>(true, 2)));
1326 /* fuse_init_out.time_gran controls the granularity of timestamps */
1327 TEST_P(TimeGran, atime_during_setattr)
1329 const char FULLPATH[] = "mountpoint/some_file.txt";
1330 const char RELPATH[] = "some_file.txt";
1331 const char *CONTENTS = "abcdefgh";
1332 ssize_t bufsize = strlen(CONTENTS);
1333 uint8_t buf[bufsize];
1335 const mode_t newmode = 0755;
1338 expect_lookup(RELPATH, ino, bufsize);
1339 expect_open(ino, 0, 1);
1340 expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1341 EXPECT_CALL(*m_mock, process(
1342 ResultOf([=](auto in) {
1343 uint32_t valid = FATTR_MODE | FATTR_ATIME;
1344 return (in.header.opcode == FUSE_SETATTR &&
1345 in.header.nodeid == ino &&
1346 in.body.setattr.valid == valid &&
1347 in.body.setattr.atimensec % m_time_gran == 0);
1350 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1351 SET_OUT_HEADER_LEN(out, attr);
1352 out.body.attr.attr.ino = ino;
1353 out.body.attr.attr.mode = S_IFREG | newmode;
1356 fd = open(FULLPATH, O_RDWR);
1357 ASSERT_LE(0, fd) << strerror(errno);
1359 ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1360 ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1365 INSTANTIATE_TEST_CASE_P(TG, TimeGran, Range(0u, 10u));