xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision be280f60dd8e8ef765a76966aac9c6ca7d6264d0)
19821f1d3SAlan Somers /*-
29821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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.
291fa8ebfbSAlan Somers  *
301fa8ebfbSAlan Somers  * $FreeBSD$
319821f1d3SAlan Somers  */
329821f1d3SAlan Somers 
339821f1d3SAlan Somers extern "C" {
348eecd9ceSAlan Somers #include <sys/param.h>
359821f1d3SAlan Somers #include <sys/mman.h>
36a639731bSAlan Somers #include <sys/resource.h>
379821f1d3SAlan Somers #include <sys/stat.h>
38a639731bSAlan Somers #include <sys/time.h>
399821f1d3SAlan Somers #include <sys/uio.h>
409821f1d3SAlan Somers 
419821f1d3SAlan Somers #include <aio.h>
429821f1d3SAlan Somers #include <fcntl.h>
43a639731bSAlan Somers #include <signal.h>
449821f1d3SAlan Somers #include <unistd.h>
459821f1d3SAlan Somers }
469821f1d3SAlan Somers 
479821f1d3SAlan Somers #include "mockfs.hh"
489821f1d3SAlan Somers #include "utils.hh"
499821f1d3SAlan Somers 
509821f1d3SAlan Somers using namespace testing;
519821f1d3SAlan Somers 
529821f1d3SAlan Somers class Write: public FuseTest {
539821f1d3SAlan Somers 
549821f1d3SAlan Somers public:
55a639731bSAlan Somers void SetUp() {
56a639731bSAlan Somers 	FuseTest::SetUp();
57a639731bSAlan Somers }
58a639731bSAlan Somers 
59a639731bSAlan Somers void TearDown() {
60a639731bSAlan Somers 	struct sigaction sa;
61a639731bSAlan Somers 
62a639731bSAlan Somers 	bzero(&sa, sizeof(sa));
63a639731bSAlan Somers 	sa.sa_handler = SIG_DFL;
64a639731bSAlan Somers 	sigaction(SIGXFSZ, &sa, NULL);
65a639731bSAlan Somers 
66a639731bSAlan Somers 	FuseTest::TearDown();
67a639731bSAlan Somers }
689821f1d3SAlan Somers 
699821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
709821f1d3SAlan Somers {
719821f1d3SAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
729821f1d3SAlan Somers }
739821f1d3SAlan Somers 
749821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r)
759821f1d3SAlan Somers {
769821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
779821f1d3SAlan Somers 		ResultOf([=](auto in) {
7829edc611SAlan Somers 			return (in.header.opcode == FUSE_RELEASE &&
7929edc611SAlan Somers 				in.header.nodeid == ino);
809821f1d3SAlan Somers 		}, Eq(true)),
819821f1d3SAlan Somers 		_)
829821f1d3SAlan Somers 	).WillRepeatedly(Invoke(r));
839821f1d3SAlan Somers }
849821f1d3SAlan Somers 
85bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
86bda39894SAlan Somers 	uint64_t osize, const void *contents)
87bda39894SAlan Somers {
88bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
89bda39894SAlan Somers }
90bda39894SAlan Somers 
9184879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */
9284879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
9384879e46SAlan Somers 	const void *contents)
9484879e46SAlan Somers {
9584879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
9684879e46SAlan Somers 		ResultOf([=](auto in) {
9784879e46SAlan Somers 			const char *buf = (const char*)in.body.bytes +
9884879e46SAlan Somers 				sizeof(struct fuse_write_in);
9984879e46SAlan Somers 
10084879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE &&
10184879e46SAlan Somers 				in.header.nodeid == ino &&
10284879e46SAlan Somers 				in.body.write.offset == offset  &&
10384879e46SAlan Somers 				in.body.write.size == size &&
10484879e46SAlan Somers 				0 == bcmp(buf, contents, size));
10584879e46SAlan Somers 		}, Eq(true)),
10684879e46SAlan Somers 		_)
10784879e46SAlan Somers 	).Times(AtMost(1))
10884879e46SAlan Somers 	.WillRepeatedly(Invoke(
10984879e46SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
11084879e46SAlan Somers 			SET_OUT_HEADER_LEN(out, write);
11184879e46SAlan Somers 			out.body.write.size = size;
11284879e46SAlan Somers 		})
11384879e46SAlan Somers 	));
11484879e46SAlan Somers }
11584879e46SAlan Somers 
1169821f1d3SAlan Somers };
1179821f1d3SAlan Somers 
11816bd2d47SAlan Somers class Write_7_8: public FuseTest {
11916bd2d47SAlan Somers 
12016bd2d47SAlan Somers public:
12116bd2d47SAlan Somers virtual void SetUp() {
12216bd2d47SAlan Somers 	m_kernel_minor_version = 8;
12316bd2d47SAlan Somers 	FuseTest::SetUp();
12416bd2d47SAlan Somers }
12516bd2d47SAlan Somers 
12616bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
12716bd2d47SAlan Somers {
12816bd2d47SAlan Somers 	FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
12916bd2d47SAlan Somers }
13016bd2d47SAlan Somers 
13116bd2d47SAlan Somers };
13216bd2d47SAlan Somers 
1339821f1d3SAlan Somers class AioWrite: public Write {
1349821f1d3SAlan Somers virtual void SetUp() {
135c2265ae7SAlan Somers 	if (!is_unsafe_aio_enabled())
1369821f1d3SAlan Somers 		GTEST_SKIP() <<
1379821f1d3SAlan Somers 			"vfs.aio.enable_unsafe must be set for this test";
138c2265ae7SAlan Somers 	FuseTest::SetUp();
1399821f1d3SAlan Somers }
1409821f1d3SAlan Somers };
1419821f1d3SAlan Somers 
1429821f1d3SAlan Somers /* Tests for the writeback cache mode */
1439821f1d3SAlan Somers class WriteBack: public Write {
144bda39894SAlan Somers public:
1459821f1d3SAlan Somers virtual void SetUp() {
146f8ebf1cdSAlan Somers 	m_init_flags |= FUSE_WRITEBACK_CACHE;
1479821f1d3SAlan Somers 	FuseTest::SetUp();
1489821f1d3SAlan Somers 	if (IsSkipped())
1499821f1d3SAlan Somers 		return;
1509821f1d3SAlan Somers }
1519821f1d3SAlan Somers 
152bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
153bda39894SAlan Somers 	uint64_t osize, const void *contents)
154bda39894SAlan Somers {
155bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
156bda39894SAlan Somers 		contents);
157bda39894SAlan Somers }
1589821f1d3SAlan Somers };
1599821f1d3SAlan Somers 
16084879e46SAlan Somers class WriteBackAsync: public WriteBack {
16184879e46SAlan Somers public:
16284879e46SAlan Somers virtual void SetUp() {
16384879e46SAlan Somers 	m_async = true;
164f928dbcbSAlan Somers 	m_maxwrite = 65536;
16584879e46SAlan Somers 	WriteBack::SetUp();
16684879e46SAlan Somers }
16784879e46SAlan Somers };
16884879e46SAlan Somers 
169fef46454SAlan Somers class TimeGran: public WriteBackAsync, public WithParamInterface<unsigned> {
170fef46454SAlan Somers public:
171fef46454SAlan Somers virtual void SetUp() {
172fef46454SAlan Somers 	m_time_gran = 1 << GetParam();
173fef46454SAlan Somers 	WriteBackAsync::SetUp();
174fef46454SAlan Somers }
175fef46454SAlan Somers };
176fef46454SAlan Somers 
1778eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */
1788eecd9ceSAlan Somers class WriteCluster: public WriteBack {
1798eecd9ceSAlan Somers public:
1808eecd9ceSAlan Somers virtual void SetUp() {
1818eecd9ceSAlan Somers 	m_async = true;
1828e765737SAlan Somers 	m_maxwrite = 1 << 25;	// Anything larger than MAXPHYS will suffice
1838eecd9ceSAlan Somers 	WriteBack::SetUp();
184f8ebf1cdSAlan Somers 	if (m_maxphys < 2 * DFLTPHYS)
185f8ebf1cdSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
186f8ebf1cdSAlan Somers 			<< " for this test";
187f2704f05SAlan Somers 	if (m_maxphys < 2 * m_maxbcachebuf)
1886ca3b02bSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf"
1896ca3b02bSAlan Somers 			<< " for this test";
1908eecd9ceSAlan Somers }
1918eecd9ceSAlan Somers };
1928eecd9ceSAlan Somers 
193f928dbcbSAlan Somers /* Tests relating to the server's max_write property */
194f928dbcbSAlan Somers class WriteMaxWrite: public Write {
195f928dbcbSAlan Somers public:
196f928dbcbSAlan Somers virtual void SetUp() {
197f928dbcbSAlan Somers 	/*
198f928dbcbSAlan Somers 	 * For this test, m_maxwrite must be less than either m_maxbcachebuf or
199f928dbcbSAlan Somers 	 * maxphys.
200f928dbcbSAlan Somers 	 */
201f928dbcbSAlan Somers 	m_maxwrite = 32768;
202f928dbcbSAlan Somers 	Write::SetUp();
203f928dbcbSAlan Somers }
204f928dbcbSAlan Somers };
205f928dbcbSAlan Somers 
206032a5bd5SAlan Somers class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int>
207032a5bd5SAlan Somers {};
208032a5bd5SAlan Somers 
209*be280f60SAlan Somers class WriteRlimitFsize: public Write, public WithParamInterface<int> {
210*be280f60SAlan Somers public:
211*be280f60SAlan Somers static sig_atomic_t s_sigxfsz;
212*be280f60SAlan Somers struct rlimit	m_initial_limit;
213*be280f60SAlan Somers 
214*be280f60SAlan Somers void SetUp() {
215*be280f60SAlan Somers 	s_sigxfsz = 0;
216*be280f60SAlan Somers 	getrlimit(RLIMIT_FSIZE, &m_initial_limit);
217*be280f60SAlan Somers 	FuseTest::SetUp();
218*be280f60SAlan Somers }
219*be280f60SAlan Somers 
220*be280f60SAlan Somers void TearDown() {
221*be280f60SAlan Somers 	setrlimit(RLIMIT_FSIZE, &m_initial_limit);
222*be280f60SAlan Somers 
223*be280f60SAlan Somers 	FuseTest::TearDown();
224*be280f60SAlan Somers }
225*be280f60SAlan Somers };
226*be280f60SAlan Somers 
227*be280f60SAlan Somers sig_atomic_t WriteRlimitFsize::s_sigxfsz = 0;
228*be280f60SAlan Somers 
229a639731bSAlan Somers void sigxfsz_handler(int __unused sig) {
230*be280f60SAlan Somers 	WriteRlimitFsize::s_sigxfsz = 1;
231a639731bSAlan Somers }
232a639731bSAlan Somers 
2339821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */
2349821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
2359821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write)
2369821f1d3SAlan Somers {
2379821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2389821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2399821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
2409821f1d3SAlan Somers 	uint64_t ino = 42;
2419821f1d3SAlan Somers 	uint64_t offset = 4096;
2429821f1d3SAlan Somers 	int fd;
2439821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
2449821f1d3SAlan Somers 	struct aiocb iocb, *piocb;
2459821f1d3SAlan Somers 
2469821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
2479821f1d3SAlan Somers 	expect_open(ino, 0, 1);
248bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
2499821f1d3SAlan Somers 
2509821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
251d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2529821f1d3SAlan Somers 
2539821f1d3SAlan Somers 	iocb.aio_nbytes = bufsize;
2549821f1d3SAlan Somers 	iocb.aio_fildes = fd;
2555a0b9a27SAlan Somers 	iocb.aio_buf = __DECONST(void *, CONTENTS);
2569821f1d3SAlan Somers 	iocb.aio_offset = offset;
2579821f1d3SAlan Somers 	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
2589821f1d3SAlan Somers 	ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
2599821f1d3SAlan Somers 	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
2607fc0921dSAlan Somers 	leak(fd);
2619821f1d3SAlan Somers }
2629821f1d3SAlan Somers 
2639821f1d3SAlan Somers /*
2649821f1d3SAlan Somers  * When a file is opened with O_APPEND, we should forward that flag to
2659821f1d3SAlan Somers  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
2669821f1d3SAlan Somers  * offset internally.  That way we'll work both with filesystems that
2679821f1d3SAlan Somers  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
2689821f1d3SAlan Somers  * simply use the offset).
2699821f1d3SAlan Somers  *
2709821f1d3SAlan Somers  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
2719821f1d3SAlan Somers  * Open.o_append test.
2729821f1d3SAlan Somers  */
2739821f1d3SAlan Somers TEST_F(Write, append)
2749821f1d3SAlan Somers {
2759821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
2769821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2779821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2789821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
2799821f1d3SAlan Somers 	uint64_t ino = 42;
2809821f1d3SAlan Somers 	/*
2819821f1d3SAlan Somers 	 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
2829821f1d3SAlan Somers 	 * using writeback caching
2839821f1d3SAlan Somers 	 */
2849821f1d3SAlan Somers 	uint64_t initial_offset = m_maxbcachebuf;
2859821f1d3SAlan Somers 	int fd;
2869821f1d3SAlan Somers 
2879821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
2889821f1d3SAlan Somers 	expect_open(ino, 0, 1);
289bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
2909821f1d3SAlan Somers 
2919821f1d3SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
2929821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
293d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2949821f1d3SAlan Somers 
2959821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
2967fc0921dSAlan Somers 	leak(fd);
2979821f1d3SAlan Somers }
2989821f1d3SAlan Somers 
299a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */
300a87e0831SAlan Somers TEST_F(Write, append_to_cached)
301a87e0831SAlan Somers {
302a87e0831SAlan Somers 	const ssize_t BUFSIZE = 9;
303a87e0831SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
304a87e0831SAlan Somers 	const char RELPATH[] = "some_file.txt";
305a87e0831SAlan Somers 	char *oldcontents, *oldbuf;
306a87e0831SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
307a87e0831SAlan Somers 	uint64_t ino = 42;
308a87e0831SAlan Somers 	/*
309a87e0831SAlan Somers 	 * Set offset in between maxbcachebuf boundary to test buffer handling
310a87e0831SAlan Somers 	 */
311a87e0831SAlan Somers 	uint64_t oldsize = m_maxbcachebuf / 2;
312a87e0831SAlan Somers 	int fd;
313a87e0831SAlan Somers 
314a87e0831SAlan Somers 	oldcontents = (char*)calloc(1, oldsize);
3155a0b9a27SAlan Somers 	ASSERT_NE(nullptr, oldcontents) << strerror(errno);
316a87e0831SAlan Somers 	oldbuf = (char*)malloc(oldsize);
3175a0b9a27SAlan Somers 	ASSERT_NE(nullptr, oldbuf) << strerror(errno);
318a87e0831SAlan Somers 
319a87e0831SAlan Somers 	expect_lookup(RELPATH, ino, oldsize);
320a87e0831SAlan Somers 	expect_open(ino, 0, 1);
321a87e0831SAlan Somers 	expect_read(ino, 0, oldsize, oldsize, oldcontents);
32284879e46SAlan Somers 	maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
323a87e0831SAlan Somers 
324a87e0831SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
325a87e0831SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
326d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
327a87e0831SAlan Somers 
328a87e0831SAlan Somers 	/* Read the old data into the cache */
329a87e0831SAlan Somers 	ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
330a87e0831SAlan Somers 		<< strerror(errno);
331a87e0831SAlan Somers 
332a87e0831SAlan Somers 	/* Write the new data.  There should be no more read operations */
333a87e0831SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3347fc0921dSAlan Somers 	leak(fd);
3357ee7e409SAlan Somers 	free(oldbuf);
3367ee7e409SAlan Somers 	free(oldcontents);
337a87e0831SAlan Somers }
338a87e0831SAlan Somers 
3399821f1d3SAlan Somers TEST_F(Write, append_direct_io)
3409821f1d3SAlan Somers {
3419821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
3429821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3439821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3449821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
3459821f1d3SAlan Somers 	uint64_t ino = 42;
3469821f1d3SAlan Somers 	uint64_t initial_offset = 4096;
3479821f1d3SAlan Somers 	int fd;
3489821f1d3SAlan Somers 
3499821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
3509821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
351bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
3529821f1d3SAlan Somers 
3539821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY | O_APPEND);
354d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
3559821f1d3SAlan Somers 
3569821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3577fc0921dSAlan Somers 	leak(fd);
3589821f1d3SAlan Somers }
3599821f1d3SAlan Somers 
3609821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */
3616af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache)
3629821f1d3SAlan Somers {
3639821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3649821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3659821f1d3SAlan Somers 	const char CONTENTS0[] = "abcdefgh";
3669821f1d3SAlan Somers 	const char CONTENTS1[] = "ijklmnop";
3679821f1d3SAlan Somers 	uint64_t ino = 42;
3689821f1d3SAlan Somers 	int fd;
3699821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
3709821f1d3SAlan Somers 	char readbuf[bufsize];
3719821f1d3SAlan Somers 
3729821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
3739821f1d3SAlan Somers 	expect_open(ino, 0, 1);
3749821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
375bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
3769821f1d3SAlan Somers 
3779821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
378d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
3799821f1d3SAlan Somers 
3809821f1d3SAlan Somers 	// Prime cache
3819821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3829821f1d3SAlan Somers 
3839821f1d3SAlan Somers 	// Write directly, evicting cache
3849821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
3859821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3869821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
3879821f1d3SAlan Somers 
3889821f1d3SAlan Somers 	// Read again.  Cache should be bypassed
3899821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
3909821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
3919821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3929821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3939821f1d3SAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS1);
3949821f1d3SAlan Somers 
3957fc0921dSAlan Somers 	leak(fd);
3969821f1d3SAlan Somers }
3979821f1d3SAlan Somers 
3989821f1d3SAlan Somers /*
39912292a99SAlan Somers  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
40012292a99SAlan Somers  * allowed to return a short write for that file handle.  However, if it does
40112292a99SAlan Somers  * then we should still do our darndest to handle it by resending the unwritten
40212292a99SAlan Somers  * portion.
4039821f1d3SAlan Somers  */
40412292a99SAlan Somers TEST_F(Write, indirect_io_short_write)
4059821f1d3SAlan Somers {
4069821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4079821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4089821f1d3SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
4099821f1d3SAlan Somers 	uint64_t ino = 42;
4109821f1d3SAlan Somers 	int fd;
4119821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
41212292a99SAlan Somers 	ssize_t bufsize0 = 11;
41312292a99SAlan Somers 	ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
41412292a99SAlan Somers 	const char *contents1 = CONTENTS + bufsize0;
4159821f1d3SAlan Somers 
4169821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
41712292a99SAlan Somers 	expect_open(ino, 0, 1);
418bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
419bda39894SAlan Somers 	expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
4209821f1d3SAlan Somers 
4219821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
422d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
4239821f1d3SAlan Somers 
4249821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4257fc0921dSAlan Somers 	leak(fd);
4269821f1d3SAlan Somers }
4279821f1d3SAlan Somers 
4283a1b3c6aSAlan Somers /* It is an error if the daemon claims to have written more data than we sent */
4293a1b3c6aSAlan Somers TEST_F(Write, indirect_io_long_write)
4303a1b3c6aSAlan Somers {
4313a1b3c6aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4323a1b3c6aSAlan Somers 	const char RELPATH[] = "some_file.txt";
4333a1b3c6aSAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
4343a1b3c6aSAlan Somers 	uint64_t ino = 42;
4353a1b3c6aSAlan Somers 	int fd;
4363a1b3c6aSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
4373a1b3c6aSAlan Somers 	ssize_t bufsize_out = 100;
4383a1b3c6aSAlan Somers 	off_t some_other_size = 25;
4393a1b3c6aSAlan Somers 	struct stat sb;
4403a1b3c6aSAlan Somers 
4413a1b3c6aSAlan Somers 	expect_lookup(RELPATH, ino, 0);
4423a1b3c6aSAlan Somers 	expect_open(ino, 0, 1);
4433a1b3c6aSAlan Somers 	expect_write(ino, 0, bufsize, bufsize_out, CONTENTS);
4443a1b3c6aSAlan Somers 	expect_getattr(ino, some_other_size);
4453a1b3c6aSAlan Somers 
4463a1b3c6aSAlan Somers 	fd = open(FULLPATH, O_WRONLY);
4473a1b3c6aSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
4483a1b3c6aSAlan Somers 
4493a1b3c6aSAlan Somers 	ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno);
4503a1b3c6aSAlan Somers 	ASSERT_EQ(EINVAL, errno);
4513a1b3c6aSAlan Somers 
4523a1b3c6aSAlan Somers 	/*
4533a1b3c6aSAlan Somers 	 * Following such an error, we should requery the server for the file's
4543a1b3c6aSAlan Somers 	 * size.
4553a1b3c6aSAlan Somers 	 */
4563a1b3c6aSAlan Somers 	fstat(fd, &sb);
4573a1b3c6aSAlan Somers 	ASSERT_EQ(sb.st_size, some_other_size);
4583a1b3c6aSAlan Somers 
4593a1b3c6aSAlan Somers 	leak(fd);
4603a1b3c6aSAlan Somers }
4613a1b3c6aSAlan Somers 
4623a1b3c6aSAlan Somers /*
4633a1b3c6aSAlan Somers  * Don't crash if the server returns a write that can't be represented as a
4643a1b3c6aSAlan Somers  * signed 32 bit number.  Regression test for
4653a1b3c6aSAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263263
4663a1b3c6aSAlan Somers  */
4673a1b3c6aSAlan Somers TEST_F(Write, indirect_io_very_long_write)
4683a1b3c6aSAlan Somers {
4693a1b3c6aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4703a1b3c6aSAlan Somers 	const char RELPATH[] = "some_file.txt";
4713a1b3c6aSAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
4723a1b3c6aSAlan Somers 	uint64_t ino = 42;
4733a1b3c6aSAlan Somers 	int fd;
4743a1b3c6aSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
4753a1b3c6aSAlan Somers 	ssize_t bufsize_out = 3 << 30;
4763a1b3c6aSAlan Somers 
4773a1b3c6aSAlan Somers 	expect_lookup(RELPATH, ino, 0);
4783a1b3c6aSAlan Somers 	expect_open(ino, 0, 1);
4793a1b3c6aSAlan Somers 	expect_write(ino, 0, bufsize, bufsize_out, CONTENTS);
4803a1b3c6aSAlan Somers 
4813a1b3c6aSAlan Somers 	fd = open(FULLPATH, O_WRONLY);
4823a1b3c6aSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
4833a1b3c6aSAlan Somers 
4843a1b3c6aSAlan Somers 	ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno);
4853a1b3c6aSAlan Somers 	ASSERT_EQ(EINVAL, errno);
4863a1b3c6aSAlan Somers 	leak(fd);
4873a1b3c6aSAlan Somers }
4883a1b3c6aSAlan Somers 
4899821f1d3SAlan Somers /*
49012292a99SAlan Somers  * When the direct_io option is used, filesystems are allowed to write less
49112292a99SAlan Somers  * data than requested.  We should return the short write to userland.
49212292a99SAlan Somers  */
49312292a99SAlan Somers TEST_F(Write, direct_io_short_write)
49412292a99SAlan Somers {
49512292a99SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
49612292a99SAlan Somers 	const char RELPATH[] = "some_file.txt";
49712292a99SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
49812292a99SAlan Somers 	uint64_t ino = 42;
49912292a99SAlan Somers 	int fd;
50012292a99SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
50112292a99SAlan Somers 	ssize_t halfbufsize = bufsize / 2;
50212292a99SAlan Somers 
50312292a99SAlan Somers 	expect_lookup(RELPATH, ino, 0);
50412292a99SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
505bda39894SAlan Somers 	expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
50612292a99SAlan Somers 
50712292a99SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
508d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
50912292a99SAlan Somers 
51012292a99SAlan Somers 	ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
5117fc0921dSAlan Somers 	leak(fd);
51212292a99SAlan Somers }
51312292a99SAlan Somers 
51412292a99SAlan Somers /*
5159821f1d3SAlan Somers  * An insidious edge case: the filesystem returns a short write, and the
5169821f1d3SAlan Somers  * difference between what we requested and what it actually wrote crosses an
5179821f1d3SAlan Somers  * iov element boundary
5189821f1d3SAlan Somers  */
51912292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov)
5209821f1d3SAlan Somers {
5219821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5229821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5239821f1d3SAlan Somers 	const char *CONTENTS0 = "abcdefgh";
5249821f1d3SAlan Somers 	const char *CONTENTS1 = "ijklmnop";
5259821f1d3SAlan Somers 	const char *EXPECTED0 = "abcdefghijklmnop";
5269821f1d3SAlan Somers 	uint64_t ino = 42;
5279821f1d3SAlan Somers 	int fd;
5289821f1d3SAlan Somers 	ssize_t size0 = strlen(CONTENTS0) - 1;
5299821f1d3SAlan Somers 	ssize_t size1 = strlen(CONTENTS1) + 1;
5309821f1d3SAlan Somers 	ssize_t totalsize = size0 + size1;
5319821f1d3SAlan Somers 	struct iovec iov[2];
5329821f1d3SAlan Somers 
5339821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
5349821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
535bda39894SAlan Somers 	expect_write(ino, 0, totalsize, size0, EXPECTED0);
5369821f1d3SAlan Somers 
5379821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
538d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
5399821f1d3SAlan Somers 
5405a0b9a27SAlan Somers 	iov[0].iov_base = __DECONST(void*, CONTENTS0);
5419821f1d3SAlan Somers 	iov[0].iov_len = strlen(CONTENTS0);
5425a0b9a27SAlan Somers 	iov[1].iov_base = __DECONST(void*, CONTENTS1);
5439821f1d3SAlan Somers 	iov[1].iov_len = strlen(CONTENTS1);
54412292a99SAlan Somers 	ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
5457fc0921dSAlan Somers 	leak(fd);
5469821f1d3SAlan Somers }
5479821f1d3SAlan Somers 
548a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */
549*be280f60SAlan Somers TEST_P(WriteRlimitFsize, rlimit_fsize)
550a639731bSAlan Somers {
551a639731bSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
552a639731bSAlan Somers 	const char RELPATH[] = "some_file.txt";
553a639731bSAlan Somers 	const char *CONTENTS = "abcdefgh";
554a639731bSAlan Somers 	struct rlimit rl;
555a639731bSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
556a639731bSAlan Somers 	off_t offset = 1'000'000'000;
557a639731bSAlan Somers 	uint64_t ino = 42;
558*be280f60SAlan Somers 	int fd, oflag;
559*be280f60SAlan Somers 
560*be280f60SAlan Somers 	oflag = GetParam();
561a639731bSAlan Somers 
562a639731bSAlan Somers 	expect_lookup(RELPATH, ino, 0);
563a639731bSAlan Somers 	expect_open(ino, 0, 1);
564a639731bSAlan Somers 
565a639731bSAlan Somers 	rl.rlim_cur = offset;
566*be280f60SAlan Somers 	rl.rlim_max = m_initial_limit.rlim_max;
567a639731bSAlan Somers 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
568a639731bSAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
569a639731bSAlan Somers 
570*be280f60SAlan Somers 	fd = open(FULLPATH, O_WRONLY | oflag);
571a639731bSAlan Somers 
572d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
573a639731bSAlan Somers 
574a639731bSAlan Somers 	ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
575a639731bSAlan Somers 	EXPECT_EQ(EFBIG, errno);
576a639731bSAlan Somers 	EXPECT_EQ(1, s_sigxfsz);
5777fc0921dSAlan Somers 	leak(fd);
578a639731bSAlan Somers }
579a639731bSAlan Somers 
5809821f1d3SAlan Somers /*
581*be280f60SAlan Somers  * When crossing the RLIMIT_FSIZE boundary, writes should be truncated, not
582*be280f60SAlan Somers  * aborted.
583*be280f60SAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=164793
584*be280f60SAlan Somers  */
585*be280f60SAlan Somers TEST_P(WriteRlimitFsize, rlimit_fsize_truncate)
586*be280f60SAlan Somers {
587*be280f60SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
588*be280f60SAlan Somers 	const char RELPATH[] = "some_file.txt";
589*be280f60SAlan Somers 	const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
590*be280f60SAlan Somers 	struct rlimit rl;
591*be280f60SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
592*be280f60SAlan Somers 	uint64_t ino = 42;
593*be280f60SAlan Somers 	off_t offset = 1 << 30;
594*be280f60SAlan Somers 	off_t limit = offset + strlen(CONTENTS) / 2;
595*be280f60SAlan Somers 	int fd, oflag;
596*be280f60SAlan Somers 
597*be280f60SAlan Somers 	oflag = GetParam();
598*be280f60SAlan Somers 
599*be280f60SAlan Somers 	expect_lookup(RELPATH, ino, 0);
600*be280f60SAlan Somers 	expect_open(ino, 0, 1);
601*be280f60SAlan Somers 	expect_write(ino, offset, bufsize / 2, bufsize / 2, CONTENTS);
602*be280f60SAlan Somers 
603*be280f60SAlan Somers 	rl.rlim_cur = limit;
604*be280f60SAlan Somers 	rl.rlim_max = m_initial_limit.rlim_max;
605*be280f60SAlan Somers 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
606*be280f60SAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
607*be280f60SAlan Somers 
608*be280f60SAlan Somers 	fd = open(FULLPATH, O_WRONLY | oflag);
609*be280f60SAlan Somers 
610*be280f60SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
611*be280f60SAlan Somers 
612*be280f60SAlan Somers 	ASSERT_EQ(bufsize / 2, pwrite(fd, CONTENTS, bufsize, offset))
613*be280f60SAlan Somers 		<< strerror(errno);
614*be280f60SAlan Somers 	leak(fd);
615*be280f60SAlan Somers }
616*be280f60SAlan Somers 
617*be280f60SAlan Somers INSTANTIATE_TEST_CASE_P(W, WriteRlimitFsize,
618*be280f60SAlan Somers 	Values(0, O_DIRECT)
619*be280f60SAlan Somers );
620*be280f60SAlan Somers 
621*be280f60SAlan Somers /*
622b9e20197SAlan Somers  * A short read indicates EOF.  Test that nothing bad happens if we get EOF
623b9e20197SAlan Somers  * during the R of a RMW operation.
624b9e20197SAlan Somers  */
625f8ebf1cdSAlan Somers TEST_F(Write, eof_during_rmw)
626b9e20197SAlan Somers {
627b9e20197SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
628b9e20197SAlan Somers 	const char RELPATH[] = "some_file.txt";
629b9e20197SAlan Somers 	const char *CONTENTS = "abcdefgh";
630b9e20197SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
631b9e20197SAlan Somers 	uint64_t ino = 42;
632b9e20197SAlan Somers 	uint64_t offset = 1;
633ae39db74SAlan Somers 	ssize_t bufsize = strlen(CONTENTS) + 1;
634b9e20197SAlan Somers 	off_t orig_fsize = 10;
635b9e20197SAlan Somers 	off_t truncated_fsize = 5;
636b9e20197SAlan Somers 	int fd;
637b9e20197SAlan Somers 
638b9e20197SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
639b9e20197SAlan Somers 	expect_open(ino, 0, 1);
640b9e20197SAlan Somers 	expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
641b9e20197SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
642b9e20197SAlan Somers 
643b9e20197SAlan Somers 	fd = open(FULLPATH, O_RDWR);
644d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
645b9e20197SAlan Somers 
646b9e20197SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
647b9e20197SAlan Somers 		<< strerror(errno);
6487fc0921dSAlan Somers 	leak(fd);
649b9e20197SAlan Somers }
650b9e20197SAlan Somers 
651b9e20197SAlan Somers /*
652032a5bd5SAlan Somers  * VOP_STRATEGY should not query the server for the file's size, even if its
653032a5bd5SAlan Somers  * cached attributes have expired.
654032a5bd5SAlan Somers  * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
655032a5bd5SAlan Somers  */
656032a5bd5SAlan Somers TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy)
657032a5bd5SAlan Somers {
658032a5bd5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
659032a5bd5SAlan Somers 	const char RELPATH[] = "some_file.txt";
660032a5bd5SAlan Somers 	Sequence seq;
661032a5bd5SAlan Somers 	const off_t filesize = 2 * m_maxbcachebuf;
662032a5bd5SAlan Somers 	void *contents;
663032a5bd5SAlan Somers 	uint64_t ino = 42;
664032a5bd5SAlan Somers 	uint64_t attr_valid = 0;
665032a5bd5SAlan Somers 	uint64_t attr_valid_nsec = 0;
666032a5bd5SAlan Somers 	mode_t mode = S_IFREG | 0644;
667032a5bd5SAlan Somers 	int fd;
668032a5bd5SAlan Somers 	int ngetattrs;
669032a5bd5SAlan Somers 
670032a5bd5SAlan Somers 	ngetattrs = GetParam();
671032a5bd5SAlan Somers 	contents = calloc(1, filesize);
672032a5bd5SAlan Somers 
673032a5bd5SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
674032a5bd5SAlan Somers 	.WillRepeatedly(Invoke(
675032a5bd5SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
676032a5bd5SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
677032a5bd5SAlan Somers 		out.body.entry.attr.mode = mode;
678032a5bd5SAlan Somers 		out.body.entry.nodeid = ino;
679032a5bd5SAlan Somers 		out.body.entry.attr.nlink = 1;
680032a5bd5SAlan Somers 		out.body.entry.attr.size = filesize;
681032a5bd5SAlan Somers 		out.body.entry.attr_valid = attr_valid;
682032a5bd5SAlan Somers 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
683032a5bd5SAlan Somers 	})));
684032a5bd5SAlan Somers 	expect_open(ino, 0, 1);
685032a5bd5SAlan Somers 	EXPECT_CALL(*m_mock, process(
686032a5bd5SAlan Somers 		ResultOf([=](auto in) {
687032a5bd5SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
688032a5bd5SAlan Somers 				in.header.nodeid == ino);
689032a5bd5SAlan Somers 		}, Eq(true)),
690032a5bd5SAlan Somers 		_)
691032a5bd5SAlan Somers 	).Times(Between(ngetattrs - 1, ngetattrs))
692032a5bd5SAlan Somers 	.InSequence(seq)
693032a5bd5SAlan Somers 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
694032a5bd5SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
695032a5bd5SAlan Somers 		out.body.attr.attr.ino = ino;
696032a5bd5SAlan Somers 		out.body.attr.attr.mode = mode;
697032a5bd5SAlan Somers 		out.body.attr.attr_valid = attr_valid;
698032a5bd5SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
699032a5bd5SAlan Somers 		out.body.attr.attr.size = filesize;
700032a5bd5SAlan Somers 	})));
701032a5bd5SAlan Somers 	EXPECT_CALL(*m_mock, process(
702032a5bd5SAlan Somers 		ResultOf([=](auto in) {
703032a5bd5SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
704032a5bd5SAlan Somers 				in.header.nodeid == ino);
705032a5bd5SAlan Somers 		}, Eq(true)),
706032a5bd5SAlan Somers 		_)
707032a5bd5SAlan Somers 	).InSequence(seq)
708032a5bd5SAlan Somers 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
709032a5bd5SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
710032a5bd5SAlan Somers 		out.body.attr.attr.ino = ino;
711032a5bd5SAlan Somers 		out.body.attr.attr.mode = mode;
712032a5bd5SAlan Somers 		out.body.attr.attr_valid = attr_valid;
713032a5bd5SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
714032a5bd5SAlan Somers 		out.body.attr.attr.size = filesize / 2;
715032a5bd5SAlan Somers 	})));
716032a5bd5SAlan Somers 	expect_write(ino, 0, filesize / 2, filesize / 2, contents);
717032a5bd5SAlan Somers 
718032a5bd5SAlan Somers 	fd = open(FULLPATH, O_RDWR);
719032a5bd5SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
720032a5bd5SAlan Somers 	ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2))
721032a5bd5SAlan Somers 		<< strerror(errno);
722032a5bd5SAlan Somers 
723032a5bd5SAlan Somers }
724032a5bd5SAlan Somers 
725032a5bd5SAlan Somers INSTANTIATE_TEST_CASE_P(W, WriteEofDuringVnopStrategy,
726032a5bd5SAlan Somers 	Values(1, 2, 3)
727032a5bd5SAlan Somers );
728032a5bd5SAlan Somers 
729032a5bd5SAlan Somers /*
7309821f1d3SAlan Somers  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
7319821f1d3SAlan Somers  * write, then it must set the FUSE_WRITE_CACHE bit
7329821f1d3SAlan Somers  */
7339821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
734f8ebf1cdSAlan Somers TEST_F(Write, mmap)
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 fd;
7419821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7429821f1d3SAlan Somers 	void *p;
7439821f1d3SAlan Somers 	uint64_t offset = 10;
7449821f1d3SAlan Somers 	size_t len;
7459821f1d3SAlan Somers 	void *zeros, *expected;
7469821f1d3SAlan Somers 
7479821f1d3SAlan Somers 	len = getpagesize();
7489821f1d3SAlan Somers 
7499821f1d3SAlan Somers 	zeros = calloc(1, len);
7505a0b9a27SAlan Somers 	ASSERT_NE(nullptr, zeros);
7519821f1d3SAlan Somers 	expected = calloc(1, len);
7525a0b9a27SAlan Somers 	ASSERT_NE(nullptr, expected);
7539821f1d3SAlan Somers 	memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
7549821f1d3SAlan Somers 
7559821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, len);
7569821f1d3SAlan Somers 	expect_open(ino, 0, 1);
7579821f1d3SAlan Somers 	expect_read(ino, 0, len, len, zeros);
7589821f1d3SAlan Somers 	/*
7599821f1d3SAlan Somers 	 * Writes from the pager may or may not be associated with the correct
760cf437e2aSAlan Somers 	 * pid, so they must set FUSE_WRITE_CACHE.
7619821f1d3SAlan Somers 	 */
762bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
7639f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
7649821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
7659821f1d3SAlan Somers 
7669821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
767d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7689821f1d3SAlan Somers 
7699821f1d3SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
7709821f1d3SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
7719821f1d3SAlan Somers 
7729821f1d3SAlan Somers 	memmove((uint8_t*)p + offset, CONTENTS, bufsize);
7739821f1d3SAlan Somers 
7749821f1d3SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
7759821f1d3SAlan Somers 	close(fd);	// Write mmap'd data on close
7769821f1d3SAlan Somers 
7779821f1d3SAlan Somers 	free(expected);
7789821f1d3SAlan Somers 	free(zeros);
7798e765737SAlan Somers 
7808e765737SAlan Somers 	leak(fd);
7819821f1d3SAlan Somers }
7829821f1d3SAlan Somers 
783f8ebf1cdSAlan Somers TEST_F(Write, pwrite)
7849821f1d3SAlan Somers {
7859821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7869821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7879821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7889821f1d3SAlan Somers 	uint64_t ino = 42;
7896ca3b02bSAlan Somers 	uint64_t offset = m_maxbcachebuf;
7909821f1d3SAlan Somers 	int fd;
7919821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7929821f1d3SAlan Somers 
7939821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7949821f1d3SAlan Somers 	expect_open(ino, 0, 1);
795bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
7969821f1d3SAlan Somers 
7979821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
798d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7999821f1d3SAlan Somers 
8009821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
8019821f1d3SAlan Somers 		<< strerror(errno);
8027fc0921dSAlan Somers 	leak(fd);
8039821f1d3SAlan Somers }
8049821f1d3SAlan Somers 
805788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */
806788af953SAlan Somers TEST_F(Write, timestamps)
807788af953SAlan Somers {
808788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
809788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
810788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
811788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
812788af953SAlan Somers 	uint64_t ino = 42;
813788af953SAlan Somers 	struct stat sb0, sb1;
814788af953SAlan Somers 	int fd;
815788af953SAlan Somers 
816788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
817788af953SAlan Somers 	expect_open(ino, 0, 1);
818788af953SAlan Somers 	maybe_expect_write(ino, 0, bufsize, CONTENTS);
819788af953SAlan Somers 
820788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
821d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
822788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
823788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
824788af953SAlan Somers 
825788af953SAlan Somers 	nap();
826788af953SAlan Somers 
827788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno);
828788af953SAlan Somers 
829788af953SAlan Somers 	EXPECT_EQ(sb0.st_atime, sb1.st_atime);
830788af953SAlan Somers 	EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
831788af953SAlan Somers 	EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
8328e765737SAlan Somers 
8338e765737SAlan Somers 	leak(fd);
834788af953SAlan Somers }
835788af953SAlan Somers 
8369821f1d3SAlan Somers TEST_F(Write, write)
8379821f1d3SAlan Somers {
8389821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8399821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8409821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
8419821f1d3SAlan Somers 	uint64_t ino = 42;
8429821f1d3SAlan Somers 	int fd;
8439821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8449821f1d3SAlan Somers 
8459821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
8469821f1d3SAlan Somers 	expect_open(ino, 0, 1);
847bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
8489821f1d3SAlan Somers 
8499821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
850d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8519821f1d3SAlan Somers 
8529821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
8537fc0921dSAlan Somers 	leak(fd);
8549821f1d3SAlan Somers }
8559821f1d3SAlan Somers 
8569821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */
857f928dbcbSAlan Somers TEST_F(WriteMaxWrite, write)
8589821f1d3SAlan Somers {
8599821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8609821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8619821f1d3SAlan Somers 	int *contents;
8629821f1d3SAlan Somers 	uint64_t ino = 42;
8639821f1d3SAlan Somers 	int fd;
8649821f1d3SAlan Somers 	ssize_t halfbufsize, bufsize;
8659821f1d3SAlan Somers 
8668eecd9ceSAlan Somers 	halfbufsize = m_mock->m_maxwrite;
867f928dbcbSAlan Somers 	if (halfbufsize >= m_maxbcachebuf || halfbufsize >= m_maxphys)
868f928dbcbSAlan Somers 		GTEST_SKIP() << "Must lower m_maxwrite for this test";
8699821f1d3SAlan Somers 	bufsize = halfbufsize * 2;
8709821f1d3SAlan Somers 	contents = (int*)malloc(bufsize);
8715a0b9a27SAlan Somers 	ASSERT_NE(nullptr, contents);
8729821f1d3SAlan Somers 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
8739821f1d3SAlan Somers 		contents[i] = i;
8749821f1d3SAlan Somers 	}
8759821f1d3SAlan Somers 
8769821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
8779821f1d3SAlan Somers 	expect_open(ino, 0, 1);
878f2704f05SAlan Somers 	maybe_expect_write(ino, 0, halfbufsize, contents);
87984879e46SAlan Somers 	maybe_expect_write(ino, halfbufsize, halfbufsize,
8809821f1d3SAlan Somers 		&contents[halfbufsize / sizeof(int)]);
8819821f1d3SAlan Somers 
8829821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
883d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8849821f1d3SAlan Somers 
8859821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
8867fc0921dSAlan Somers 	leak(fd);
8879821f1d3SAlan Somers 
8889821f1d3SAlan Somers 	free(contents);
8899821f1d3SAlan Somers }
8909821f1d3SAlan Somers 
8919821f1d3SAlan Somers TEST_F(Write, write_nothing)
8929821f1d3SAlan Somers {
8939821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8949821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8959821f1d3SAlan Somers 	const char *CONTENTS = "";
8969821f1d3SAlan Somers 	uint64_t ino = 42;
8979821f1d3SAlan Somers 	int fd;
8989821f1d3SAlan Somers 	ssize_t bufsize = 0;
8999821f1d3SAlan Somers 
9009821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
9019821f1d3SAlan Somers 	expect_open(ino, 0, 1);
9029821f1d3SAlan Somers 
9039821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
904d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
9059821f1d3SAlan Somers 
9069821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9077fc0921dSAlan Somers 	leak(fd);
9089821f1d3SAlan Somers }
9099821f1d3SAlan Somers 
91016bd2d47SAlan Somers TEST_F(Write_7_8, write)
91116bd2d47SAlan Somers {
91216bd2d47SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
91316bd2d47SAlan Somers 	const char RELPATH[] = "some_file.txt";
91416bd2d47SAlan Somers 	const char *CONTENTS = "abcdefgh";
91516bd2d47SAlan Somers 	uint64_t ino = 42;
91616bd2d47SAlan Somers 	int fd;
91716bd2d47SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
91816bd2d47SAlan Somers 
91916bd2d47SAlan Somers 	expect_lookup(RELPATH, ino, 0);
92016bd2d47SAlan Somers 	expect_open(ino, 0, 1);
921bda39894SAlan Somers 	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
92216bd2d47SAlan Somers 
92316bd2d47SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
924d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
92516bd2d47SAlan Somers 
92616bd2d47SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9277fc0921dSAlan Somers 	leak(fd);
92816bd2d47SAlan Somers }
92916bd2d47SAlan Somers 
9309821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */
93184879e46SAlan Somers TEST_F(WriteBackAsync, close)
9329821f1d3SAlan Somers {
9339821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9349821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
9359821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
9369821f1d3SAlan Somers 	uint64_t ino = 42;
9379821f1d3SAlan Somers 	int fd;
9389821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
9399821f1d3SAlan Somers 
9409821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
9419821f1d3SAlan Somers 	expect_open(ino, 0, 1);
942bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
9439821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
9449821f1d3SAlan Somers 		ResultOf([=](auto in) {
94529edc611SAlan Somers 			return (in.header.opcode == FUSE_SETATTR);
9469821f1d3SAlan Somers 		}, Eq(true)),
9479821f1d3SAlan Somers 		_)
94829edc611SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
9499821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
95029edc611SAlan Somers 		out.body.attr.attr.ino = ino;	// Must match nodeid
9519821f1d3SAlan Somers 	})));
9529f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
9539821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
9549821f1d3SAlan Somers 
9559821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
9569821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
9579821f1d3SAlan Somers 
9589821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9599821f1d3SAlan Somers 	close(fd);
9609821f1d3SAlan Somers }
9619821f1d3SAlan Somers 
9628eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */
9638eecd9ceSAlan Somers TEST_F(WriteCluster, clustering)
9648eecd9ceSAlan Somers {
9658eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9668eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
9678eecd9ceSAlan Somers 	uint64_t ino = 42;
9688eecd9ceSAlan Somers 	int i, fd;
9698eecd9ceSAlan Somers 	void *wbuf, *wbuf2x;
9706ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
9716ca3b02bSAlan Somers 	off_t filesize = 5 * bufsize;
9728eecd9ceSAlan Somers 
9738eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
9745a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf) << strerror(errno);
9758eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
9768eecd9ceSAlan Somers 	wbuf2x = malloc(2 * bufsize);
9775a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf2x) << strerror(errno);
9788eecd9ceSAlan Somers 	memset(wbuf2x, 'X', 2 * bufsize);
9798eecd9ceSAlan Somers 
9808eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
9818eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
9828eecd9ceSAlan Somers 	/*
9838eecd9ceSAlan Somers 	 * Writes of bufsize-bytes each should be clustered into greater sizes.
9848eecd9ceSAlan Somers 	 * The amount of clustering is adaptive, so the first write actually
9858eecd9ceSAlan Somers 	 * issued will be 2x bufsize and subsequent writes may be larger
9868eecd9ceSAlan Somers 	 */
9878eecd9ceSAlan Somers 	expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
9888eecd9ceSAlan Somers 	expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
9898eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
9908eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
9918eecd9ceSAlan Somers 
9928eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
9938eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
9948eecd9ceSAlan Somers 
9958eecd9ceSAlan Somers 	for (i = 0; i < 4; i++) {
9968eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
9978eecd9ceSAlan Somers 			<< strerror(errno);
9988eecd9ceSAlan Somers 	}
9998eecd9ceSAlan Somers 	close(fd);
10007ee7e409SAlan Somers 	free(wbuf2x);
10017ee7e409SAlan Somers 	free(wbuf);
10028eecd9ceSAlan Somers }
10038eecd9ceSAlan Somers 
10048eecd9ceSAlan Somers /*
10058eecd9ceSAlan Somers  * When clustering writes, an I/O error to any of the cluster's children should
10068eecd9ceSAlan Somers  * not panic the system on unmount
10078eecd9ceSAlan Somers  */
10088eecd9ceSAlan Somers /*
1009425bbe9eSAlan Somers  * Regression test for bug 238585
10108eecd9ceSAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
10118eecd9ceSAlan Somers  */
1012425bbe9eSAlan Somers TEST_F(WriteCluster, cluster_write_err)
10138eecd9ceSAlan Somers {
10148eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
10158eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
10168eecd9ceSAlan Somers 	uint64_t ino = 42;
10178eecd9ceSAlan Somers 	int i, fd;
10188eecd9ceSAlan Somers 	void *wbuf;
10196ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
10206ca3b02bSAlan Somers 	off_t filesize = 4 * bufsize;
10218eecd9ceSAlan Somers 
10228eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
10235a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf) << strerror(errno);
10248eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
10258eecd9ceSAlan Somers 
10268eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
10278eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
10288eecd9ceSAlan Somers 	EXPECT_CALL(*m_mock, process(
10298eecd9ceSAlan Somers 		ResultOf([=](auto in) {
10308eecd9ceSAlan Somers 			return (in.header.opcode == FUSE_WRITE);
10318eecd9ceSAlan Somers 		}, Eq(true)),
10328eecd9ceSAlan Somers 		_)
10338eecd9ceSAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
10348eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
10358eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
10368eecd9ceSAlan Somers 
10378eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
10388eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
10398eecd9ceSAlan Somers 
10408eecd9ceSAlan Somers 	for (i = 0; i < 3; i++) {
10418eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
10428eecd9ceSAlan Somers 			<< strerror(errno);
10438eecd9ceSAlan Somers 	}
10448eecd9ceSAlan Somers 	close(fd);
10457ee7e409SAlan Somers 	free(wbuf);
10468eecd9ceSAlan Somers }
10478eecd9ceSAlan Somers 
10489821f1d3SAlan Somers /*
10495fccbf31SAlan Somers  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
10505fccbf31SAlan Somers  * server.  The FUSE protocol explicitly allows that.
10515fccbf31SAlan Somers  */
10525fccbf31SAlan Somers TEST_F(WriteBack, rmw)
10535fccbf31SAlan Somers {
10545fccbf31SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
10555fccbf31SAlan Somers 	const char RELPATH[] = "some_file.txt";
10565fccbf31SAlan Somers 	const char *CONTENTS = "abcdefgh";
10575fccbf31SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
10585fccbf31SAlan Somers 	uint64_t ino = 42;
10595fccbf31SAlan Somers 	uint64_t offset = 1;
10605fccbf31SAlan Somers 	off_t fsize = 10;
10615fccbf31SAlan Somers 	int fd;
10625fccbf31SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
10635fccbf31SAlan Somers 
1064cad67791SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
10655fccbf31SAlan Somers 	expect_open(ino, 0, 1);
1066d4fd0c81SAlan Somers 	expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
106784879e46SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
10685fccbf31SAlan Somers 
10695fccbf31SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
1070d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
10715fccbf31SAlan Somers 
10725fccbf31SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
10735fccbf31SAlan Somers 		<< strerror(errno);
10747fc0921dSAlan Somers 	leak(fd);
10755fccbf31SAlan Somers }
10765fccbf31SAlan Somers 
10775fccbf31SAlan Somers /*
10789821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
10799821f1d3SAlan Somers  */
108084879e46SAlan Somers TEST_F(WriteBack, cache)
10819821f1d3SAlan Somers {
10829821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
10839821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
10849821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
10859821f1d3SAlan Somers 	uint64_t ino = 42;
10869821f1d3SAlan Somers 	int fd;
10879821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
10888e765737SAlan Somers 	uint8_t readbuf[bufsize];
10899821f1d3SAlan Somers 
10909821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
10919821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1092bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
10939821f1d3SAlan Somers 
10949821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1095d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
10969821f1d3SAlan Somers 
10979821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
10989821f1d3SAlan Somers 	/*
10999821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
11009821f1d3SAlan Somers 	 * filesystem daemon
11019821f1d3SAlan Somers 	 */
11029821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
11039821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
11047fc0921dSAlan Somers 	leak(fd);
11059821f1d3SAlan Somers }
11069821f1d3SAlan Somers 
11079821f1d3SAlan Somers /*
11089821f1d3SAlan Somers  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
11099821f1d3SAlan Somers  * an odd test, because it would be unusual to use O_DIRECT for writes but not
11109821f1d3SAlan Somers  * reads.
11119821f1d3SAlan Somers  */
11129821f1d3SAlan Somers TEST_F(WriteBack, o_direct)
11139821f1d3SAlan Somers {
11149821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
11159821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
11169821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
11179821f1d3SAlan Somers 	uint64_t ino = 42;
11189821f1d3SAlan Somers 	int fd;
11199821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
11208e765737SAlan Somers 	uint8_t readbuf[bufsize];
11219821f1d3SAlan Somers 
11229821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
11239821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1124bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1125bda39894SAlan Somers 		CONTENTS);
11269821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
11279821f1d3SAlan Somers 
11289821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
1129d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
11309821f1d3SAlan Somers 
11319821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
11329821f1d3SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
11339821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
11349821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
11359821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
11367fc0921dSAlan Somers 	leak(fd);
11379821f1d3SAlan Somers }
11389821f1d3SAlan Somers 
1139a62772a7SAlan Somers TEST_F(WriteBack, direct_io)
1140a62772a7SAlan Somers {
1141a62772a7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1142a62772a7SAlan Somers 	const char RELPATH[] = "some_file.txt";
1143a62772a7SAlan Somers 	const char *CONTENTS = "abcdefgh";
1144a62772a7SAlan Somers 	uint64_t ino = 42;
1145a62772a7SAlan Somers 	int fd;
1146a62772a7SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1147a62772a7SAlan Somers 	uint8_t readbuf[bufsize];
1148a62772a7SAlan Somers 
1149a62772a7SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1150a62772a7SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
1151a62772a7SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1152a62772a7SAlan Somers 		CONTENTS);
1153a62772a7SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1154a62772a7SAlan Somers 
1155a62772a7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1156d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1157a62772a7SAlan Somers 
1158a62772a7SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1159a62772a7SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
1160a62772a7SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1161a62772a7SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1162a62772a7SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1163a62772a7SAlan Somers 	leak(fd);
1164a62772a7SAlan Somers }
1165a62772a7SAlan Somers 
1166a62772a7SAlan Somers /*
1167a62772a7SAlan Somers  * mmap should still be possible even if the server used direct_io.  Mmap will
1168a62772a7SAlan Somers  * still use the cache, though.
1169a62772a7SAlan Somers  *
1170a62772a7SAlan Somers  * Regression test for bug 247276
1171a62772a7SAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276
1172a62772a7SAlan Somers  */
1173a62772a7SAlan Somers TEST_F(WriteBack, mmap_direct_io)
1174a62772a7SAlan Somers {
1175a62772a7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1176a62772a7SAlan Somers 	const char RELPATH[] = "some_file.txt";
1177a62772a7SAlan Somers 	const char *CONTENTS = "abcdefgh";
1178a62772a7SAlan Somers 	uint64_t ino = 42;
1179a62772a7SAlan Somers 	int fd;
1180a62772a7SAlan Somers 	size_t len;
1181a62772a7SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1182a62772a7SAlan Somers 	void *p, *zeros;
1183a62772a7SAlan Somers 
1184a62772a7SAlan Somers 	len = getpagesize();
1185a62772a7SAlan Somers 	zeros = calloc(1, len);
1186a62772a7SAlan Somers 	ASSERT_NE(nullptr, zeros);
1187a62772a7SAlan Somers 
1188a62772a7SAlan Somers 	expect_lookup(RELPATH, ino, len);
1189a62772a7SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
1190a62772a7SAlan Somers 	expect_read(ino, 0, len, len, zeros);
1191a62772a7SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
1192a62772a7SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros);
1193a62772a7SAlan Somers 	expect_release(ino, ReturnErrno(0));
1194a62772a7SAlan Somers 
1195a62772a7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1196d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1197a62772a7SAlan Somers 
1198a62772a7SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1199a62772a7SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1200a62772a7SAlan Somers 
1201a62772a7SAlan Somers 	memmove((uint8_t*)p, CONTENTS, bufsize);
1202a62772a7SAlan Somers 
1203a62772a7SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
1204a62772a7SAlan Somers 	close(fd);	// Write mmap'd data on close
1205a62772a7SAlan Somers 
1206a62772a7SAlan Somers 	free(zeros);
1207a62772a7SAlan Somers }
1208a62772a7SAlan Somers 
12099821f1d3SAlan Somers /*
121084879e46SAlan Somers  * When mounted with -o async, the writeback cache mode should delay writes
121184879e46SAlan Somers  */
121284879e46SAlan Somers TEST_F(WriteBackAsync, delay)
121384879e46SAlan Somers {
121484879e46SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
121584879e46SAlan Somers 	const char RELPATH[] = "some_file.txt";
121684879e46SAlan Somers 	const char *CONTENTS = "abcdefgh";
121784879e46SAlan Somers 	uint64_t ino = 42;
121884879e46SAlan Somers 	int fd;
121984879e46SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
122084879e46SAlan Somers 
122184879e46SAlan Somers 	expect_lookup(RELPATH, ino, 0);
122284879e46SAlan Somers 	expect_open(ino, 0, 1);
122384879e46SAlan Somers 	/* Write should be cached, but FUSE_WRITE shouldn't be sent */
122484879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
122584879e46SAlan Somers 		ResultOf([=](auto in) {
122684879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE);
122784879e46SAlan Somers 		}, Eq(true)),
122884879e46SAlan Somers 		_)
122984879e46SAlan Somers 	).Times(0);
123084879e46SAlan Somers 
123184879e46SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1232d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
123384879e46SAlan Somers 
123484879e46SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
123584879e46SAlan Somers 
123684879e46SAlan Somers 	/* Don't close the file because that would flush the cache */
12378e765737SAlan Somers 	leak(fd);
123884879e46SAlan Somers }
123984879e46SAlan Somers 
124084879e46SAlan Somers /*
1241669a092aSAlan Somers  * A direct write should not evict dirty cached data from outside of its own
1242669a092aSAlan Somers  * byte range.
1243669a092aSAlan Somers  */
1244669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached)
1245669a092aSAlan Somers {
1246669a092aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1247669a092aSAlan Somers 	const char RELPATH[] = "some_file.txt";
1248669a092aSAlan Somers 	const char CONTENTS0[] = "abcdefgh";
1249669a092aSAlan Somers 	const char CONTENTS1[] = "ijklmnop";
1250669a092aSAlan Somers 	uint64_t ino = 42;
1251669a092aSAlan Somers 	int fd;
1252669a092aSAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
1253669a092aSAlan Somers 	ssize_t fsize = 2 * m_maxbcachebuf;
1254669a092aSAlan Somers 	char readbuf[bufsize];
1255669a092aSAlan Somers 	void *zeros;
1256669a092aSAlan Somers 
1257669a092aSAlan Somers 	zeros = calloc(1, m_maxbcachebuf);
1258669a092aSAlan Somers 	ASSERT_NE(nullptr, zeros);
1259669a092aSAlan Somers 
1260669a092aSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
1261669a092aSAlan Somers 	expect_open(ino, 0, 1);
1262669a092aSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros);
1263669a092aSAlan Somers 	FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0,
1264669a092aSAlan Somers 		CONTENTS1);
1265669a092aSAlan Somers 
1266669a092aSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1267d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1268669a092aSAlan Somers 
1269669a092aSAlan Somers 	// Cache first block with dirty data.  This will entail first reading
1270669a092aSAlan Somers 	// the existing data.
1271669a092aSAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0))
1272669a092aSAlan Somers 		<< strerror(errno);
1273669a092aSAlan Somers 
1274669a092aSAlan Somers 	// Write directly to second block
1275669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1276669a092aSAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf))
1277669a092aSAlan Somers 		<< strerror(errno);
1278669a092aSAlan Somers 
1279669a092aSAlan Somers 	// Read from the first block again.  Should be serviced by cache.
1280669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1281669a092aSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno);
1282669a092aSAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS0);
1283669a092aSAlan Somers 
1284669a092aSAlan Somers 	leak(fd);
1285669a092aSAlan Somers 	free(zeros);
1286669a092aSAlan Somers }
1287669a092aSAlan Somers 
1288669a092aSAlan Somers /*
1289669a092aSAlan Somers  * If a direct io write partially overlaps one or two blocks of dirty cached
1290669a092aSAlan Somers  * data, No dirty data should be lost.  Admittedly this is a weird test,
1291669a092aSAlan Somers  * because it would be unusual to use O_DIRECT and the writeback cache.
1292669a092aSAlan Somers  */
1293669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block)
1294669a092aSAlan Somers {
1295669a092aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1296669a092aSAlan Somers 	const char RELPATH[] = "some_file.txt";
1297669a092aSAlan Somers 	uint64_t ino = 42;
1298669a092aSAlan Somers 	int fd;
1299669a092aSAlan Somers 	off_t bs = m_maxbcachebuf;
1300669a092aSAlan Somers 	ssize_t fsize = 3 * bs;
1301669a092aSAlan Somers 	void *readbuf, *zeros, *ones, *zeroones, *onezeros;
1302669a092aSAlan Somers 
1303669a092aSAlan Somers 	readbuf = malloc(bs);
1304669a092aSAlan Somers 	ASSERT_NE(nullptr, readbuf) << strerror(errno);
1305669a092aSAlan Somers 	zeros = calloc(1, 3 * bs);
1306669a092aSAlan Somers 	ASSERT_NE(nullptr, zeros);
1307669a092aSAlan Somers 	ones = calloc(1, 2 * bs);
1308669a092aSAlan Somers 	ASSERT_NE(nullptr, ones);
1309669a092aSAlan Somers 	memset(ones, 1, 2 * bs);
1310669a092aSAlan Somers 	zeroones = calloc(1, bs);
1311669a092aSAlan Somers 	ASSERT_NE(nullptr, zeroones);
1312669a092aSAlan Somers 	memset((uint8_t*)zeroones + bs / 2, 1, bs / 2);
1313669a092aSAlan Somers 	onezeros = calloc(1, bs);
1314669a092aSAlan Somers 	ASSERT_NE(nullptr, onezeros);
1315669a092aSAlan Somers 	memset(onezeros, 1, bs / 2);
1316669a092aSAlan Somers 
1317669a092aSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
1318669a092aSAlan Somers 	expect_open(ino, 0, 1);
1319669a092aSAlan Somers 
1320669a092aSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1321d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1322669a092aSAlan Somers 
1323669a092aSAlan Somers 	/* Cache first and third blocks with dirty data.  */
1324669a092aSAlan Somers 	ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno);
1325669a092aSAlan Somers 
1326669a092aSAlan Somers 	/*
1327669a092aSAlan Somers 	 * Write directly to all three blocks.  The partially written blocks
1328669a092aSAlan Somers 	 * will be flushed because they're dirty.
1329669a092aSAlan Somers 	 */
1330669a092aSAlan Somers 	FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros);
1331669a092aSAlan Somers 	FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros);
1332669a092aSAlan Somers 	/* The direct write is split in two because of the m_maxwrite value */
1333669a092aSAlan Somers 	FuseTest::expect_write(ino,     bs / 2, bs, bs, 0, 0, ones);
1334669a092aSAlan Somers 	FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones);
1335669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1336669a092aSAlan Somers 	ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno);
1337669a092aSAlan Somers 
1338669a092aSAlan Somers 	/*
1339669a092aSAlan Somers 	 * Read from both the valid and invalid portions of the first and third
1340669a092aSAlan Somers 	 * blocks again.  This will entail FUSE_READ operations because these
1341669a092aSAlan Somers 	 * blocks were invalidated by the direct write.
1342669a092aSAlan Somers 	 */
1343669a092aSAlan Somers 	expect_read(ino, 0, bs, bs, zeroones);
1344669a092aSAlan Somers 	expect_read(ino, 2 * bs, bs, bs, onezeros);
1345669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1346669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno);
1347669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1348669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2))
1349669a092aSAlan Somers 		<< strerror(errno);
1350669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1351669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2))
1352669a092aSAlan Somers 		<< strerror(errno);
1353669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1354669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs))
1355669a092aSAlan Somers 		<< strerror(errno);
1356669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1357669a092aSAlan Somers 
1358669a092aSAlan Somers 	leak(fd);
1359669a092aSAlan Somers 	free(zeroones);
1360669a092aSAlan Somers 	free(onezeros);
1361669a092aSAlan Somers 	free(ones);
1362669a092aSAlan Somers 	free(zeros);
1363669a092aSAlan Somers 	free(readbuf);
1364669a092aSAlan Somers }
1365669a092aSAlan Somers 
1366669a092aSAlan Somers /*
1367aef22f2dSAlan Somers  * In WriteBack mode, writes may be cached beyond what the server thinks is the
1368aef22f2dSAlan Somers  * EOF.  In this case, a short read at EOF should _not_ cause fusefs to update
1369aef22f2dSAlan Somers  * the file's size.
1370aef22f2dSAlan Somers  */
1371aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof)
1372aef22f2dSAlan Somers {
1373aef22f2dSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1374aef22f2dSAlan Somers 	const char RELPATH[] = "some_file.txt";
1375aef22f2dSAlan Somers 	const char *CONTENTS0 = "abcdefgh";
1376aef22f2dSAlan Somers 	const char *CONTENTS1 = "ijklmnop";
1377aef22f2dSAlan Somers 	uint64_t ino = 42;
1378aef22f2dSAlan Somers 	int fd;
1379aef22f2dSAlan Somers 	off_t offset = m_maxbcachebuf;
1380aef22f2dSAlan Somers 	ssize_t wbufsize = strlen(CONTENTS1);
1381aef22f2dSAlan Somers 	off_t old_filesize = (off_t)strlen(CONTENTS0);
1382aef22f2dSAlan Somers 	ssize_t rbufsize = 2 * old_filesize;
1383aef22f2dSAlan Somers 	char readbuf[rbufsize];
1384aef22f2dSAlan Somers 	size_t holesize = rbufsize - old_filesize;
1385aef22f2dSAlan Somers 	char hole[holesize];
1386aef22f2dSAlan Somers 	struct stat sb;
1387aef22f2dSAlan Somers 	ssize_t r;
1388aef22f2dSAlan Somers 
1389aef22f2dSAlan Somers 	expect_lookup(RELPATH, ino, 0);
1390aef22f2dSAlan Somers 	expect_open(ino, 0, 1);
1391aef22f2dSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
1392aef22f2dSAlan Somers 
1393aef22f2dSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1394d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1395aef22f2dSAlan Somers 
1396aef22f2dSAlan Somers 	/* Write and cache data beyond EOF */
1397aef22f2dSAlan Somers 	ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
1398aef22f2dSAlan Somers 		<< strerror(errno);
1399aef22f2dSAlan Somers 
1400aef22f2dSAlan Somers 	/* Read from the old EOF */
1401aef22f2dSAlan Somers 	r = pread(fd, readbuf, rbufsize, 0);
1402aef22f2dSAlan Somers 	ASSERT_LE(0, r) << strerror(errno);
1403aef22f2dSAlan Somers 	EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
1404aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
1405aef22f2dSAlan Somers 	bzero(hole, holesize);
1406aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
1407aef22f2dSAlan Somers 
1408aef22f2dSAlan Somers 	/* The file's size should still be what was established by pwrite */
1409aef22f2dSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1410aef22f2dSAlan Somers 	EXPECT_EQ(offset + wbufsize, sb.st_size);
14117fc0921dSAlan Somers 	leak(fd);
1412aef22f2dSAlan Somers }
1413aef22f2dSAlan Somers 
1414aef22f2dSAlan Somers /*
1415788af953SAlan Somers  * When a file has dirty writes that haven't been flushed, the server's notion
1416788af953SAlan Somers  * of its mtime and ctime will be wrong.  The kernel should ignore those if it
1417788af953SAlan Somers  * gets them from a FUSE_GETATTR before flushing.
1418788af953SAlan Somers  */
1419788af953SAlan Somers TEST_F(WriteBackAsync, timestamps)
1420788af953SAlan Somers {
1421788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1422788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1423788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1424788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1425788af953SAlan Somers 	uint64_t ino = 42;
1426788af953SAlan Somers 	uint64_t attr_valid = 0;
1427788af953SAlan Somers 	uint64_t attr_valid_nsec = 0;
1428788af953SAlan Somers 	uint64_t server_time = 12345;
1429788af953SAlan Somers 	mode_t mode = S_IFREG | 0644;
1430788af953SAlan Somers 	int fd;
1431788af953SAlan Somers 
1432788af953SAlan Somers 	struct stat sb;
1433788af953SAlan Somers 
1434788af953SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
1435788af953SAlan Somers 	.WillRepeatedly(Invoke(
1436788af953SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
1437788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
1438788af953SAlan Somers 		out.body.entry.attr.mode = mode;
1439788af953SAlan Somers 		out.body.entry.nodeid = ino;
1440788af953SAlan Somers 		out.body.entry.attr.nlink = 1;
1441788af953SAlan Somers 		out.body.entry.attr_valid = attr_valid;
1442788af953SAlan Somers 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
1443788af953SAlan Somers 	})));
1444788af953SAlan Somers 	expect_open(ino, 0, 1);
1445788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1446788af953SAlan Somers 		ResultOf([=](auto in) {
1447788af953SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
1448788af953SAlan Somers 				in.header.nodeid == ino);
1449788af953SAlan Somers 		}, Eq(true)),
1450788af953SAlan Somers 		_)
1451788af953SAlan Somers 	).WillRepeatedly(Invoke(
1452788af953SAlan Somers 	ReturnImmediate([=](auto i __unused, auto& out) {
1453788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1454788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1455788af953SAlan Somers 		out.body.attr.attr.mode = mode;
1456788af953SAlan Somers 		out.body.attr.attr_valid = attr_valid;
1457788af953SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
1458788af953SAlan Somers 		out.body.attr.attr.atime = server_time;
1459788af953SAlan Somers 		out.body.attr.attr.mtime = server_time;
1460788af953SAlan Somers 		out.body.attr.attr.ctime = server_time;
1461788af953SAlan Somers 	})));
1462788af953SAlan Somers 
1463788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1464d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1465788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1466788af953SAlan Somers 
1467788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1468788af953SAlan Somers 	EXPECT_EQ((time_t)server_time, sb.st_atime);
1469788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_mtime);
1470788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_ctime);
14718e765737SAlan Somers 
14728e765737SAlan Somers 	leak(fd);
1473788af953SAlan Somers }
1474788af953SAlan Somers 
1475788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */
1476788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr)
1477788af953SAlan Somers {
1478788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1479788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1480788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1481788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1482788af953SAlan Somers 	uint64_t ino = 42;
1483788af953SAlan Somers 	const mode_t newmode = 0755;
1484788af953SAlan Somers 	int fd;
1485788af953SAlan Somers 
1486788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1487788af953SAlan Somers 	expect_open(ino, 0, 1);
1488788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1489788af953SAlan Somers 		ResultOf([=](auto in) {
14900a8fe2d3SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1491788af953SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1492788af953SAlan Somers 				in.header.nodeid == ino &&
1493788af953SAlan Somers 				in.body.setattr.valid == valid);
1494788af953SAlan Somers 		}, Eq(true)),
1495788af953SAlan Somers 		_)
1496788af953SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1497788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1498788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1499788af953SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1500788af953SAlan Somers 	})));
1501788af953SAlan Somers 
1502788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1503d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1504788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1505788af953SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
15068e765737SAlan Somers 
15078e765737SAlan Somers 	leak(fd);
1508788af953SAlan Somers }
1509788af953SAlan Somers 
1510fef46454SAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */
1511fef46454SAlan Somers TEST_P(TimeGran, timestamps_during_setattr)
1512fef46454SAlan Somers {
1513fef46454SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1514fef46454SAlan Somers 	const char RELPATH[] = "some_file.txt";
1515fef46454SAlan Somers 	const char *CONTENTS = "abcdefgh";
1516fef46454SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1517fef46454SAlan Somers 	uint64_t ino = 42;
1518fef46454SAlan Somers 	const mode_t newmode = 0755;
1519fef46454SAlan Somers 	int fd;
1520fef46454SAlan Somers 
1521fef46454SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1522fef46454SAlan Somers 	expect_open(ino, 0, 1);
1523fef46454SAlan Somers 	EXPECT_CALL(*m_mock, process(
1524fef46454SAlan Somers 		ResultOf([=](auto in) {
1525fef46454SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1526fef46454SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1527fef46454SAlan Somers 				in.header.nodeid == ino &&
1528fef46454SAlan Somers 				in.body.setattr.valid == valid &&
1529fef46454SAlan Somers 				in.body.setattr.mtimensec % m_time_gran == 0 &&
1530fef46454SAlan Somers 				in.body.setattr.ctimensec % m_time_gran == 0);
1531fef46454SAlan Somers 		}, Eq(true)),
1532fef46454SAlan Somers 		_)
1533fef46454SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1534fef46454SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1535fef46454SAlan Somers 		out.body.attr.attr.ino = ino;
1536fef46454SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1537fef46454SAlan Somers 	})));
1538fef46454SAlan Somers 
1539fef46454SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1540d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1541fef46454SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1542fef46454SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
15438e765737SAlan Somers 
15448e765737SAlan Somers 	leak(fd);
1545fef46454SAlan Somers }
1546fef46454SAlan Somers 
1547fef46454SAlan Somers INSTANTIATE_TEST_CASE_P(RA, TimeGran, Range(0u, 10u));
1548fef46454SAlan Somers 
1549788af953SAlan Somers /*
15509821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
15519821f1d3SAlan Somers  */
1552f8ebf1cdSAlan Somers TEST_F(Write, writethrough)
15539821f1d3SAlan Somers {
15549821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
15559821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
15569821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
15579821f1d3SAlan Somers 	uint64_t ino = 42;
15589821f1d3SAlan Somers 	int fd;
15599821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
15608e765737SAlan Somers 	uint8_t readbuf[bufsize];
15619821f1d3SAlan Somers 
15629821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
15639821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1564bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
15659821f1d3SAlan Somers 
15669821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1567d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
15689821f1d3SAlan Somers 
15699821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
15709821f1d3SAlan Somers 	/*
15719821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
15729821f1d3SAlan Somers 	 * filesystem daemon
15739821f1d3SAlan Somers 	 */
1574b5aaf286SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
15759821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
15767fc0921dSAlan Somers 	leak(fd);
15779821f1d3SAlan Somers }
15789821f1d3SAlan Somers 
15790d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */
15800d3a88d7SAlan Somers TEST_F(Write, update_file_size)
15819821f1d3SAlan Somers {
15829821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
15839821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
15849821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
15859821f1d3SAlan Somers 	struct stat sb;
15869821f1d3SAlan Somers 	uint64_t ino = 42;
15879821f1d3SAlan Somers 	int fd;
15889821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
15899821f1d3SAlan Somers 
15909821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
15919821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1592bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
15939821f1d3SAlan Somers 
15949821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1595d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
15969821f1d3SAlan Somers 
15979821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
15989821f1d3SAlan Somers 	/* Get cached attributes */
15999821f1d3SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
16009821f1d3SAlan Somers 	ASSERT_EQ(bufsize, sb.st_size);
16017fc0921dSAlan Somers 	leak(fd);
16029821f1d3SAlan Somers }
1603