xref: /freebsd/tests/sys/fs/fusefs/read.cc (revision 8bae22bb)
19821f1d3SAlan Somers /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
39821f1d3SAlan Somers  *
49821f1d3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers  *
69821f1d3SAlan Somers  * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers  * from the FreeBSD Foundation.
89821f1d3SAlan Somers  *
99821f1d3SAlan Somers  * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers  * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers  * are met:
129821f1d3SAlan Somers  * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers  *    documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers  *
189821f1d3SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers  * SUCH DAMAGE.
299821f1d3SAlan Somers  */
309821f1d3SAlan Somers 
319821f1d3SAlan Somers extern "C" {
32a1c9f4adSAlan Somers #include <sys/param.h>
339821f1d3SAlan Somers #include <sys/mman.h>
349821f1d3SAlan Somers #include <sys/socket.h>
359821f1d3SAlan Somers #include <sys/sysctl.h>
369821f1d3SAlan Somers #include <sys/uio.h>
379821f1d3SAlan Somers 
389821f1d3SAlan Somers #include <aio.h>
399821f1d3SAlan Somers #include <fcntl.h>
40e97ae4adSAlan Somers #include <semaphore.h>
414f917847SAlan Somers #include <setjmp.h>
424f917847SAlan Somers #include <signal.h>
439821f1d3SAlan Somers #include <unistd.h>
449821f1d3SAlan Somers }
459821f1d3SAlan Somers 
469821f1d3SAlan Somers #include "mockfs.hh"
479821f1d3SAlan Somers #include "utils.hh"
489821f1d3SAlan Somers 
499821f1d3SAlan Somers using namespace testing;
509821f1d3SAlan Somers 
519821f1d3SAlan Somers class Read: public FuseTest {
529821f1d3SAlan Somers 
539821f1d3SAlan Somers public:
expect_lookup(const char * relpath,uint64_t ino,uint64_t size)549821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
559821f1d3SAlan Somers {
569821f1d3SAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
579821f1d3SAlan Somers }
589821f1d3SAlan Somers };
599821f1d3SAlan Somers 
60fb619c94SAlan Somers class RofsRead: public Read {
61fb619c94SAlan Somers public:
SetUp()62fb619c94SAlan Somers virtual void SetUp() {
63fb619c94SAlan Somers 	m_ro = true;
64fb619c94SAlan Somers 	Read::SetUp();
65fb619c94SAlan Somers }
66fb619c94SAlan Somers };
67fb619c94SAlan Somers 
6816bd2d47SAlan Somers class Read_7_8: public FuseTest {
6916bd2d47SAlan Somers public:
SetUp()7016bd2d47SAlan Somers virtual void SetUp() {
7116bd2d47SAlan Somers 	m_kernel_minor_version = 8;
7216bd2d47SAlan Somers 	FuseTest::SetUp();
7316bd2d47SAlan Somers }
7416bd2d47SAlan Somers 
expect_lookup(const char * relpath,uint64_t ino,uint64_t size)7516bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
7616bd2d47SAlan Somers {
7716bd2d47SAlan Somers 	FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
7816bd2d47SAlan Somers }
7916bd2d47SAlan Somers };
8016bd2d47SAlan Somers 
819821f1d3SAlan Somers class AioRead: public Read {
829821f1d3SAlan Somers public:
SetUp()839821f1d3SAlan Somers virtual void SetUp() {
84c2265ae7SAlan Somers 	if (!is_unsafe_aio_enabled())
859821f1d3SAlan Somers 		GTEST_SKIP() <<
869821f1d3SAlan Somers 			"vfs.aio.enable_unsafe must be set for this test";
87c2265ae7SAlan Somers 	FuseTest::SetUp();
889821f1d3SAlan Somers }
899821f1d3SAlan Somers };
909821f1d3SAlan Somers 
919821f1d3SAlan Somers class AsyncRead: public AioRead {
SetUp()929821f1d3SAlan Somers 	virtual void SetUp() {
939821f1d3SAlan Somers 		m_init_flags = FUSE_ASYNC_READ;
949821f1d3SAlan Somers 		AioRead::SetUp();
959821f1d3SAlan Somers 	}
969821f1d3SAlan Somers };
979821f1d3SAlan Somers 
98f8ebf1cdSAlan Somers class ReadAhead: public Read,
99f2704f05SAlan Somers 		 public WithParamInterface<tuple<bool, int>>
100402b609cSAlan Somers {
SetUp()1019821f1d3SAlan Somers 	virtual void SetUp() {
102f2704f05SAlan Somers 		int val;
103f2704f05SAlan Somers 		const char *node = "vfs.maxbcachebuf";
104f2704f05SAlan Somers 		size_t size = sizeof(val);
105f2704f05SAlan Somers 		ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
106f2704f05SAlan Somers 			<< strerror(errno);
107f2704f05SAlan Somers 
108f2704f05SAlan Somers 		m_maxreadahead = val * get<1>(GetParam());
109402b609cSAlan Somers 		m_noclusterr = get<0>(GetParam());
110f8ebf1cdSAlan Somers 		Read::SetUp();
1119821f1d3SAlan Somers 	}
1129821f1d3SAlan Somers };
1139821f1d3SAlan Somers 
11491972cfcSAlan Somers class ReadNoatime: public Read {
SetUp()11591972cfcSAlan Somers 	virtual void SetUp() {
11691972cfcSAlan Somers 		m_noatime = true;
11791972cfcSAlan Somers 		Read::SetUp();
11891972cfcSAlan Somers 	}
11991972cfcSAlan Somers };
12091972cfcSAlan Somers 
1214f917847SAlan Somers class ReadSigbus: public Read
1224f917847SAlan Somers {
1234f917847SAlan Somers public:
1244f917847SAlan Somers static jmp_buf s_jmpbuf;
1253fcbde5eSKonstantin Belousov static void *s_si_addr;
1264f917847SAlan Somers 
TearDown()1274f917847SAlan Somers void TearDown() {
1284f917847SAlan Somers 	struct sigaction sa;
1294f917847SAlan Somers 
1304f917847SAlan Somers 	bzero(&sa, sizeof(sa));
1314f917847SAlan Somers 	sa.sa_handler = SIG_DFL;
1324f917847SAlan Somers 	sigaction(SIGBUS, &sa, NULL);
1334f917847SAlan Somers 
1344f917847SAlan Somers 	FuseTest::TearDown();
1354f917847SAlan Somers }
1364f917847SAlan Somers 
1374f917847SAlan Somers };
1384f917847SAlan Somers 
1394f917847SAlan Somers static void
handle_sigbus(int signo __unused,siginfo_t * info,void * uap __unused)1404f917847SAlan Somers handle_sigbus(int signo __unused, siginfo_t *info, void *uap __unused) {
1413fcbde5eSKonstantin Belousov 	ReadSigbus::s_si_addr = info->si_addr;
1424f917847SAlan Somers 	longjmp(ReadSigbus::s_jmpbuf, 1);
1434f917847SAlan Somers }
1444f917847SAlan Somers 
1454f917847SAlan Somers jmp_buf ReadSigbus::s_jmpbuf;
1463fcbde5eSKonstantin Belousov void *ReadSigbus::s_si_addr;
1474f917847SAlan Somers 
14891972cfcSAlan Somers class TimeGran: public Read, public WithParamInterface<unsigned> {
14991972cfcSAlan Somers public:
SetUp()15091972cfcSAlan Somers virtual void SetUp() {
15191972cfcSAlan Somers 	m_time_gran = 1 << GetParam();
15291972cfcSAlan Somers 	Read::SetUp();
15391972cfcSAlan Somers }
15491972cfcSAlan Somers };
15591972cfcSAlan Somers 
1569821f1d3SAlan Somers /* AIO reads need to set the header's pid field correctly */
1579821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
TEST_F(AioRead,aio_read)1589821f1d3SAlan Somers TEST_F(AioRead, aio_read)
1599821f1d3SAlan Somers {
1609821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1619821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
1629821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
1639821f1d3SAlan Somers 	uint64_t ino = 42;
1649821f1d3SAlan Somers 	int fd;
1659821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1668e765737SAlan Somers 	uint8_t buf[bufsize];
1679821f1d3SAlan Somers 	struct aiocb iocb, *piocb;
1689821f1d3SAlan Somers 
1699821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
1709821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1719821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1729821f1d3SAlan Somers 
1739821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
1749821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1759821f1d3SAlan Somers 
1769821f1d3SAlan Somers 	iocb.aio_nbytes = bufsize;
1779821f1d3SAlan Somers 	iocb.aio_fildes = fd;
1789821f1d3SAlan Somers 	iocb.aio_buf = buf;
1799821f1d3SAlan Somers 	iocb.aio_offset = 0;
1809821f1d3SAlan Somers 	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
1819821f1d3SAlan Somers 	ASSERT_EQ(0, aio_read(&iocb)) << strerror(errno);
1829821f1d3SAlan Somers 	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
1839821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
1847fc0921dSAlan Somers 
1857fc0921dSAlan Somers 	leak(fd);
1869821f1d3SAlan Somers }
1879821f1d3SAlan Somers 
1889821f1d3SAlan Somers /*
1899821f1d3SAlan Somers  * Without the FUSE_ASYNC_READ mount option, fuse(4) should ensure that there
1909821f1d3SAlan Somers  * is at most one outstanding read operation per file handle
1919821f1d3SAlan Somers  */
TEST_F(AioRead,async_read_disabled)1929821f1d3SAlan Somers TEST_F(AioRead, async_read_disabled)
1939821f1d3SAlan Somers {
1949821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1959821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
1969821f1d3SAlan Somers 	uint64_t ino = 42;
1979821f1d3SAlan Somers 	int fd;
1989821f1d3SAlan Somers 	ssize_t bufsize = 50;
1999821f1d3SAlan Somers 	char buf0[bufsize], buf1[bufsize];
2009821f1d3SAlan Somers 	off_t off0 = 0;
2016ca3b02bSAlan Somers 	off_t off1 = m_maxbcachebuf;
2029821f1d3SAlan Somers 	struct aiocb iocb0, iocb1;
203e97ae4adSAlan Somers 	volatile sig_atomic_t read_count = 0;
2049821f1d3SAlan Somers 
205e97ae4adSAlan Somers 	expect_lookup(RELPATH, ino, 131072);
2069821f1d3SAlan Somers 	expect_open(ino, 0, 1);
2079821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
2089821f1d3SAlan Somers 		ResultOf([=](auto in) {
20929edc611SAlan Somers 			return (in.header.opcode == FUSE_READ &&
21029edc611SAlan Somers 				in.header.nodeid == ino &&
21129edc611SAlan Somers 				in.body.read.fh == FH &&
21229edc611SAlan Somers 				in.body.read.offset == (uint64_t)off0);
2139821f1d3SAlan Somers 		}, Eq(true)),
2149821f1d3SAlan Somers 		_)
215e97ae4adSAlan Somers 	).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
216e97ae4adSAlan Somers 		read_count++;
2179821f1d3SAlan Somers 		/* Filesystem is slow to respond */
2189821f1d3SAlan Somers 	}));
2199821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
2209821f1d3SAlan Somers 		ResultOf([=](auto in) {
22129edc611SAlan Somers 			return (in.header.opcode == FUSE_READ &&
22229edc611SAlan Somers 				in.header.nodeid == ino &&
22329edc611SAlan Somers 				in.body.read.fh == FH &&
22429edc611SAlan Somers 				in.body.read.offset == (uint64_t)off1);
2259821f1d3SAlan Somers 		}, Eq(true)),
2269821f1d3SAlan Somers 		_)
227e97ae4adSAlan Somers 	).WillRepeatedly(Invoke([&](auto in __unused, auto &out __unused) {
228e97ae4adSAlan Somers 		read_count++;
229e97ae4adSAlan Somers 		/* Filesystem is slow to respond */
230e97ae4adSAlan Somers 	}));
2319821f1d3SAlan Somers 
2329821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
2339821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2349821f1d3SAlan Somers 
2359821f1d3SAlan Somers 	/*
2369821f1d3SAlan Somers 	 * Submit two AIO read requests, and respond to neither.  If the
2379821f1d3SAlan Somers 	 * filesystem ever gets the second read request, then we failed to
2389821f1d3SAlan Somers 	 * limit outstanding reads.
2399821f1d3SAlan Somers 	 */
2409821f1d3SAlan Somers 	iocb0.aio_nbytes = bufsize;
2419821f1d3SAlan Somers 	iocb0.aio_fildes = fd;
2429821f1d3SAlan Somers 	iocb0.aio_buf = buf0;
2439821f1d3SAlan Somers 	iocb0.aio_offset = off0;
2449821f1d3SAlan Somers 	iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
2459821f1d3SAlan Somers 	ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
2469821f1d3SAlan Somers 
2479821f1d3SAlan Somers 	iocb1.aio_nbytes = bufsize;
2489821f1d3SAlan Somers 	iocb1.aio_fildes = fd;
2499821f1d3SAlan Somers 	iocb1.aio_buf = buf1;
2509821f1d3SAlan Somers 	iocb1.aio_offset = off1;
2519821f1d3SAlan Somers 	iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
2529821f1d3SAlan Somers 	ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
2539821f1d3SAlan Somers 
2549821f1d3SAlan Somers 	/*
2559821f1d3SAlan Somers 	 * Sleep for awhile to make sure the kernel has had a chance to issue
2569821f1d3SAlan Somers 	 * the second read, even though the first has not yet returned
2579821f1d3SAlan Somers 	 */
258a87257acSAlan Somers 	nap();
259e97ae4adSAlan Somers 	EXPECT_EQ(read_count, 1);
2609821f1d3SAlan Somers 
261e97ae4adSAlan Somers 	m_mock->kill_daemon();
262e97ae4adSAlan Somers 	/* Wait for AIO activity to complete, but ignore errors */
263e97ae4adSAlan Somers 	(void)aio_waitcomplete(NULL, NULL);
264e97ae4adSAlan Somers 
2657fc0921dSAlan Somers 	leak(fd);
2669821f1d3SAlan Somers }
2679821f1d3SAlan Somers 
2689821f1d3SAlan Somers /*
2699821f1d3SAlan Somers  * With the FUSE_ASYNC_READ mount option, fuse(4) may issue multiple
2709821f1d3SAlan Somers  * simultaneous read requests on the same file handle.
2719821f1d3SAlan Somers  */
TEST_F(AsyncRead,async_read)272e97ae4adSAlan Somers TEST_F(AsyncRead, async_read)
2739821f1d3SAlan Somers {
2749821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2759821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2769821f1d3SAlan Somers 	uint64_t ino = 42;
2779821f1d3SAlan Somers 	int fd;
2789821f1d3SAlan Somers 	ssize_t bufsize = 50;
2799821f1d3SAlan Somers 	char buf0[bufsize], buf1[bufsize];
2809821f1d3SAlan Somers 	off_t off0 = 0;
2816ca3b02bSAlan Somers 	off_t off1 = m_maxbcachebuf;
2826ca3b02bSAlan Somers 	off_t fsize = 2 * m_maxbcachebuf;
2839821f1d3SAlan Somers 	struct aiocb iocb0, iocb1;
284e97ae4adSAlan Somers 	sem_t sem;
2859821f1d3SAlan Somers 
286e97ae4adSAlan Somers 	ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno);
287e97ae4adSAlan Somers 
2886ca3b02bSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
2899821f1d3SAlan Somers 	expect_open(ino, 0, 1);
2909821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
2919821f1d3SAlan Somers 		ResultOf([=](auto in) {
29229edc611SAlan Somers 			return (in.header.opcode == FUSE_READ &&
29329edc611SAlan Somers 				in.header.nodeid == ino &&
29429edc611SAlan Somers 				in.body.read.fh == FH &&
29529edc611SAlan Somers 				in.body.read.offset == (uint64_t)off0);
2969821f1d3SAlan Somers 		}, Eq(true)),
2979821f1d3SAlan Somers 		_)
298e97ae4adSAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
299e97ae4adSAlan Somers 		sem_post(&sem);
3009821f1d3SAlan Somers 		/* Filesystem is slow to respond */
3019821f1d3SAlan Somers 	}));
3029821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
3039821f1d3SAlan Somers 		ResultOf([=](auto in) {
30429edc611SAlan Somers 			return (in.header.opcode == FUSE_READ &&
30529edc611SAlan Somers 				in.header.nodeid == ino &&
30629edc611SAlan Somers 				in.body.read.fh == FH &&
30729edc611SAlan Somers 				in.body.read.offset == (uint64_t)off1);
3089821f1d3SAlan Somers 		}, Eq(true)),
3099821f1d3SAlan Somers 		_)
310e97ae4adSAlan Somers 	).WillOnce(Invoke([&](auto in __unused, auto &out __unused) {
311e97ae4adSAlan Somers 		sem_post(&sem);
3129821f1d3SAlan Somers 		/* Filesystem is slow to respond */
3139821f1d3SAlan Somers 	}));
3149821f1d3SAlan Somers 
3159821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
3169821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
3179821f1d3SAlan Somers 
3189821f1d3SAlan Somers 	/*
3199821f1d3SAlan Somers 	 * Submit two AIO read requests, but respond to neither.  Ensure that
3209821f1d3SAlan Somers 	 * we received both.
3219821f1d3SAlan Somers 	 */
3229821f1d3SAlan Somers 	iocb0.aio_nbytes = bufsize;
3239821f1d3SAlan Somers 	iocb0.aio_fildes = fd;
3249821f1d3SAlan Somers 	iocb0.aio_buf = buf0;
3259821f1d3SAlan Somers 	iocb0.aio_offset = off0;
3269821f1d3SAlan Somers 	iocb0.aio_sigevent.sigev_notify = SIGEV_NONE;
3279821f1d3SAlan Somers 	ASSERT_EQ(0, aio_read(&iocb0)) << strerror(errno);
3289821f1d3SAlan Somers 
3299821f1d3SAlan Somers 	iocb1.aio_nbytes = bufsize;
3309821f1d3SAlan Somers 	iocb1.aio_fildes = fd;
3319821f1d3SAlan Somers 	iocb1.aio_buf = buf1;
3329821f1d3SAlan Somers 	iocb1.aio_offset = off1;
3339821f1d3SAlan Somers 	iocb1.aio_sigevent.sigev_notify = SIGEV_NONE;
3349821f1d3SAlan Somers 	ASSERT_EQ(0, aio_read(&iocb1)) << strerror(errno);
3359821f1d3SAlan Somers 
336e97ae4adSAlan Somers 	/* Wait until both reads have reached the daemon */
337e97ae4adSAlan Somers 	ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
338e97ae4adSAlan Somers 	ASSERT_EQ(0, sem_wait(&sem)) << strerror(errno);
3399821f1d3SAlan Somers 
340e97ae4adSAlan Somers 	m_mock->kill_daemon();
341e97ae4adSAlan Somers 	/* Wait for AIO activity to complete, but ignore errors */
342e97ae4adSAlan Somers 	(void)aio_waitcomplete(NULL, NULL);
343e97ae4adSAlan Somers 
3447fc0921dSAlan Somers 	leak(fd);
3459821f1d3SAlan Somers }
3469821f1d3SAlan Somers 
34791972cfcSAlan Somers /* The kernel should update the cached atime attribute during a read */
TEST_F(Read,atime)34891972cfcSAlan Somers TEST_F(Read, atime)
34991972cfcSAlan Somers {
35091972cfcSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
35191972cfcSAlan Somers 	const char RELPATH[] = "some_file.txt";
35291972cfcSAlan Somers 	const char *CONTENTS = "abcdefgh";
35391972cfcSAlan Somers 	struct stat sb1, sb2;
35491972cfcSAlan Somers 	uint64_t ino = 42;
35591972cfcSAlan Somers 	int fd;
35691972cfcSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
35791972cfcSAlan Somers 	uint8_t buf[bufsize];
35891972cfcSAlan Somers 
35991972cfcSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
36091972cfcSAlan Somers 	expect_open(ino, 0, 1);
36191972cfcSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
36291972cfcSAlan Somers 
36391972cfcSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
36491972cfcSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
36591972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1));
36691972cfcSAlan Somers 
36791972cfcSAlan Somers 	/* Ensure atime will be different than it was during lookup */
36891972cfcSAlan Somers 	nap();
36991972cfcSAlan Somers 
37091972cfcSAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
37191972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb2));
37291972cfcSAlan Somers 
37391972cfcSAlan Somers 	/* The kernel should automatically update atime during read */
37491972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <));
37591972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
37691972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
37791972cfcSAlan Somers 
37891972cfcSAlan Somers 	leak(fd);
37991972cfcSAlan Somers }
38091972cfcSAlan Somers 
38191972cfcSAlan Somers /* The kernel should update the cached atime attribute during a cached read */
TEST_F(Read,atime_cached)38291972cfcSAlan Somers TEST_F(Read, atime_cached)
38391972cfcSAlan Somers {
38491972cfcSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
38591972cfcSAlan Somers 	const char RELPATH[] = "some_file.txt";
38691972cfcSAlan Somers 	const char *CONTENTS = "abcdefgh";
38791972cfcSAlan Somers 	struct stat sb1, sb2;
38891972cfcSAlan Somers 	uint64_t ino = 42;
38991972cfcSAlan Somers 	int fd;
39091972cfcSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
39191972cfcSAlan Somers 	uint8_t buf[bufsize];
39291972cfcSAlan Somers 
39391972cfcSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
39491972cfcSAlan Somers 	expect_open(ino, 0, 1);
39591972cfcSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
39691972cfcSAlan Somers 
39791972cfcSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
39891972cfcSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
39991972cfcSAlan Somers 
40091972cfcSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
40191972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1));
40291972cfcSAlan Somers 
40391972cfcSAlan Somers 	/* Ensure atime will be different than it was during the first read */
40491972cfcSAlan Somers 	nap();
40591972cfcSAlan Somers 
40691972cfcSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
40791972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb2));
40891972cfcSAlan Somers 
40991972cfcSAlan Somers 	/* The kernel should automatically update atime during read */
41091972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, <));
41191972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
41291972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
41391972cfcSAlan Somers 
41491972cfcSAlan Somers 	leak(fd);
41591972cfcSAlan Somers }
41691972cfcSAlan Somers 
41791972cfcSAlan Somers /* dirty atime values should be flushed during close */
TEST_F(Read,atime_during_close)41891972cfcSAlan Somers TEST_F(Read, atime_during_close)
41991972cfcSAlan Somers {
42091972cfcSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
42191972cfcSAlan Somers 	const char RELPATH[] = "some_file.txt";
42291972cfcSAlan Somers 	const char *CONTENTS = "abcdefgh";
42391972cfcSAlan Somers 	struct stat sb;
42491972cfcSAlan Somers 	uint64_t ino = 42;
42591972cfcSAlan Somers 	const mode_t newmode = 0755;
42691972cfcSAlan Somers 	int fd;
42791972cfcSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
42891972cfcSAlan Somers 	uint8_t buf[bufsize];
42991972cfcSAlan Somers 
43091972cfcSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
43191972cfcSAlan Somers 	expect_open(ino, 0, 1);
43291972cfcSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
43391972cfcSAlan Somers 	EXPECT_CALL(*m_mock, process(
43491972cfcSAlan Somers 		ResultOf([&](auto in) {
43591972cfcSAlan Somers 			uint32_t valid = FATTR_ATIME;
43691972cfcSAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
43791972cfcSAlan Somers 				in.header.nodeid == ino &&
43891972cfcSAlan Somers 				in.body.setattr.valid == valid &&
43991972cfcSAlan Somers 				(time_t)in.body.setattr.atime ==
44091972cfcSAlan Somers 					sb.st_atim.tv_sec &&
441d109559dSAlan Somers 				(long)in.body.setattr.atimensec ==
44291972cfcSAlan Somers 					sb.st_atim.tv_nsec);
44391972cfcSAlan Somers 		}, Eq(true)),
44491972cfcSAlan Somers 		_)
44591972cfcSAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
44691972cfcSAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
44791972cfcSAlan Somers 		out.body.attr.attr.ino = ino;
44891972cfcSAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
44991972cfcSAlan Somers 	})));
45091972cfcSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
45191972cfcSAlan Somers 	expect_release(ino, FuseTest::FH);
45291972cfcSAlan Somers 
45391972cfcSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
45491972cfcSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
45591972cfcSAlan Somers 
45691972cfcSAlan Somers 	/* Ensure atime will be different than during lookup */
45791972cfcSAlan Somers 	nap();
45891972cfcSAlan Somers 
45991972cfcSAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
46091972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb));
46191972cfcSAlan Somers 
46291972cfcSAlan Somers 	close(fd);
46391972cfcSAlan Somers }
46491972cfcSAlan Somers 
465fb619c94SAlan Somers /*
466fb619c94SAlan Somers  * When not using -o default_permissions, the daemon may make its own decisions
467fb619c94SAlan Somers  * regarding access permissions, and these may be unpredictable.  If it rejects
468fb619c94SAlan Somers  * our attempt to set atime, that should not cause close(2) to fail.
469fb619c94SAlan Somers  */
TEST_F(Read,atime_during_close_eacces)470fb619c94SAlan Somers TEST_F(Read, atime_during_close_eacces)
471fb619c94SAlan Somers {
472fb619c94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
473fb619c94SAlan Somers 	const char RELPATH[] = "some_file.txt";
474fb619c94SAlan Somers 	const char *CONTENTS = "abcdefgh";
475fb619c94SAlan Somers 	uint64_t ino = 42;
476fb619c94SAlan Somers 	int fd;
477fb619c94SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
478fb619c94SAlan Somers 	uint8_t buf[bufsize];
479fb619c94SAlan Somers 
480fb619c94SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
481fb619c94SAlan Somers 	expect_open(ino, 0, 1);
482fb619c94SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
483fb619c94SAlan Somers 	EXPECT_CALL(*m_mock, process(
484fb619c94SAlan Somers 		ResultOf([&](auto in) {
485fb619c94SAlan Somers 			uint32_t valid = FATTR_ATIME;
486fb619c94SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
487fb619c94SAlan Somers 				in.header.nodeid == ino &&
488fb619c94SAlan Somers 				in.body.setattr.valid == valid);
489fb619c94SAlan Somers 		}, Eq(true)),
490fb619c94SAlan Somers 		_)
491fb619c94SAlan Somers 	).WillOnce(Invoke(ReturnErrno(EACCES)));
492fb619c94SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
493fb619c94SAlan Somers 	expect_release(ino, FuseTest::FH);
494fb619c94SAlan Somers 
495fb619c94SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
496fb619c94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
497fb619c94SAlan Somers 
498fb619c94SAlan Somers 	/* Ensure atime will be different than during lookup */
499fb619c94SAlan Somers 	nap();
500fb619c94SAlan Somers 
501fb619c94SAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
502fb619c94SAlan Somers 
503fb619c94SAlan Somers 	ASSERT_EQ(0, close(fd));
504fb619c94SAlan Somers }
505fb619c94SAlan Somers 
50691972cfcSAlan Somers /* A cached atime should be flushed during FUSE_SETATTR */
TEST_F(Read,atime_during_setattr)50791972cfcSAlan Somers TEST_F(Read, atime_during_setattr)
50891972cfcSAlan Somers {
50991972cfcSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
51091972cfcSAlan Somers 	const char RELPATH[] = "some_file.txt";
51191972cfcSAlan Somers 	const char *CONTENTS = "abcdefgh";
51291972cfcSAlan Somers 	struct stat sb;
51391972cfcSAlan Somers 	uint64_t ino = 42;
51491972cfcSAlan Somers 	const mode_t newmode = 0755;
51591972cfcSAlan Somers 	int fd;
51691972cfcSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
51791972cfcSAlan Somers 	uint8_t buf[bufsize];
51891972cfcSAlan Somers 
51991972cfcSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
52091972cfcSAlan Somers 	expect_open(ino, 0, 1);
52191972cfcSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
52291972cfcSAlan Somers 	EXPECT_CALL(*m_mock, process(
52391972cfcSAlan Somers 		ResultOf([&](auto in) {
52491972cfcSAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_ATIME;
52591972cfcSAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
52691972cfcSAlan Somers 				in.header.nodeid == ino &&
52791972cfcSAlan Somers 				in.body.setattr.valid == valid &&
52891972cfcSAlan Somers 				(time_t)in.body.setattr.atime ==
52991972cfcSAlan Somers 					sb.st_atim.tv_sec &&
530d109559dSAlan Somers 				(long)in.body.setattr.atimensec ==
53191972cfcSAlan Somers 					sb.st_atim.tv_nsec);
53291972cfcSAlan Somers 		}, Eq(true)),
53391972cfcSAlan Somers 		_)
53491972cfcSAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
53591972cfcSAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
53691972cfcSAlan Somers 		out.body.attr.attr.ino = ino;
53791972cfcSAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
53891972cfcSAlan Somers 	})));
53991972cfcSAlan Somers 
54091972cfcSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
54191972cfcSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
54291972cfcSAlan Somers 
54391972cfcSAlan Somers 	/* Ensure atime will be different than during lookup */
54491972cfcSAlan Somers 	nap();
54591972cfcSAlan Somers 
54691972cfcSAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
54791972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb));
54891972cfcSAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
54991972cfcSAlan Somers 
55091972cfcSAlan Somers 	leak(fd);
55191972cfcSAlan Somers }
55291972cfcSAlan Somers 
5539821f1d3SAlan Somers /* 0-length reads shouldn't cause any confusion */
TEST_F(Read,direct_io_read_nothing)5549821f1d3SAlan Somers TEST_F(Read, direct_io_read_nothing)
5559821f1d3SAlan Somers {
5569821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5579821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5589821f1d3SAlan Somers 	uint64_t ino = 42;
5599821f1d3SAlan Somers 	int fd;
5609821f1d3SAlan Somers 	uint64_t offset = 100;
5619821f1d3SAlan Somers 	char buf[80];
5629821f1d3SAlan Somers 
5639821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, offset + 1000);
5649821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
5659821f1d3SAlan Somers 
5669821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
5679821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
5689821f1d3SAlan Somers 
5699821f1d3SAlan Somers 	ASSERT_EQ(0, pread(fd, buf, 0, offset)) << strerror(errno);
5707fc0921dSAlan Somers 	leak(fd);
5719821f1d3SAlan Somers }
5729821f1d3SAlan Somers 
5739821f1d3SAlan Somers /*
5749821f1d3SAlan Somers  * With direct_io, reads should not fill the cache.  They should go straight to
5759821f1d3SAlan Somers  * the daemon
5769821f1d3SAlan Somers  */
TEST_F(Read,direct_io_pread)5779821f1d3SAlan Somers TEST_F(Read, direct_io_pread)
5789821f1d3SAlan Somers {
5799821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5809821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5819821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
5829821f1d3SAlan Somers 	uint64_t ino = 42;
5839821f1d3SAlan Somers 	int fd;
5849821f1d3SAlan Somers 	uint64_t offset = 100;
5859821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5868e765737SAlan Somers 	uint8_t buf[bufsize];
5879821f1d3SAlan Somers 
5889821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, offset + bufsize);
5899821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
5909821f1d3SAlan Somers 	expect_read(ino, offset, bufsize, bufsize, CONTENTS);
5919821f1d3SAlan Somers 
5929821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
5939821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
5949821f1d3SAlan Somers 
5959821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
5969821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
597f8ebf1cdSAlan Somers 
598f8ebf1cdSAlan Somers 	// With FOPEN_DIRECT_IO, the cache should be bypassed.  The server will
599f8ebf1cdSAlan Somers 	// get a 2nd read request.
600f8ebf1cdSAlan Somers 	expect_read(ino, offset, bufsize, bufsize, CONTENTS);
601f8ebf1cdSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
602f8ebf1cdSAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
6037fc0921dSAlan Somers 	leak(fd);
6049821f1d3SAlan Somers }
6059821f1d3SAlan Somers 
6069821f1d3SAlan Somers /*
6079821f1d3SAlan Somers  * With direct_io, filesystems are allowed to return less data than is
6089821f1d3SAlan Somers  * requested.  fuse(4) should return a short read to userland.
6099821f1d3SAlan Somers  */
TEST_F(Read,direct_io_short_read)6109821f1d3SAlan Somers TEST_F(Read, direct_io_short_read)
6119821f1d3SAlan Somers {
6129821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6139821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6149821f1d3SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
6159821f1d3SAlan Somers 	uint64_t ino = 42;
6169821f1d3SAlan Somers 	int fd;
6179821f1d3SAlan Somers 	uint64_t offset = 100;
6189821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6199821f1d3SAlan Somers 	ssize_t halfbufsize = bufsize / 2;
6208e765737SAlan Somers 	uint8_t buf[bufsize];
6219821f1d3SAlan Somers 
6229821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, offset + bufsize);
6239821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
6249821f1d3SAlan Somers 	expect_read(ino, offset, bufsize, halfbufsize, CONTENTS);
6259821f1d3SAlan Somers 
6269821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
6279821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
6289821f1d3SAlan Somers 
6299821f1d3SAlan Somers 	ASSERT_EQ(halfbufsize, pread(fd, buf, bufsize, offset))
6309821f1d3SAlan Somers 		<< strerror(errno);
6319821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, halfbufsize));
6327fc0921dSAlan Somers 	leak(fd);
6339821f1d3SAlan Somers }
6349821f1d3SAlan Somers 
TEST_F(Read,eio)6359821f1d3SAlan Somers TEST_F(Read, eio)
6369821f1d3SAlan Somers {
6379821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6389821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6399821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6409821f1d3SAlan Somers 	uint64_t ino = 42;
6419821f1d3SAlan Somers 	int fd;
6429821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6438e765737SAlan Somers 	uint8_t buf[bufsize];
6449821f1d3SAlan Somers 
6459821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
6469821f1d3SAlan Somers 	expect_open(ino, 0, 1);
6479821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
6489821f1d3SAlan Somers 		ResultOf([=](auto in) {
64929edc611SAlan Somers 			return (in.header.opcode == FUSE_READ);
6509821f1d3SAlan Somers 		}, Eq(true)),
6519821f1d3SAlan Somers 		_)
6529821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnErrno(EIO)));
6539821f1d3SAlan Somers 
6549821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
6559821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
6569821f1d3SAlan Somers 
6579821f1d3SAlan Somers 	ASSERT_EQ(-1, read(fd, buf, bufsize)) << strerror(errno);
6589821f1d3SAlan Somers 	ASSERT_EQ(EIO, errno);
6597fc0921dSAlan Somers 	leak(fd);
6609821f1d3SAlan Somers }
6619821f1d3SAlan Somers 
6629821f1d3SAlan Somers /*
663aef22f2dSAlan Somers  * If the server returns a short read when direct io is not in use, that
664b9e20197SAlan Somers  * indicates EOF, because of a server-side truncation.  We should invalidate
665b9e20197SAlan Somers  * all cached attributes.  We may update the file size,
666aef22f2dSAlan Somers  */
TEST_F(Read,eof)667f8ebf1cdSAlan Somers TEST_F(Read, eof)
668aef22f2dSAlan Somers {
669aef22f2dSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
670aef22f2dSAlan Somers 	const char RELPATH[] = "some_file.txt";
671aef22f2dSAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
672aef22f2dSAlan Somers 	uint64_t ino = 42;
673aef22f2dSAlan Somers 	int fd;
674aef22f2dSAlan Somers 	uint64_t offset = 100;
675aef22f2dSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
676aef22f2dSAlan Somers 	ssize_t partbufsize = 3 * bufsize / 4;
677b9e20197SAlan Somers 	ssize_t r;
6788e765737SAlan Somers 	uint8_t buf[bufsize];
679aef22f2dSAlan Somers 	struct stat sb;
680aef22f2dSAlan Somers 
681aef22f2dSAlan Somers 	expect_lookup(RELPATH, ino, offset + bufsize);
682aef22f2dSAlan Somers 	expect_open(ino, 0, 1);
683aef22f2dSAlan Somers 	expect_read(ino, 0, offset + bufsize, offset + partbufsize, CONTENTS);
684b9e20197SAlan Somers 	expect_getattr(ino, offset + partbufsize);
685aef22f2dSAlan Somers 
686aef22f2dSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
687aef22f2dSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
688aef22f2dSAlan Somers 
689b9e20197SAlan Somers 	r = pread(fd, buf, bufsize, offset);
690b9e20197SAlan Somers 	ASSERT_LE(0, r) << strerror(errno);
691b9e20197SAlan Somers 	EXPECT_EQ(partbufsize, r) << strerror(errno);
692aef22f2dSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb));
693aef22f2dSAlan Somers 	EXPECT_EQ((off_t)(offset + partbufsize), sb.st_size);
6947fc0921dSAlan Somers 	leak(fd);
695aef22f2dSAlan Somers }
696aef22f2dSAlan Somers 
697f8ebf1cdSAlan Somers /* Like Read.eof, but causes an entire buffer to be invalidated */
TEST_F(Read,eof_of_whole_buffer)698f8ebf1cdSAlan Somers TEST_F(Read, eof_of_whole_buffer)
699aef22f2dSAlan Somers {
700aef22f2dSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
701aef22f2dSAlan Somers 	const char RELPATH[] = "some_file.txt";
702aef22f2dSAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
703aef22f2dSAlan Somers 	uint64_t ino = 42;
704aef22f2dSAlan Somers 	int fd;
705aef22f2dSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
706aef22f2dSAlan Somers 	off_t old_filesize = m_maxbcachebuf * 2 + bufsize;
7078e765737SAlan Somers 	uint8_t buf[bufsize];
708aef22f2dSAlan Somers 	struct stat sb;
709aef22f2dSAlan Somers 
710aef22f2dSAlan Somers 	expect_lookup(RELPATH, ino, old_filesize);
711aef22f2dSAlan Somers 	expect_open(ino, 0, 1);
712aef22f2dSAlan Somers 	expect_read(ino, 2 * m_maxbcachebuf, bufsize, bufsize, CONTENTS);
713aef22f2dSAlan Somers 	expect_read(ino, m_maxbcachebuf, m_maxbcachebuf, 0, CONTENTS);
714b9e20197SAlan Somers 	expect_getattr(ino, m_maxbcachebuf);
715aef22f2dSAlan Somers 
716aef22f2dSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
717aef22f2dSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
718aef22f2dSAlan Somers 
719aef22f2dSAlan Somers 	/* Cache the third block */
720aef22f2dSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, m_maxbcachebuf * 2))
721aef22f2dSAlan Somers 		<< strerror(errno);
722aef22f2dSAlan Somers 	/* Try to read the 2nd block, but it's past EOF */
723aef22f2dSAlan Somers 	ASSERT_EQ(0, pread(fd, buf, bufsize, m_maxbcachebuf))
724aef22f2dSAlan Somers 		<< strerror(errno);
725aef22f2dSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb));
726aef22f2dSAlan Somers 	EXPECT_EQ((off_t)(m_maxbcachebuf), sb.st_size);
7277fc0921dSAlan Somers 	leak(fd);
728aef22f2dSAlan Somers }
729aef22f2dSAlan Somers 
730aef22f2dSAlan Somers /*
7319821f1d3SAlan Somers  * With the keep_cache option, the kernel may keep its read cache across
7329821f1d3SAlan Somers  * multiple open(2)s.
7339821f1d3SAlan Somers  */
TEST_F(Read,keep_cache)734f8ebf1cdSAlan Somers TEST_F(Read, keep_cache)
7359821f1d3SAlan Somers {
7369821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7379821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7389821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7399821f1d3SAlan Somers 	uint64_t ino = 42;
7409821f1d3SAlan Somers 	int fd0, fd1;
7419821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7428e765737SAlan Somers 	uint8_t buf[bufsize];
7439821f1d3SAlan Somers 
7449821f1d3SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
745a7e81cb3SAlan Somers 	expect_open(ino, FOPEN_KEEP_CACHE, 2);
7469821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
7479821f1d3SAlan Somers 
7489821f1d3SAlan Somers 	fd0 = open(FULLPATH, O_RDONLY);
7499821f1d3SAlan Somers 	ASSERT_LE(0, fd0) << strerror(errno);
7509821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
7519821f1d3SAlan Somers 
752a7e81cb3SAlan Somers 	fd1 = open(FULLPATH, O_RDWR);
7539821f1d3SAlan Somers 	ASSERT_LE(0, fd1) << strerror(errno);
7549821f1d3SAlan Somers 
7559821f1d3SAlan Somers 	/*
7569821f1d3SAlan Somers 	 * This read should be serviced by cache, even though it's on the other
7579821f1d3SAlan Somers 	 * file descriptor
7589821f1d3SAlan Somers 	 */
7599821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd1, buf, bufsize)) << strerror(errno);
7609821f1d3SAlan Somers 
7617fc0921dSAlan Somers 	leak(fd0);
7627fc0921dSAlan Somers 	leak(fd1);
7639821f1d3SAlan Somers }
7649821f1d3SAlan Somers 
7659821f1d3SAlan Somers /*
7669821f1d3SAlan Somers  * Without the keep_cache option, the kernel should drop its read caches on
7679821f1d3SAlan Somers  * every open
7689821f1d3SAlan Somers  */
TEST_F(Read,keep_cache_disabled)7699821f1d3SAlan Somers TEST_F(Read, keep_cache_disabled)
7709821f1d3SAlan Somers {
7719821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7729821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7739821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7749821f1d3SAlan Somers 	uint64_t ino = 42;
7759821f1d3SAlan Somers 	int fd0, fd1;
7769821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7778e765737SAlan Somers 	uint8_t buf[bufsize];
7789821f1d3SAlan Somers 
7799821f1d3SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, bufsize, 2);
780a7e81cb3SAlan Somers 	expect_open(ino, 0, 2);
7819821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
7829821f1d3SAlan Somers 
7839821f1d3SAlan Somers 	fd0 = open(FULLPATH, O_RDONLY);
7849821f1d3SAlan Somers 	ASSERT_LE(0, fd0) << strerror(errno);
7859821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
7869821f1d3SAlan Somers 
787a7e81cb3SAlan Somers 	fd1 = open(FULLPATH, O_RDWR);
7889821f1d3SAlan Somers 	ASSERT_LE(0, fd1) << strerror(errno);
7899821f1d3SAlan Somers 
7909821f1d3SAlan Somers 	/*
7919821f1d3SAlan Somers 	 * This read should not be serviced by cache, even though it's on the
7929821f1d3SAlan Somers 	 * original file descriptor
7939821f1d3SAlan Somers 	 */
7949821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
7959821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd0, 0, SEEK_SET)) << strerror(errno);
7969821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd0, buf, bufsize)) << strerror(errno);
7979821f1d3SAlan Somers 
7987fc0921dSAlan Somers 	leak(fd0);
7997fc0921dSAlan Somers 	leak(fd1);
8009821f1d3SAlan Somers }
8019821f1d3SAlan Somers 
TEST_F(Read,mmap)802f8ebf1cdSAlan Somers TEST_F(Read, mmap)
8039821f1d3SAlan Somers {
8049821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8059821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8069821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
8079821f1d3SAlan Somers 	uint64_t ino = 42;
8089821f1d3SAlan Somers 	int fd;
8099821f1d3SAlan Somers 	ssize_t len;
810cc04566cSAlan Somers 	size_t bufsize = strlen(CONTENTS);
8119821f1d3SAlan Somers 	void *p;
8129821f1d3SAlan Somers 
8139821f1d3SAlan Somers 	len = getpagesize();
8149821f1d3SAlan Somers 
8159821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
8169821f1d3SAlan Somers 	expect_open(ino, 0, 1);
8179821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
8189821f1d3SAlan Somers 		ResultOf([=](auto in) {
81929edc611SAlan Somers 			return (in.header.opcode == FUSE_READ &&
82029edc611SAlan Somers 				in.header.nodeid == ino &&
82129edc611SAlan Somers 				in.body.read.fh == Read::FH &&
82229edc611SAlan Somers 				in.body.read.offset == 0 &&
823f74b33d9SAlan Somers 				in.body.read.size == bufsize);
8249821f1d3SAlan Somers 		}, Eq(true)),
8259821f1d3SAlan Somers 		_)
82629edc611SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
82729edc611SAlan Somers 		out.header.len = sizeof(struct fuse_out_header) + bufsize;
82829edc611SAlan Somers 		memmove(out.body.bytes, CONTENTS, bufsize);
8299821f1d3SAlan Somers 	})));
8309821f1d3SAlan Somers 
8319821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
8329821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8339821f1d3SAlan Somers 
8349821f1d3SAlan Somers 	p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
8359821f1d3SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
8369821f1d3SAlan Somers 
8379821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
8389821f1d3SAlan Somers 
8399821f1d3SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
8407fc0921dSAlan Somers 	leak(fd);
8419821f1d3SAlan Somers }
8429821f1d3SAlan Somers 
84391972cfcSAlan Somers /*
84491972cfcSAlan Somers  * The kernel should not update the cached atime attribute during a read, if
84591972cfcSAlan Somers  * MNT_NOATIME is used.
84691972cfcSAlan Somers  */
TEST_F(ReadNoatime,atime)84791972cfcSAlan Somers TEST_F(ReadNoatime, atime)
84891972cfcSAlan Somers {
84991972cfcSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
85091972cfcSAlan Somers 	const char RELPATH[] = "some_file.txt";
85191972cfcSAlan Somers 	const char *CONTENTS = "abcdefgh";
85291972cfcSAlan Somers 	struct stat sb1, sb2;
85391972cfcSAlan Somers 	uint64_t ino = 42;
85491972cfcSAlan Somers 	int fd;
85591972cfcSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
85691972cfcSAlan Somers 	uint8_t buf[bufsize];
85791972cfcSAlan Somers 
85891972cfcSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
85991972cfcSAlan Somers 	expect_open(ino, 0, 1);
86091972cfcSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
86191972cfcSAlan Somers 
86291972cfcSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
86391972cfcSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
86491972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1));
86591972cfcSAlan Somers 
86691972cfcSAlan Somers 	nap();
86791972cfcSAlan Somers 
86891972cfcSAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
86991972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb2));
87091972cfcSAlan Somers 
87191972cfcSAlan Somers 	/* The kernel should not update atime during read */
87291972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==));
87391972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
87491972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
87591972cfcSAlan Somers 
87691972cfcSAlan Somers 	leak(fd);
87791972cfcSAlan Somers }
87891972cfcSAlan Somers 
87991972cfcSAlan Somers /*
88091972cfcSAlan Somers  * The kernel should not update the cached atime attribute during a cached
88191972cfcSAlan Somers  * read, if MNT_NOATIME is used.
88291972cfcSAlan Somers  */
TEST_F(ReadNoatime,atime_cached)88391972cfcSAlan Somers TEST_F(ReadNoatime, atime_cached)
88491972cfcSAlan Somers {
88591972cfcSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
88691972cfcSAlan Somers 	const char RELPATH[] = "some_file.txt";
88791972cfcSAlan Somers 	const char *CONTENTS = "abcdefgh";
88891972cfcSAlan Somers 	struct stat sb1, sb2;
88991972cfcSAlan Somers 	uint64_t ino = 42;
89091972cfcSAlan Somers 	int fd;
89191972cfcSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
89291972cfcSAlan Somers 	uint8_t buf[bufsize];
89391972cfcSAlan Somers 
89491972cfcSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
89591972cfcSAlan Somers 	expect_open(ino, 0, 1);
89691972cfcSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
89791972cfcSAlan Somers 
89891972cfcSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
89991972cfcSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
90091972cfcSAlan Somers 
90191972cfcSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
90291972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1));
90391972cfcSAlan Somers 
90491972cfcSAlan Somers 	nap();
90591972cfcSAlan Somers 
90691972cfcSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, 0)) << strerror(errno);
90791972cfcSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb2));
90891972cfcSAlan Somers 
90991972cfcSAlan Somers 	/* The kernel should automatically update atime during read */
91091972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_atim, &sb2.st_atim, ==));
91191972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_ctim, &sb2.st_ctim, ==));
91291972cfcSAlan Somers 	EXPECT_TRUE(timespeccmp(&sb1.st_mtim, &sb2.st_mtim, ==));
91391972cfcSAlan Somers 
91491972cfcSAlan Somers 	leak(fd);
91591972cfcSAlan Somers }
91691972cfcSAlan Somers 
9174f917847SAlan Somers /* Read of an mmap()ed file fails */
TEST_F(ReadSigbus,mmap_eio)9184f917847SAlan Somers TEST_F(ReadSigbus, mmap_eio)
9194f917847SAlan Somers {
9204f917847SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9214f917847SAlan Somers 	const char RELPATH[] = "some_file.txt";
9224f917847SAlan Somers 	const char *CONTENTS = "abcdefgh";
9234f917847SAlan Somers 	struct sigaction sa;
9244f917847SAlan Somers 	uint64_t ino = 42;
9254f917847SAlan Somers 	int fd;
9264f917847SAlan Somers 	ssize_t len;
9274f917847SAlan Somers 	size_t bufsize = strlen(CONTENTS);
9284f917847SAlan Somers 	void *p;
9294f917847SAlan Somers 
9304f917847SAlan Somers 	len = getpagesize();
9314f917847SAlan Somers 
9324f917847SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
9334f917847SAlan Somers 	expect_open(ino, 0, 1);
9344f917847SAlan Somers 	EXPECT_CALL(*m_mock, process(
9354f917847SAlan Somers 		ResultOf([=](auto in) {
9364f917847SAlan Somers 			return (in.header.opcode == FUSE_READ &&
9374f917847SAlan Somers 				in.header.nodeid == ino &&
9384f917847SAlan Somers 				in.body.read.fh == Read::FH);
9394f917847SAlan Somers 		}, Eq(true)),
9404f917847SAlan Somers 		_)
9414f917847SAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
9424f917847SAlan Somers 
9434f917847SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
9444f917847SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
9454f917847SAlan Somers 
9464f917847SAlan Somers 	p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
9474f917847SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
9484f917847SAlan Somers 
9494f917847SAlan Somers 	/* Accessing the mapped page should return SIGBUS.  */
9504f917847SAlan Somers 
9514f917847SAlan Somers 	bzero(&sa, sizeof(sa));
9524f917847SAlan Somers 	sa.sa_handler = SIG_DFL;
9534f917847SAlan Somers 	sa.sa_sigaction = handle_sigbus;
9544f917847SAlan Somers 	sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
9554f917847SAlan Somers 	ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
9564f917847SAlan Somers 	if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
9574f917847SAlan Somers 		atomic_signal_fence(std::memory_order::memory_order_seq_cst);
9584f917847SAlan Somers 		volatile char x __unused = *(volatile char*)p;
9594f917847SAlan Somers 		FAIL() << "shouldn't get here";
9604f917847SAlan Somers 	}
9614f917847SAlan Somers 
9623fcbde5eSKonstantin Belousov 	ASSERT_EQ(p, ReadSigbus::s_si_addr);
9634f917847SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
9644f917847SAlan Somers 	leak(fd);
9654f917847SAlan Somers }
9664f917847SAlan Somers 
9679821f1d3SAlan Somers /*
968b9e20197SAlan Somers  * A read via mmap comes up short, indicating that the file was truncated
969b9e20197SAlan Somers  * server-side.
970b9e20197SAlan Somers  */
TEST_F(Read,mmap_eof)971f8ebf1cdSAlan Somers TEST_F(Read, mmap_eof)
972b9e20197SAlan Somers {
973b9e20197SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
974b9e20197SAlan Somers 	const char RELPATH[] = "some_file.txt";
975b9e20197SAlan Somers 	const char *CONTENTS = "abcdefgh";
976b9e20197SAlan Somers 	uint64_t ino = 42;
977b9e20197SAlan Somers 	int fd;
978b9e20197SAlan Somers 	ssize_t len;
979b9e20197SAlan Somers 	size_t bufsize = strlen(CONTENTS);
980b9e20197SAlan Somers 	struct stat sb;
981b9e20197SAlan Somers 	void *p;
982b9e20197SAlan Somers 
983b9e20197SAlan Somers 	len = getpagesize();
984b9e20197SAlan Somers 
985f74b33d9SAlan Somers 	expect_lookup(RELPATH, ino, m_maxbcachebuf);
986b9e20197SAlan Somers 	expect_open(ino, 0, 1);
987b9e20197SAlan Somers 	EXPECT_CALL(*m_mock, process(
988b9e20197SAlan Somers 		ResultOf([=](auto in) {
989b9e20197SAlan Somers 			return (in.header.opcode == FUSE_READ &&
990b9e20197SAlan Somers 				in.header.nodeid == ino &&
991b9e20197SAlan Somers 				in.body.read.fh == Read::FH &&
992b9e20197SAlan Somers 				in.body.read.offset == 0 &&
993f74b33d9SAlan Somers 				in.body.read.size == (uint32_t)m_maxbcachebuf);
994b9e20197SAlan Somers 		}, Eq(true)),
995b9e20197SAlan Somers 		_)
996b9e20197SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
997b9e20197SAlan Somers 		out.header.len = sizeof(struct fuse_out_header) + bufsize;
998b9e20197SAlan Somers 		memmove(out.body.bytes, CONTENTS, bufsize);
999b9e20197SAlan Somers 	})));
1000b9e20197SAlan Somers 	expect_getattr(ino, bufsize);
1001b9e20197SAlan Somers 
1002b9e20197SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
1003b9e20197SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1004b9e20197SAlan Somers 
1005b9e20197SAlan Somers 	p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
1006b9e20197SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1007b9e20197SAlan Somers 
1008b9e20197SAlan Somers 	/* The file size should be automatically truncated */
1009b9e20197SAlan Somers 	ASSERT_EQ(0, memcmp(p, CONTENTS, bufsize));
1010b9e20197SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1011b9e20197SAlan Somers 	EXPECT_EQ((off_t)bufsize, sb.st_size);
1012b9e20197SAlan Somers 
1013b9e20197SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
10147fc0921dSAlan Somers 	leak(fd);
1015b9e20197SAlan Somers }
1016b9e20197SAlan Somers 
1017b9e20197SAlan Somers /*
10184f917847SAlan Somers  * During VOP_GETPAGES, the FUSE server fails a FUSE_GETATTR operation.  This
10194f917847SAlan Somers  * almost certainly indicates a buggy FUSE server, and our goal should be not
10204f917847SAlan Somers  * to panic.  Instead, generate SIGBUS.
10214f917847SAlan Somers  */
TEST_F(ReadSigbus,mmap_getblksz_fail)10224f917847SAlan Somers TEST_F(ReadSigbus, mmap_getblksz_fail)
10234f917847SAlan Somers {
10244f917847SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
10254f917847SAlan Somers 	const char RELPATH[] = "some_file.txt";
10264f917847SAlan Somers 	const char *CONTENTS = "abcdefgh";
10274f917847SAlan Somers 	struct sigaction sa;
10284f917847SAlan Somers 	Sequence seq;
10294f917847SAlan Somers 	uint64_t ino = 42;
10304f917847SAlan Somers 	int fd;
10314f917847SAlan Somers 	ssize_t len;
10324f917847SAlan Somers 	size_t bufsize = strlen(CONTENTS);
10334f917847SAlan Somers 	mode_t mode = S_IFREG | 0644;
10344f917847SAlan Somers 	void *p;
10354f917847SAlan Somers 
10364f917847SAlan Somers 	len = getpagesize();
10374f917847SAlan Somers 
10384f917847SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, mode, bufsize, 1, 0);
10394f917847SAlan Somers 	/* Expect two GETATTR calls that succeed, followed by one that fail. */
10404f917847SAlan Somers 	EXPECT_CALL(*m_mock, process(
10414f917847SAlan Somers 		ResultOf([=](auto in) {
10424f917847SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
10434f917847SAlan Somers 				in.header.nodeid == ino);
10444f917847SAlan Somers 		}, Eq(true)),
10454f917847SAlan Somers 		_)
10464f917847SAlan Somers 	).Times(2)
10474f917847SAlan Somers 	.InSequence(seq)
10484f917847SAlan Somers 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
10494f917847SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
10504f917847SAlan Somers 		out.body.attr.attr.ino = ino;
10514f917847SAlan Somers 		out.body.attr.attr.mode = mode;
10524f917847SAlan Somers 		out.body.attr.attr.size = bufsize;
10534f917847SAlan Somers 		out.body.attr.attr_valid = 0;
10544f917847SAlan Somers 	})));
10554f917847SAlan Somers 	EXPECT_CALL(*m_mock, process(
10564f917847SAlan Somers 		ResultOf([=](auto in) {
10574f917847SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
10584f917847SAlan Somers 				in.header.nodeid == ino);
10594f917847SAlan Somers 		}, Eq(true)),
10604f917847SAlan Somers 		_)
10614f917847SAlan Somers 	).InSequence(seq)
10624f917847SAlan Somers 	.WillRepeatedly(Invoke(ReturnErrno(EIO)));
10634f917847SAlan Somers 	expect_open(ino, 0, 1);
10644f917847SAlan Somers 	EXPECT_CALL(*m_mock, process(
10654f917847SAlan Somers 		ResultOf([=](auto in) {
10664f917847SAlan Somers 			return (in.header.opcode == FUSE_READ);
10674f917847SAlan Somers 		}, Eq(true)),
10684f917847SAlan Somers 		_)
10694f917847SAlan Somers 	).Times(0);
10704f917847SAlan Somers 
10714f917847SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
10724f917847SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
10734f917847SAlan Somers 
10744f917847SAlan Somers 	p = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
10754f917847SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
10764f917847SAlan Somers 
10774f917847SAlan Somers 	/* Accessing the mapped page should return SIGBUS.  */
10784f917847SAlan Somers 	bzero(&sa, sizeof(sa));
10794f917847SAlan Somers 	sa.sa_handler = SIG_DFL;
10804f917847SAlan Somers 	sa.sa_sigaction = handle_sigbus;
10814f917847SAlan Somers 	sa.sa_flags = SA_RESETHAND | SA_SIGINFO;
10824f917847SAlan Somers 	ASSERT_EQ(0, sigaction(SIGBUS, &sa, NULL)) << strerror(errno);
10834f917847SAlan Somers 	if (setjmp(ReadSigbus::s_jmpbuf) == 0) {
10844f917847SAlan Somers 		atomic_signal_fence(std::memory_order::memory_order_seq_cst);
10854f917847SAlan Somers 		volatile char x __unused = *(volatile char*)p;
10864f917847SAlan Somers 		FAIL() << "shouldn't get here";
10874f917847SAlan Somers 	}
10884f917847SAlan Somers 
10893fcbde5eSKonstantin Belousov 	ASSERT_EQ(p, ReadSigbus::s_si_addr);
10904f917847SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
10914f917847SAlan Somers 	leak(fd);
10924f917847SAlan Somers }
10934f917847SAlan Somers 
10944f917847SAlan Somers /*
10959821f1d3SAlan Somers  * Just as when FOPEN_DIRECT_IO is used, reads with O_DIRECT should bypass
10969821f1d3SAlan Somers  * cache and to straight to the daemon
10979821f1d3SAlan Somers  */
TEST_F(Read,o_direct)10989821f1d3SAlan Somers TEST_F(Read, o_direct)
10999821f1d3SAlan Somers {
11009821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
11019821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
11029821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
11039821f1d3SAlan Somers 	uint64_t ino = 42;
11049821f1d3SAlan Somers 	int fd;
11059821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
11068e765737SAlan Somers 	uint8_t buf[bufsize];
11079821f1d3SAlan Somers 
11089821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
11099821f1d3SAlan Somers 	expect_open(ino, 0, 1);
11109821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
11119821f1d3SAlan Somers 
11129821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
11139821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
11149821f1d3SAlan Somers 
11159821f1d3SAlan Somers 	// Fill the cache
11169821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
11179821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
11189821f1d3SAlan Somers 
11199821f1d3SAlan Somers 	// Reads with o_direct should bypass the cache
11209821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
11219821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
11229821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
11239821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
11249821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
11259821f1d3SAlan Somers 
11267fc0921dSAlan Somers 	leak(fd);
11279821f1d3SAlan Somers }
11289821f1d3SAlan Somers 
TEST_F(Read,pread)11299821f1d3SAlan Somers TEST_F(Read, pread)
11309821f1d3SAlan Somers {
11319821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
11329821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
11339821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
11349821f1d3SAlan Somers 	uint64_t ino = 42;
11359821f1d3SAlan Somers 	int fd;
11369821f1d3SAlan Somers 	/*
11379821f1d3SAlan Somers 	 * Set offset to a maxbcachebuf boundary so we'll be sure what offset
11389821f1d3SAlan Somers 	 * to read from.  Without this, the read might start at a lower offset.
11399821f1d3SAlan Somers 	 */
11409821f1d3SAlan Somers 	uint64_t offset = m_maxbcachebuf;
11419821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
11428e765737SAlan Somers 	uint8_t buf[bufsize];
11439821f1d3SAlan Somers 
11449821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, offset + bufsize);
11459821f1d3SAlan Somers 	expect_open(ino, 0, 1);
11469821f1d3SAlan Somers 	expect_read(ino, offset, bufsize, bufsize, CONTENTS);
11479821f1d3SAlan Somers 
11489821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
11499821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
11509821f1d3SAlan Somers 
11519821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pread(fd, buf, bufsize, offset)) << strerror(errno);
11529821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
11537fc0921dSAlan Somers 	leak(fd);
11549821f1d3SAlan Somers }
11559821f1d3SAlan Somers 
TEST_F(Read,read)11569821f1d3SAlan Somers TEST_F(Read, read)
11579821f1d3SAlan Somers {
11589821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
11599821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
11609821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
11619821f1d3SAlan Somers 	uint64_t ino = 42;
11629821f1d3SAlan Somers 	int fd;
11639821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
11648e765737SAlan Somers 	uint8_t buf[bufsize];
11659821f1d3SAlan Somers 
11669821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
11679821f1d3SAlan Somers 	expect_open(ino, 0, 1);
11689821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
11699821f1d3SAlan Somers 
11709821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
11719821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
11729821f1d3SAlan Somers 
11739821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
11749821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
11759821f1d3SAlan Somers 
11767fc0921dSAlan Somers 	leak(fd);
11779821f1d3SAlan Somers }
11789821f1d3SAlan Somers 
TEST_F(Read_7_8,read)117916bd2d47SAlan Somers TEST_F(Read_7_8, read)
118016bd2d47SAlan Somers {
118116bd2d47SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
118216bd2d47SAlan Somers 	const char RELPATH[] = "some_file.txt";
118316bd2d47SAlan Somers 	const char *CONTENTS = "abcdefgh";
118416bd2d47SAlan Somers 	uint64_t ino = 42;
118516bd2d47SAlan Somers 	int fd;
118616bd2d47SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
11878e765737SAlan Somers 	uint8_t buf[bufsize];
118816bd2d47SAlan Somers 
118916bd2d47SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
119016bd2d47SAlan Somers 	expect_open(ino, 0, 1);
119116bd2d47SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
119216bd2d47SAlan Somers 
119316bd2d47SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
119416bd2d47SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
119516bd2d47SAlan Somers 
119616bd2d47SAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
119716bd2d47SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
119816bd2d47SAlan Somers 
11997fc0921dSAlan Somers 	leak(fd);
120016bd2d47SAlan Somers }
120116bd2d47SAlan Somers 
1202eadd12d3SAlan Somers /*
1203eadd12d3SAlan Somers  * If cacheing is enabled, the kernel should try to read an entire cache block
1204eadd12d3SAlan Somers  * at a time.
1205eadd12d3SAlan Somers  */
TEST_F(Read,cache_block)1206f8ebf1cdSAlan Somers TEST_F(Read, cache_block)
12079821f1d3SAlan Somers {
12089821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
12099821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
12109821f1d3SAlan Somers 	const char *CONTENTS0 = "abcdefghijklmnop";
12119821f1d3SAlan Somers 	uint64_t ino = 42;
12129821f1d3SAlan Somers 	int fd;
12139821f1d3SAlan Somers 	ssize_t bufsize = 8;
1214eadd12d3SAlan Somers 	ssize_t filesize = m_maxbcachebuf * 2;
12159821f1d3SAlan Somers 	char *contents;
12169821f1d3SAlan Somers 	char buf[bufsize];
12179821f1d3SAlan Somers 	const char *contents1 = CONTENTS0 + bufsize;
12189821f1d3SAlan Somers 
12198bae22bbSAlan Somers 	contents = new char[filesize]();
12209821f1d3SAlan Somers 	memmove(contents, CONTENTS0, strlen(CONTENTS0));
12219821f1d3SAlan Somers 
12229821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, filesize);
12239821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1224eadd12d3SAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf,
12259821f1d3SAlan Somers 		contents);
12269821f1d3SAlan Somers 
12279821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
12289821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
12299821f1d3SAlan Somers 
12309821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
12319821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS0, bufsize));
12329821f1d3SAlan Somers 
12339821f1d3SAlan Somers 	/* A subsequent read should be serviced by cache */
12349821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
12359821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, contents1, bufsize));
12367fc0921dSAlan Somers 	leak(fd);
12378bae22bbSAlan Somers 	delete[] contents;
12389821f1d3SAlan Somers }
12399821f1d3SAlan Somers 
12409821f1d3SAlan Somers /* Reading with sendfile should work (though it obviously won't be 0-copy) */
TEST_F(Read,sendfile)1241f8ebf1cdSAlan Somers TEST_F(Read, sendfile)
12429821f1d3SAlan Somers {
12439821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
12449821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
12459821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
12469821f1d3SAlan Somers 	uint64_t ino = 42;
12479821f1d3SAlan Somers 	int fd;
1248cc04566cSAlan Somers 	size_t bufsize = strlen(CONTENTS);
12498e765737SAlan Somers 	uint8_t buf[bufsize];
12509821f1d3SAlan Somers 	int sp[2];
12519821f1d3SAlan Somers 	off_t sbytes;
12529821f1d3SAlan Somers 
12539821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
12549821f1d3SAlan Somers 	expect_open(ino, 0, 1);
12559821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
12569821f1d3SAlan Somers 		ResultOf([=](auto in) {
125729edc611SAlan Somers 			return (in.header.opcode == FUSE_READ &&
125829edc611SAlan Somers 				in.header.nodeid == ino &&
125929edc611SAlan Somers 				in.body.read.fh == Read::FH &&
126029edc611SAlan Somers 				in.body.read.offset == 0 &&
1261f74b33d9SAlan Somers 				in.body.read.size == bufsize);
12629821f1d3SAlan Somers 		}, Eq(true)),
12639821f1d3SAlan Somers 		_)
126429edc611SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
126529edc611SAlan Somers 		out.header.len = sizeof(struct fuse_out_header) + bufsize;
126629edc611SAlan Somers 		memmove(out.body.bytes, CONTENTS, bufsize);
12679821f1d3SAlan Somers 	})));
12689821f1d3SAlan Somers 
12699821f1d3SAlan Somers 	ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
12709821f1d3SAlan Somers 		<< strerror(errno);
12719821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
12729821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
12739821f1d3SAlan Somers 
12749821f1d3SAlan Somers 	ASSERT_EQ(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0))
12759821f1d3SAlan Somers 		<< strerror(errno);
127629edc611SAlan Somers 	ASSERT_EQ(static_cast<ssize_t>(bufsize), read(sp[0], buf, bufsize))
1277cc04566cSAlan Somers 		<< strerror(errno);
12789821f1d3SAlan Somers 	ASSERT_EQ(0, memcmp(buf, CONTENTS, bufsize));
12799821f1d3SAlan Somers 
12809821f1d3SAlan Somers 	close(sp[1]);
12819821f1d3SAlan Somers 	close(sp[0]);
12827fc0921dSAlan Somers 	leak(fd);
12839821f1d3SAlan Somers }
12849821f1d3SAlan Somers 
12859821f1d3SAlan Somers /* sendfile should fail gracefully if fuse declines the read */
12869821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236466 */
TEST_F(Read,sendfile_eio)1287fca79580SAlan Somers TEST_F(Read, sendfile_eio)
12889821f1d3SAlan Somers {
12899821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
12909821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
12919821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
12929821f1d3SAlan Somers 	uint64_t ino = 42;
12939821f1d3SAlan Somers 	int fd;
12949821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
12959821f1d3SAlan Somers 	int sp[2];
12969821f1d3SAlan Somers 	off_t sbytes;
12979821f1d3SAlan Somers 
12989821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
12999821f1d3SAlan Somers 	expect_open(ino, 0, 1);
13009821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
13019821f1d3SAlan Somers 		ResultOf([=](auto in) {
130229edc611SAlan Somers 			return (in.header.opcode == FUSE_READ);
13039821f1d3SAlan Somers 		}, Eq(true)),
13049821f1d3SAlan Somers 		_)
13059821f1d3SAlan Somers 	).WillOnce(Invoke(ReturnErrno(EIO)));
13069821f1d3SAlan Somers 
13079821f1d3SAlan Somers 	ASSERT_EQ(0, socketpair(PF_LOCAL, SOCK_STREAM, 0, sp))
13089821f1d3SAlan Somers 		<< strerror(errno);
13099821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
13109821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
13119821f1d3SAlan Somers 
13129821f1d3SAlan Somers 	ASSERT_NE(0, sendfile(fd, sp[1], 0, bufsize, NULL, &sbytes, 0));
13139821f1d3SAlan Somers 
13149821f1d3SAlan Somers 	close(sp[1]);
13159821f1d3SAlan Somers 	close(sp[0]);
13167fc0921dSAlan Somers 	leak(fd);
13179821f1d3SAlan Somers }
13189821f1d3SAlan Somers 
1319402b609cSAlan Somers /*
1320a1c9f4adSAlan Somers  * Sequential reads should use readahead.  And if allowed, large reads should
1321a1c9f4adSAlan Somers  * be clustered.
1322402b609cSAlan Somers  */
TEST_P(ReadAhead,readahead)1323a1c9f4adSAlan Somers TEST_P(ReadAhead, readahead) {
1324402b609cSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1325402b609cSAlan Somers 	const char RELPATH[] = "some_file.txt";
1326402b609cSAlan Somers 	uint64_t ino = 42;
1327a1c9f4adSAlan Somers 	int fd, maxcontig, clustersize;
1328402b609cSAlan Somers 	ssize_t bufsize = 4 * m_maxbcachebuf;
1329402b609cSAlan Somers 	ssize_t filesize = bufsize;
1330402b609cSAlan Somers 	uint64_t len;
1331402b609cSAlan Somers 	char *rbuf, *contents;
1332402b609cSAlan Somers 	off_t offs;
1333402b609cSAlan Somers 
13348bae22bbSAlan Somers 	contents = new char[filesize];
1335402b609cSAlan Somers 	memset(contents, 'X', filesize);
13368bae22bbSAlan Somers 	rbuf = new char[bufsize]();
1337402b609cSAlan Somers 
1338402b609cSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
1339402b609cSAlan Somers 	expect_open(ino, 0, 1);
1340402b609cSAlan Somers 	maxcontig = m_noclusterr ? m_maxbcachebuf :
1341f2704f05SAlan Somers 		m_maxbcachebuf + m_maxreadahead;
1342f2704f05SAlan Somers 	clustersize = MIN(maxcontig, m_maxphys);
1343a1c9f4adSAlan Somers 	for (offs = 0; offs < bufsize; offs += clustersize) {
1344a1c9f4adSAlan Somers 		len = std::min((size_t)clustersize, (size_t)(filesize - offs));
1345402b609cSAlan Somers 		expect_read(ino, offs, len, len, contents + offs);
1346402b609cSAlan Somers 	}
1347402b609cSAlan Somers 
1348402b609cSAlan Somers 	fd = open(FULLPATH, O_RDONLY);
1349402b609cSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1350402b609cSAlan Somers 
1351402b609cSAlan Somers 	/* Set the internal readahead counter to a "large" value */
1352402b609cSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_READAHEAD, 1'000'000'000)) << strerror(errno);
1353402b609cSAlan Somers 
1354402b609cSAlan Somers 	ASSERT_EQ(bufsize, read(fd, rbuf, bufsize)) << strerror(errno);
1355402b609cSAlan Somers 	ASSERT_EQ(0, memcmp(rbuf, contents, bufsize));
1356402b609cSAlan Somers 
13577fc0921dSAlan Somers 	leak(fd);
13588bae22bbSAlan Somers 	delete[] rbuf;
13598bae22bbSAlan Somers 	delete[] contents;
1360402b609cSAlan Somers }
1361402b609cSAlan Somers 
1362811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(RA, ReadAhead,
1363f2704f05SAlan Somers 	Values(tuple<bool, int>(false, 0),
1364f2704f05SAlan Somers 	       tuple<bool, int>(false, 1),
1365f2704f05SAlan Somers 	       tuple<bool, int>(false, 2),
1366f2704f05SAlan Somers 	       tuple<bool, int>(false, 3),
1367f2704f05SAlan Somers 	       tuple<bool, int>(true, 0),
1368f2704f05SAlan Somers 	       tuple<bool, int>(true, 1),
1369f2704f05SAlan Somers 	       tuple<bool, int>(true, 2)));
137091972cfcSAlan Somers 
1371fb619c94SAlan Somers /* With read-only mounts, fuse should never update atime during close */
TEST_F(RofsRead,atime_during_close)1372fb619c94SAlan Somers TEST_F(RofsRead, atime_during_close)
1373fb619c94SAlan Somers {
1374fb619c94SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1375fb619c94SAlan Somers 	const char RELPATH[] = "some_file.txt";
1376fb619c94SAlan Somers 	const char *CONTENTS = "abcdefgh";
1377fb619c94SAlan Somers 	uint64_t ino = 42;
1378fb619c94SAlan Somers 	int fd;
1379fb619c94SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1380fb619c94SAlan Somers 	uint8_t buf[bufsize];
1381fb619c94SAlan Somers 
1382fb619c94SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
1383fb619c94SAlan Somers 	expect_open(ino, 0, 1);
1384fb619c94SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1385fb619c94SAlan Somers 	EXPECT_CALL(*m_mock, process(
1386fb619c94SAlan Somers 		ResultOf([&](auto in) {
1387fb619c94SAlan Somers 			return (in.header.opcode == FUSE_SETATTR);
1388fb619c94SAlan Somers 		}, Eq(true)),
1389fb619c94SAlan Somers 		_)
1390fb619c94SAlan Somers 	).Times(0);
1391fb619c94SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
1392fb619c94SAlan Somers 	expect_release(ino, FuseTest::FH);
1393fb619c94SAlan Somers 
1394fb619c94SAlan Somers 	fd = open(FULLPATH, O_RDONLY);
1395fb619c94SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1396fb619c94SAlan Somers 
1397fb619c94SAlan Somers 	/* Ensure atime will be different than during lookup */
1398fb619c94SAlan Somers 	nap();
1399fb619c94SAlan Somers 
1400fb619c94SAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
1401fb619c94SAlan Somers 
1402fb619c94SAlan Somers 	close(fd);
1403fb619c94SAlan Somers }
1404fb619c94SAlan Somers 
140591972cfcSAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */
TEST_P(TimeGran,atime_during_setattr)140691972cfcSAlan Somers TEST_P(TimeGran, atime_during_setattr)
140791972cfcSAlan Somers {
140891972cfcSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
140991972cfcSAlan Somers 	const char RELPATH[] = "some_file.txt";
141091972cfcSAlan Somers 	const char *CONTENTS = "abcdefgh";
141191972cfcSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
141291972cfcSAlan Somers 	uint8_t buf[bufsize];
141391972cfcSAlan Somers 	uint64_t ino = 42;
141491972cfcSAlan Somers 	const mode_t newmode = 0755;
141591972cfcSAlan Somers 	int fd;
141691972cfcSAlan Somers 
141791972cfcSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
141891972cfcSAlan Somers 	expect_open(ino, 0, 1);
141991972cfcSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
142091972cfcSAlan Somers 	EXPECT_CALL(*m_mock, process(
142191972cfcSAlan Somers 		ResultOf([=](auto in) {
142291972cfcSAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_ATIME;
142391972cfcSAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
142491972cfcSAlan Somers 				in.header.nodeid == ino &&
142591972cfcSAlan Somers 				in.body.setattr.valid == valid &&
142691972cfcSAlan Somers 				in.body.setattr.atimensec % m_time_gran == 0);
142791972cfcSAlan Somers 		}, Eq(true)),
142891972cfcSAlan Somers 		_)
142991972cfcSAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
143091972cfcSAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
143191972cfcSAlan Somers 		out.body.attr.attr.ino = ino;
143291972cfcSAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
143391972cfcSAlan Somers 	})));
143491972cfcSAlan Somers 
143591972cfcSAlan Somers 	fd = open(FULLPATH, O_RDWR);
143691972cfcSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
143791972cfcSAlan Somers 
143891972cfcSAlan Somers 	ASSERT_EQ(bufsize, read(fd, buf, bufsize)) << strerror(errno);
143991972cfcSAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
144091972cfcSAlan Somers 
144191972cfcSAlan Somers 	leak(fd);
144291972cfcSAlan Somers }
144391972cfcSAlan Somers 
1444811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(TG, TimeGran, Range(0u, 10u));
1445