xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision 032a5bd55b3a003d3560435422a95f27f91685fe)
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 static sig_atomic_t s_sigxfsz;
56a639731bSAlan Somers 
57a639731bSAlan Somers void SetUp() {
58a639731bSAlan Somers 	s_sigxfsz = 0;
59a639731bSAlan Somers 	FuseTest::SetUp();
60a639731bSAlan Somers }
61a639731bSAlan Somers 
62a639731bSAlan Somers void TearDown() {
63a639731bSAlan Somers 	struct sigaction sa;
64a639731bSAlan Somers 
65a639731bSAlan Somers 	bzero(&sa, sizeof(sa));
66a639731bSAlan Somers 	sa.sa_handler = SIG_DFL;
67a639731bSAlan Somers 	sigaction(SIGXFSZ, &sa, NULL);
68a639731bSAlan Somers 
69a639731bSAlan Somers 	FuseTest::TearDown();
70a639731bSAlan Somers }
719821f1d3SAlan Somers 
729821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
739821f1d3SAlan Somers {
749821f1d3SAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
759821f1d3SAlan Somers }
769821f1d3SAlan Somers 
779821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r)
789821f1d3SAlan Somers {
799821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
809821f1d3SAlan Somers 		ResultOf([=](auto in) {
8129edc611SAlan Somers 			return (in.header.opcode == FUSE_RELEASE &&
8229edc611SAlan Somers 				in.header.nodeid == ino);
839821f1d3SAlan Somers 		}, Eq(true)),
849821f1d3SAlan Somers 		_)
859821f1d3SAlan Somers 	).WillRepeatedly(Invoke(r));
869821f1d3SAlan Somers }
879821f1d3SAlan Somers 
88bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
89bda39894SAlan Somers 	uint64_t osize, const void *contents)
90bda39894SAlan Somers {
91bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
92bda39894SAlan Somers }
93bda39894SAlan Somers 
9484879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */
9584879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
9684879e46SAlan Somers 	const void *contents)
9784879e46SAlan Somers {
9884879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
9984879e46SAlan Somers 		ResultOf([=](auto in) {
10084879e46SAlan Somers 			const char *buf = (const char*)in.body.bytes +
10184879e46SAlan Somers 				sizeof(struct fuse_write_in);
10284879e46SAlan Somers 
10384879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE &&
10484879e46SAlan Somers 				in.header.nodeid == ino &&
10584879e46SAlan Somers 				in.body.write.offset == offset  &&
10684879e46SAlan Somers 				in.body.write.size == size &&
10784879e46SAlan Somers 				0 == bcmp(buf, contents, size));
10884879e46SAlan Somers 		}, Eq(true)),
10984879e46SAlan Somers 		_)
11084879e46SAlan Somers 	).Times(AtMost(1))
11184879e46SAlan Somers 	.WillRepeatedly(Invoke(
11284879e46SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
11384879e46SAlan Somers 			SET_OUT_HEADER_LEN(out, write);
11484879e46SAlan Somers 			out.body.write.size = size;
11584879e46SAlan Somers 		})
11684879e46SAlan Somers 	));
11784879e46SAlan Somers }
11884879e46SAlan Somers 
1199821f1d3SAlan Somers };
1209821f1d3SAlan Somers 
121a639731bSAlan Somers sig_atomic_t Write::s_sigxfsz = 0;
122a639731bSAlan Somers 
12316bd2d47SAlan Somers class Write_7_8: public FuseTest {
12416bd2d47SAlan Somers 
12516bd2d47SAlan Somers public:
12616bd2d47SAlan Somers virtual void SetUp() {
12716bd2d47SAlan Somers 	m_kernel_minor_version = 8;
12816bd2d47SAlan Somers 	FuseTest::SetUp();
12916bd2d47SAlan Somers }
13016bd2d47SAlan Somers 
13116bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
13216bd2d47SAlan Somers {
13316bd2d47SAlan Somers 	FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
13416bd2d47SAlan Somers }
13516bd2d47SAlan Somers 
13616bd2d47SAlan Somers };
13716bd2d47SAlan Somers 
1389821f1d3SAlan Somers class AioWrite: public Write {
1399821f1d3SAlan Somers virtual void SetUp() {
140c2265ae7SAlan Somers 	if (!is_unsafe_aio_enabled())
1419821f1d3SAlan Somers 		GTEST_SKIP() <<
1429821f1d3SAlan Somers 			"vfs.aio.enable_unsafe must be set for this test";
143c2265ae7SAlan Somers 	FuseTest::SetUp();
1449821f1d3SAlan Somers }
1459821f1d3SAlan Somers };
1469821f1d3SAlan Somers 
1479821f1d3SAlan Somers /* Tests for the writeback cache mode */
1489821f1d3SAlan Somers class WriteBack: public Write {
149bda39894SAlan Somers public:
1509821f1d3SAlan Somers virtual void SetUp() {
151f8ebf1cdSAlan Somers 	m_init_flags |= FUSE_WRITEBACK_CACHE;
1529821f1d3SAlan Somers 	FuseTest::SetUp();
1539821f1d3SAlan Somers 	if (IsSkipped())
1549821f1d3SAlan Somers 		return;
1559821f1d3SAlan Somers }
1569821f1d3SAlan Somers 
157bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
158bda39894SAlan Somers 	uint64_t osize, const void *contents)
159bda39894SAlan Somers {
160bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
161bda39894SAlan Somers 		contents);
162bda39894SAlan Somers }
1639821f1d3SAlan Somers };
1649821f1d3SAlan Somers 
16584879e46SAlan Somers class WriteBackAsync: public WriteBack {
16684879e46SAlan Somers public:
16784879e46SAlan Somers virtual void SetUp() {
16884879e46SAlan Somers 	m_async = true;
169f928dbcbSAlan Somers 	m_maxwrite = 65536;
17084879e46SAlan Somers 	WriteBack::SetUp();
17184879e46SAlan Somers }
17284879e46SAlan Somers };
17384879e46SAlan Somers 
174fef46454SAlan Somers class TimeGran: public WriteBackAsync, public WithParamInterface<unsigned> {
175fef46454SAlan Somers public:
176fef46454SAlan Somers virtual void SetUp() {
177fef46454SAlan Somers 	m_time_gran = 1 << GetParam();
178fef46454SAlan Somers 	WriteBackAsync::SetUp();
179fef46454SAlan Somers }
180fef46454SAlan Somers };
181fef46454SAlan Somers 
1828eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */
1838eecd9ceSAlan Somers class WriteCluster: public WriteBack {
1848eecd9ceSAlan Somers public:
1858eecd9ceSAlan Somers virtual void SetUp() {
1868eecd9ceSAlan Somers 	m_async = true;
1878e765737SAlan Somers 	m_maxwrite = 1 << 25;	// Anything larger than MAXPHYS will suffice
1888eecd9ceSAlan Somers 	WriteBack::SetUp();
189f8ebf1cdSAlan Somers 	if (m_maxphys < 2 * DFLTPHYS)
190f8ebf1cdSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
191f8ebf1cdSAlan Somers 			<< " for this test";
192f2704f05SAlan Somers 	if (m_maxphys < 2 * m_maxbcachebuf)
1936ca3b02bSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf"
1946ca3b02bSAlan Somers 			<< " for this test";
1958eecd9ceSAlan Somers }
1968eecd9ceSAlan Somers };
1978eecd9ceSAlan Somers 
198f928dbcbSAlan Somers /* Tests relating to the server's max_write property */
199f928dbcbSAlan Somers class WriteMaxWrite: public Write {
200f928dbcbSAlan Somers public:
201f928dbcbSAlan Somers virtual void SetUp() {
202f928dbcbSAlan Somers 	/*
203f928dbcbSAlan Somers 	 * For this test, m_maxwrite must be less than either m_maxbcachebuf or
204f928dbcbSAlan Somers 	 * maxphys.
205f928dbcbSAlan Somers 	 */
206f928dbcbSAlan Somers 	m_maxwrite = 32768;
207f928dbcbSAlan Somers 	Write::SetUp();
208f928dbcbSAlan Somers }
209f928dbcbSAlan Somers };
210f928dbcbSAlan Somers 
211*032a5bd5SAlan Somers class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int>
212*032a5bd5SAlan Somers {};
213*032a5bd5SAlan Somers 
214a639731bSAlan Somers void sigxfsz_handler(int __unused sig) {
215a639731bSAlan Somers 	Write::s_sigxfsz = 1;
216a639731bSAlan Somers }
217a639731bSAlan Somers 
2189821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */
2199821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
2209821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write)
2219821f1d3SAlan Somers {
2229821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2239821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2249821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
2259821f1d3SAlan Somers 	uint64_t ino = 42;
2269821f1d3SAlan Somers 	uint64_t offset = 4096;
2279821f1d3SAlan Somers 	int fd;
2289821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
2299821f1d3SAlan Somers 	struct aiocb iocb, *piocb;
2309821f1d3SAlan Somers 
2319821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
2329821f1d3SAlan Somers 	expect_open(ino, 0, 1);
233bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
2349821f1d3SAlan Somers 
2359821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
236d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2379821f1d3SAlan Somers 
2389821f1d3SAlan Somers 	iocb.aio_nbytes = bufsize;
2399821f1d3SAlan Somers 	iocb.aio_fildes = fd;
2405a0b9a27SAlan Somers 	iocb.aio_buf = __DECONST(void *, CONTENTS);
2419821f1d3SAlan Somers 	iocb.aio_offset = offset;
2429821f1d3SAlan Somers 	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
2439821f1d3SAlan Somers 	ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
2449821f1d3SAlan Somers 	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
2457fc0921dSAlan Somers 	leak(fd);
2469821f1d3SAlan Somers }
2479821f1d3SAlan Somers 
2489821f1d3SAlan Somers /*
2499821f1d3SAlan Somers  * When a file is opened with O_APPEND, we should forward that flag to
2509821f1d3SAlan Somers  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
2519821f1d3SAlan Somers  * offset internally.  That way we'll work both with filesystems that
2529821f1d3SAlan Somers  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
2539821f1d3SAlan Somers  * simply use the offset).
2549821f1d3SAlan Somers  *
2559821f1d3SAlan Somers  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
2569821f1d3SAlan Somers  * Open.o_append test.
2579821f1d3SAlan Somers  */
2589821f1d3SAlan Somers TEST_F(Write, append)
2599821f1d3SAlan Somers {
2609821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
2619821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2629821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2639821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
2649821f1d3SAlan Somers 	uint64_t ino = 42;
2659821f1d3SAlan Somers 	/*
2669821f1d3SAlan Somers 	 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
2679821f1d3SAlan Somers 	 * using writeback caching
2689821f1d3SAlan Somers 	 */
2699821f1d3SAlan Somers 	uint64_t initial_offset = m_maxbcachebuf;
2709821f1d3SAlan Somers 	int fd;
2719821f1d3SAlan Somers 
2729821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
2739821f1d3SAlan Somers 	expect_open(ino, 0, 1);
274bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
2759821f1d3SAlan Somers 
2769821f1d3SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
2779821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
278d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2799821f1d3SAlan Somers 
2809821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
2817fc0921dSAlan Somers 	leak(fd);
2829821f1d3SAlan Somers }
2839821f1d3SAlan Somers 
284a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */
285a87e0831SAlan Somers TEST_F(Write, append_to_cached)
286a87e0831SAlan Somers {
287a87e0831SAlan Somers 	const ssize_t BUFSIZE = 9;
288a87e0831SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
289a87e0831SAlan Somers 	const char RELPATH[] = "some_file.txt";
290a87e0831SAlan Somers 	char *oldcontents, *oldbuf;
291a87e0831SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
292a87e0831SAlan Somers 	uint64_t ino = 42;
293a87e0831SAlan Somers 	/*
294a87e0831SAlan Somers 	 * Set offset in between maxbcachebuf boundary to test buffer handling
295a87e0831SAlan Somers 	 */
296a87e0831SAlan Somers 	uint64_t oldsize = m_maxbcachebuf / 2;
297a87e0831SAlan Somers 	int fd;
298a87e0831SAlan Somers 
299a87e0831SAlan Somers 	oldcontents = (char*)calloc(1, oldsize);
3005a0b9a27SAlan Somers 	ASSERT_NE(nullptr, oldcontents) << strerror(errno);
301a87e0831SAlan Somers 	oldbuf = (char*)malloc(oldsize);
3025a0b9a27SAlan Somers 	ASSERT_NE(nullptr, oldbuf) << strerror(errno);
303a87e0831SAlan Somers 
304a87e0831SAlan Somers 	expect_lookup(RELPATH, ino, oldsize);
305a87e0831SAlan Somers 	expect_open(ino, 0, 1);
306a87e0831SAlan Somers 	expect_read(ino, 0, oldsize, oldsize, oldcontents);
30784879e46SAlan Somers 	maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
308a87e0831SAlan Somers 
309a87e0831SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
310a87e0831SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
311d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
312a87e0831SAlan Somers 
313a87e0831SAlan Somers 	/* Read the old data into the cache */
314a87e0831SAlan Somers 	ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
315a87e0831SAlan Somers 		<< strerror(errno);
316a87e0831SAlan Somers 
317a87e0831SAlan Somers 	/* Write the new data.  There should be no more read operations */
318a87e0831SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3197fc0921dSAlan Somers 	leak(fd);
3207ee7e409SAlan Somers 	free(oldbuf);
3217ee7e409SAlan Somers 	free(oldcontents);
322a87e0831SAlan Somers }
323a87e0831SAlan Somers 
3249821f1d3SAlan Somers TEST_F(Write, append_direct_io)
3259821f1d3SAlan Somers {
3269821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
3279821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3289821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3299821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
3309821f1d3SAlan Somers 	uint64_t ino = 42;
3319821f1d3SAlan Somers 	uint64_t initial_offset = 4096;
3329821f1d3SAlan Somers 	int fd;
3339821f1d3SAlan Somers 
3349821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
3359821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
336bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
3379821f1d3SAlan Somers 
3389821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY | O_APPEND);
339d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
3409821f1d3SAlan Somers 
3419821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3427fc0921dSAlan Somers 	leak(fd);
3439821f1d3SAlan Somers }
3449821f1d3SAlan Somers 
3459821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */
3466af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache)
3479821f1d3SAlan Somers {
3489821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3499821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3509821f1d3SAlan Somers 	const char CONTENTS0[] = "abcdefgh";
3519821f1d3SAlan Somers 	const char CONTENTS1[] = "ijklmnop";
3529821f1d3SAlan Somers 	uint64_t ino = 42;
3539821f1d3SAlan Somers 	int fd;
3549821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
3559821f1d3SAlan Somers 	char readbuf[bufsize];
3569821f1d3SAlan Somers 
3579821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
3589821f1d3SAlan Somers 	expect_open(ino, 0, 1);
3599821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
360bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
3619821f1d3SAlan Somers 
3629821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
363d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
3649821f1d3SAlan Somers 
3659821f1d3SAlan Somers 	// Prime cache
3669821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3679821f1d3SAlan Somers 
3689821f1d3SAlan Somers 	// Write directly, evicting cache
3699821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
3709821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3719821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
3729821f1d3SAlan Somers 
3739821f1d3SAlan Somers 	// Read again.  Cache should be bypassed
3749821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
3759821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
3769821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3779821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3789821f1d3SAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS1);
3799821f1d3SAlan Somers 
3807fc0921dSAlan Somers 	leak(fd);
3819821f1d3SAlan Somers }
3829821f1d3SAlan Somers 
3839821f1d3SAlan Somers /*
38412292a99SAlan Somers  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
38512292a99SAlan Somers  * allowed to return a short write for that file handle.  However, if it does
38612292a99SAlan Somers  * then we should still do our darndest to handle it by resending the unwritten
38712292a99SAlan Somers  * portion.
3889821f1d3SAlan Somers  */
38912292a99SAlan Somers TEST_F(Write, indirect_io_short_write)
3909821f1d3SAlan Somers {
3919821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3929821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3939821f1d3SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
3949821f1d3SAlan Somers 	uint64_t ino = 42;
3959821f1d3SAlan Somers 	int fd;
3969821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
39712292a99SAlan Somers 	ssize_t bufsize0 = 11;
39812292a99SAlan Somers 	ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
39912292a99SAlan Somers 	const char *contents1 = CONTENTS + bufsize0;
4009821f1d3SAlan Somers 
4019821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
40212292a99SAlan Somers 	expect_open(ino, 0, 1);
403bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
404bda39894SAlan Somers 	expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
4059821f1d3SAlan Somers 
4069821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
407d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
4089821f1d3SAlan Somers 
4099821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4107fc0921dSAlan Somers 	leak(fd);
4119821f1d3SAlan Somers }
4129821f1d3SAlan Somers 
4139821f1d3SAlan Somers /*
41412292a99SAlan Somers  * When the direct_io option is used, filesystems are allowed to write less
41512292a99SAlan Somers  * data than requested.  We should return the short write to userland.
41612292a99SAlan Somers  */
41712292a99SAlan Somers TEST_F(Write, direct_io_short_write)
41812292a99SAlan Somers {
41912292a99SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
42012292a99SAlan Somers 	const char RELPATH[] = "some_file.txt";
42112292a99SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
42212292a99SAlan Somers 	uint64_t ino = 42;
42312292a99SAlan Somers 	int fd;
42412292a99SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
42512292a99SAlan Somers 	ssize_t halfbufsize = bufsize / 2;
42612292a99SAlan Somers 
42712292a99SAlan Somers 	expect_lookup(RELPATH, ino, 0);
42812292a99SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
429bda39894SAlan Somers 	expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
43012292a99SAlan Somers 
43112292a99SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
432d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
43312292a99SAlan Somers 
43412292a99SAlan Somers 	ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4357fc0921dSAlan Somers 	leak(fd);
43612292a99SAlan Somers }
43712292a99SAlan Somers 
43812292a99SAlan Somers /*
4399821f1d3SAlan Somers  * An insidious edge case: the filesystem returns a short write, and the
4409821f1d3SAlan Somers  * difference between what we requested and what it actually wrote crosses an
4419821f1d3SAlan Somers  * iov element boundary
4429821f1d3SAlan Somers  */
44312292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov)
4449821f1d3SAlan Somers {
4459821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4469821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4479821f1d3SAlan Somers 	const char *CONTENTS0 = "abcdefgh";
4489821f1d3SAlan Somers 	const char *CONTENTS1 = "ijklmnop";
4499821f1d3SAlan Somers 	const char *EXPECTED0 = "abcdefghijklmnop";
4509821f1d3SAlan Somers 	uint64_t ino = 42;
4519821f1d3SAlan Somers 	int fd;
4529821f1d3SAlan Somers 	ssize_t size0 = strlen(CONTENTS0) - 1;
4539821f1d3SAlan Somers 	ssize_t size1 = strlen(CONTENTS1) + 1;
4549821f1d3SAlan Somers 	ssize_t totalsize = size0 + size1;
4559821f1d3SAlan Somers 	struct iovec iov[2];
4569821f1d3SAlan Somers 
4579821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
4589821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
459bda39894SAlan Somers 	expect_write(ino, 0, totalsize, size0, EXPECTED0);
4609821f1d3SAlan Somers 
4619821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
462d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
4639821f1d3SAlan Somers 
4645a0b9a27SAlan Somers 	iov[0].iov_base = __DECONST(void*, CONTENTS0);
4659821f1d3SAlan Somers 	iov[0].iov_len = strlen(CONTENTS0);
4665a0b9a27SAlan Somers 	iov[1].iov_base = __DECONST(void*, CONTENTS1);
4679821f1d3SAlan Somers 	iov[1].iov_len = strlen(CONTENTS1);
46812292a99SAlan Somers 	ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
4697fc0921dSAlan Somers 	leak(fd);
4709821f1d3SAlan Somers }
4719821f1d3SAlan Somers 
472a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */
473a639731bSAlan Somers TEST_F(Write, rlimit_fsize)
474a639731bSAlan Somers {
475a639731bSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
476a639731bSAlan Somers 	const char RELPATH[] = "some_file.txt";
477a639731bSAlan Somers 	const char *CONTENTS = "abcdefgh";
478a639731bSAlan Somers 	struct rlimit rl;
479a639731bSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
480a639731bSAlan Somers 	off_t offset = 1'000'000'000;
481a639731bSAlan Somers 	uint64_t ino = 42;
482a639731bSAlan Somers 	int fd;
483a639731bSAlan Somers 
484a639731bSAlan Somers 	expect_lookup(RELPATH, ino, 0);
485a639731bSAlan Somers 	expect_open(ino, 0, 1);
486a639731bSAlan Somers 
487a639731bSAlan Somers 	rl.rlim_cur = offset;
488a639731bSAlan Somers 	rl.rlim_max = 10 * offset;
489a639731bSAlan Somers 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
490a639731bSAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
491a639731bSAlan Somers 
492a639731bSAlan Somers 	fd = open(FULLPATH, O_WRONLY);
493a639731bSAlan Somers 
494d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
495a639731bSAlan Somers 
496a639731bSAlan Somers 	ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
497a639731bSAlan Somers 	EXPECT_EQ(EFBIG, errno);
498a639731bSAlan Somers 	EXPECT_EQ(1, s_sigxfsz);
4997fc0921dSAlan Somers 	leak(fd);
500a639731bSAlan Somers }
501a639731bSAlan Somers 
5029821f1d3SAlan Somers /*
503b9e20197SAlan Somers  * A short read indicates EOF.  Test that nothing bad happens if we get EOF
504b9e20197SAlan Somers  * during the R of a RMW operation.
505b9e20197SAlan Somers  */
506f8ebf1cdSAlan Somers TEST_F(Write, eof_during_rmw)
507b9e20197SAlan Somers {
508b9e20197SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
509b9e20197SAlan Somers 	const char RELPATH[] = "some_file.txt";
510b9e20197SAlan Somers 	const char *CONTENTS = "abcdefgh";
511b9e20197SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
512b9e20197SAlan Somers 	uint64_t ino = 42;
513b9e20197SAlan Somers 	uint64_t offset = 1;
514ae39db74SAlan Somers 	ssize_t bufsize = strlen(CONTENTS) + 1;
515b9e20197SAlan Somers 	off_t orig_fsize = 10;
516b9e20197SAlan Somers 	off_t truncated_fsize = 5;
517b9e20197SAlan Somers 	int fd;
518b9e20197SAlan Somers 
519b9e20197SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
520b9e20197SAlan Somers 	expect_open(ino, 0, 1);
521b9e20197SAlan Somers 	expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
522b9e20197SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
523b9e20197SAlan Somers 
524b9e20197SAlan Somers 	fd = open(FULLPATH, O_RDWR);
525d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
526b9e20197SAlan Somers 
527b9e20197SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
528b9e20197SAlan Somers 		<< strerror(errno);
5297fc0921dSAlan Somers 	leak(fd);
530b9e20197SAlan Somers }
531b9e20197SAlan Somers 
532b9e20197SAlan Somers /*
533*032a5bd5SAlan Somers  * VOP_STRATEGY should not query the server for the file's size, even if its
534*032a5bd5SAlan Somers  * cached attributes have expired.
535*032a5bd5SAlan Somers  * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
536*032a5bd5SAlan Somers  */
537*032a5bd5SAlan Somers TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy)
538*032a5bd5SAlan Somers {
539*032a5bd5SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
540*032a5bd5SAlan Somers 	const char RELPATH[] = "some_file.txt";
541*032a5bd5SAlan Somers 	Sequence seq;
542*032a5bd5SAlan Somers 	const off_t filesize = 2 * m_maxbcachebuf;
543*032a5bd5SAlan Somers 	void *contents;
544*032a5bd5SAlan Somers 	uint64_t ino = 42;
545*032a5bd5SAlan Somers 	uint64_t attr_valid = 0;
546*032a5bd5SAlan Somers 	uint64_t attr_valid_nsec = 0;
547*032a5bd5SAlan Somers 	mode_t mode = S_IFREG | 0644;
548*032a5bd5SAlan Somers 	int fd;
549*032a5bd5SAlan Somers 	int ngetattrs;
550*032a5bd5SAlan Somers 
551*032a5bd5SAlan Somers 	ngetattrs = GetParam();
552*032a5bd5SAlan Somers 	contents = calloc(1, filesize);
553*032a5bd5SAlan Somers 
554*032a5bd5SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
555*032a5bd5SAlan Somers 	.WillRepeatedly(Invoke(
556*032a5bd5SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
557*032a5bd5SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
558*032a5bd5SAlan Somers 		out.body.entry.attr.mode = mode;
559*032a5bd5SAlan Somers 		out.body.entry.nodeid = ino;
560*032a5bd5SAlan Somers 		out.body.entry.attr.nlink = 1;
561*032a5bd5SAlan Somers 		out.body.entry.attr.size = filesize;
562*032a5bd5SAlan Somers 		out.body.entry.attr_valid = attr_valid;
563*032a5bd5SAlan Somers 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
564*032a5bd5SAlan Somers 	})));
565*032a5bd5SAlan Somers 	expect_open(ino, 0, 1);
566*032a5bd5SAlan Somers 	EXPECT_CALL(*m_mock, process(
567*032a5bd5SAlan Somers 		ResultOf([=](auto in) {
568*032a5bd5SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
569*032a5bd5SAlan Somers 				in.header.nodeid == ino);
570*032a5bd5SAlan Somers 		}, Eq(true)),
571*032a5bd5SAlan Somers 		_)
572*032a5bd5SAlan Somers 	).Times(Between(ngetattrs - 1, ngetattrs))
573*032a5bd5SAlan Somers 	.InSequence(seq)
574*032a5bd5SAlan Somers 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
575*032a5bd5SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
576*032a5bd5SAlan Somers 		out.body.attr.attr.ino = ino;
577*032a5bd5SAlan Somers 		out.body.attr.attr.mode = mode;
578*032a5bd5SAlan Somers 		out.body.attr.attr_valid = attr_valid;
579*032a5bd5SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
580*032a5bd5SAlan Somers 		out.body.attr.attr.size = filesize;
581*032a5bd5SAlan Somers 	})));
582*032a5bd5SAlan Somers 	EXPECT_CALL(*m_mock, process(
583*032a5bd5SAlan Somers 		ResultOf([=](auto in) {
584*032a5bd5SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
585*032a5bd5SAlan Somers 				in.header.nodeid == ino);
586*032a5bd5SAlan Somers 		}, Eq(true)),
587*032a5bd5SAlan Somers 		_)
588*032a5bd5SAlan Somers 	).InSequence(seq)
589*032a5bd5SAlan Somers 	.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
590*032a5bd5SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
591*032a5bd5SAlan Somers 		out.body.attr.attr.ino = ino;
592*032a5bd5SAlan Somers 		out.body.attr.attr.mode = mode;
593*032a5bd5SAlan Somers 		out.body.attr.attr_valid = attr_valid;
594*032a5bd5SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
595*032a5bd5SAlan Somers 		out.body.attr.attr.size = filesize / 2;
596*032a5bd5SAlan Somers 	})));
597*032a5bd5SAlan Somers 	expect_write(ino, 0, filesize / 2, filesize / 2, contents);
598*032a5bd5SAlan Somers 
599*032a5bd5SAlan Somers 	fd = open(FULLPATH, O_RDWR);
600*032a5bd5SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
601*032a5bd5SAlan Somers 	ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2))
602*032a5bd5SAlan Somers 		<< strerror(errno);
603*032a5bd5SAlan Somers 
604*032a5bd5SAlan Somers }
605*032a5bd5SAlan Somers 
606*032a5bd5SAlan Somers INSTANTIATE_TEST_CASE_P(W, WriteEofDuringVnopStrategy,
607*032a5bd5SAlan Somers 	Values(1, 2, 3)
608*032a5bd5SAlan Somers );
609*032a5bd5SAlan Somers 
610*032a5bd5SAlan Somers /*
6119821f1d3SAlan Somers  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
6129821f1d3SAlan Somers  * write, then it must set the FUSE_WRITE_CACHE bit
6139821f1d3SAlan Somers  */
6149821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
615f8ebf1cdSAlan Somers TEST_F(Write, mmap)
6169821f1d3SAlan Somers {
6179821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6189821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6199821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6209821f1d3SAlan Somers 	uint64_t ino = 42;
6219821f1d3SAlan Somers 	int fd;
6229821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6239821f1d3SAlan Somers 	void *p;
6249821f1d3SAlan Somers 	uint64_t offset = 10;
6259821f1d3SAlan Somers 	size_t len;
6269821f1d3SAlan Somers 	void *zeros, *expected;
6279821f1d3SAlan Somers 
6289821f1d3SAlan Somers 	len = getpagesize();
6299821f1d3SAlan Somers 
6309821f1d3SAlan Somers 	zeros = calloc(1, len);
6315a0b9a27SAlan Somers 	ASSERT_NE(nullptr, zeros);
6329821f1d3SAlan Somers 	expected = calloc(1, len);
6335a0b9a27SAlan Somers 	ASSERT_NE(nullptr, expected);
6349821f1d3SAlan Somers 	memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
6359821f1d3SAlan Somers 
6369821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, len);
6379821f1d3SAlan Somers 	expect_open(ino, 0, 1);
6389821f1d3SAlan Somers 	expect_read(ino, 0, len, len, zeros);
6399821f1d3SAlan Somers 	/*
6409821f1d3SAlan Somers 	 * Writes from the pager may or may not be associated with the correct
641cf437e2aSAlan Somers 	 * pid, so they must set FUSE_WRITE_CACHE.
6429821f1d3SAlan Somers 	 */
643bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
6449f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
6459821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
6469821f1d3SAlan Somers 
6479821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
648d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
6499821f1d3SAlan Somers 
6509821f1d3SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
6519821f1d3SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
6529821f1d3SAlan Somers 
6539821f1d3SAlan Somers 	memmove((uint8_t*)p + offset, CONTENTS, bufsize);
6549821f1d3SAlan Somers 
6559821f1d3SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
6569821f1d3SAlan Somers 	close(fd);	// Write mmap'd data on close
6579821f1d3SAlan Somers 
6589821f1d3SAlan Somers 	free(expected);
6599821f1d3SAlan Somers 	free(zeros);
6608e765737SAlan Somers 
6618e765737SAlan Somers 	leak(fd);
6629821f1d3SAlan Somers }
6639821f1d3SAlan Somers 
664f8ebf1cdSAlan Somers TEST_F(Write, pwrite)
6659821f1d3SAlan Somers {
6669821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6679821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6689821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6699821f1d3SAlan Somers 	uint64_t ino = 42;
6706ca3b02bSAlan Somers 	uint64_t offset = m_maxbcachebuf;
6719821f1d3SAlan Somers 	int fd;
6729821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6739821f1d3SAlan Somers 
6749821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6759821f1d3SAlan Somers 	expect_open(ino, 0, 1);
676bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
6779821f1d3SAlan Somers 
6789821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
679d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
6809821f1d3SAlan Somers 
6819821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
6829821f1d3SAlan Somers 		<< strerror(errno);
6837fc0921dSAlan Somers 	leak(fd);
6849821f1d3SAlan Somers }
6859821f1d3SAlan Somers 
686788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */
687788af953SAlan Somers TEST_F(Write, timestamps)
688788af953SAlan Somers {
689788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
690788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
691788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
692788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
693788af953SAlan Somers 	uint64_t ino = 42;
694788af953SAlan Somers 	struct stat sb0, sb1;
695788af953SAlan Somers 	int fd;
696788af953SAlan Somers 
697788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
698788af953SAlan Somers 	expect_open(ino, 0, 1);
699788af953SAlan Somers 	maybe_expect_write(ino, 0, bufsize, CONTENTS);
700788af953SAlan Somers 
701788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
702d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
703788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
704788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
705788af953SAlan Somers 
706788af953SAlan Somers 	nap();
707788af953SAlan Somers 
708788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno);
709788af953SAlan Somers 
710788af953SAlan Somers 	EXPECT_EQ(sb0.st_atime, sb1.st_atime);
711788af953SAlan Somers 	EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
712788af953SAlan Somers 	EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
7138e765737SAlan Somers 
7148e765737SAlan Somers 	leak(fd);
715788af953SAlan Somers }
716788af953SAlan Somers 
7179821f1d3SAlan Somers TEST_F(Write, write)
7189821f1d3SAlan Somers {
7199821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7209821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7219821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7229821f1d3SAlan Somers 	uint64_t ino = 42;
7239821f1d3SAlan Somers 	int fd;
7249821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7259821f1d3SAlan Somers 
7269821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7279821f1d3SAlan Somers 	expect_open(ino, 0, 1);
728bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
7299821f1d3SAlan Somers 
7309821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
731d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7329821f1d3SAlan Somers 
7339821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7347fc0921dSAlan Somers 	leak(fd);
7359821f1d3SAlan Somers }
7369821f1d3SAlan Somers 
7379821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */
738f928dbcbSAlan Somers TEST_F(WriteMaxWrite, write)
7399821f1d3SAlan Somers {
7409821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7419821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7429821f1d3SAlan Somers 	int *contents;
7439821f1d3SAlan Somers 	uint64_t ino = 42;
7449821f1d3SAlan Somers 	int fd;
7459821f1d3SAlan Somers 	ssize_t halfbufsize, bufsize;
7469821f1d3SAlan Somers 
7478eecd9ceSAlan Somers 	halfbufsize = m_mock->m_maxwrite;
748f928dbcbSAlan Somers 	if (halfbufsize >= m_maxbcachebuf || halfbufsize >= m_maxphys)
749f928dbcbSAlan Somers 		GTEST_SKIP() << "Must lower m_maxwrite for this test";
7509821f1d3SAlan Somers 	bufsize = halfbufsize * 2;
7519821f1d3SAlan Somers 	contents = (int*)malloc(bufsize);
7525a0b9a27SAlan Somers 	ASSERT_NE(nullptr, contents);
7539821f1d3SAlan Somers 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
7549821f1d3SAlan Somers 		contents[i] = i;
7559821f1d3SAlan Somers 	}
7569821f1d3SAlan Somers 
7579821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7589821f1d3SAlan Somers 	expect_open(ino, 0, 1);
759f2704f05SAlan Somers 	maybe_expect_write(ino, 0, halfbufsize, contents);
76084879e46SAlan Somers 	maybe_expect_write(ino, halfbufsize, halfbufsize,
7619821f1d3SAlan Somers 		&contents[halfbufsize / sizeof(int)]);
7629821f1d3SAlan Somers 
7639821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
764d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7659821f1d3SAlan Somers 
7669821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
7677fc0921dSAlan Somers 	leak(fd);
7689821f1d3SAlan Somers 
7699821f1d3SAlan Somers 	free(contents);
7709821f1d3SAlan Somers }
7719821f1d3SAlan Somers 
7729821f1d3SAlan Somers TEST_F(Write, write_nothing)
7739821f1d3SAlan Somers {
7749821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7759821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7769821f1d3SAlan Somers 	const char *CONTENTS = "";
7779821f1d3SAlan Somers 	uint64_t ino = 42;
7789821f1d3SAlan Somers 	int fd;
7799821f1d3SAlan Somers 	ssize_t bufsize = 0;
7809821f1d3SAlan Somers 
7819821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7829821f1d3SAlan Somers 	expect_open(ino, 0, 1);
7839821f1d3SAlan Somers 
7849821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
785d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7869821f1d3SAlan Somers 
7879821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7887fc0921dSAlan Somers 	leak(fd);
7899821f1d3SAlan Somers }
7909821f1d3SAlan Somers 
79116bd2d47SAlan Somers TEST_F(Write_7_8, write)
79216bd2d47SAlan Somers {
79316bd2d47SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
79416bd2d47SAlan Somers 	const char RELPATH[] = "some_file.txt";
79516bd2d47SAlan Somers 	const char *CONTENTS = "abcdefgh";
79616bd2d47SAlan Somers 	uint64_t ino = 42;
79716bd2d47SAlan Somers 	int fd;
79816bd2d47SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
79916bd2d47SAlan Somers 
80016bd2d47SAlan Somers 	expect_lookup(RELPATH, ino, 0);
80116bd2d47SAlan Somers 	expect_open(ino, 0, 1);
802bda39894SAlan Somers 	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
80316bd2d47SAlan Somers 
80416bd2d47SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
805d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
80616bd2d47SAlan Somers 
80716bd2d47SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
8087fc0921dSAlan Somers 	leak(fd);
80916bd2d47SAlan Somers }
81016bd2d47SAlan Somers 
8119821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */
81284879e46SAlan Somers TEST_F(WriteBackAsync, close)
8139821f1d3SAlan Somers {
8149821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8159821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8169821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
8179821f1d3SAlan Somers 	uint64_t ino = 42;
8189821f1d3SAlan Somers 	int fd;
8199821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8209821f1d3SAlan Somers 
8219821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
8229821f1d3SAlan Somers 	expect_open(ino, 0, 1);
823bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
8249821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
8259821f1d3SAlan Somers 		ResultOf([=](auto in) {
82629edc611SAlan Somers 			return (in.header.opcode == FUSE_SETATTR);
8279821f1d3SAlan Somers 		}, Eq(true)),
8289821f1d3SAlan Somers 		_)
82929edc611SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
8309821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
83129edc611SAlan Somers 		out.body.attr.attr.ino = ino;	// Must match nodeid
8329821f1d3SAlan Somers 	})));
8339f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
8349821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
8359821f1d3SAlan Somers 
8369821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
8379821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8389821f1d3SAlan Somers 
8399821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
8409821f1d3SAlan Somers 	close(fd);
8419821f1d3SAlan Somers }
8429821f1d3SAlan Somers 
8438eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */
8448eecd9ceSAlan Somers TEST_F(WriteCluster, clustering)
8458eecd9ceSAlan Somers {
8468eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8478eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
8488eecd9ceSAlan Somers 	uint64_t ino = 42;
8498eecd9ceSAlan Somers 	int i, fd;
8508eecd9ceSAlan Somers 	void *wbuf, *wbuf2x;
8516ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
8526ca3b02bSAlan Somers 	off_t filesize = 5 * bufsize;
8538eecd9ceSAlan Somers 
8548eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
8555a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf) << strerror(errno);
8568eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
8578eecd9ceSAlan Somers 	wbuf2x = malloc(2 * bufsize);
8585a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf2x) << strerror(errno);
8598eecd9ceSAlan Somers 	memset(wbuf2x, 'X', 2 * bufsize);
8608eecd9ceSAlan Somers 
8618eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
8628eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
8638eecd9ceSAlan Somers 	/*
8648eecd9ceSAlan Somers 	 * Writes of bufsize-bytes each should be clustered into greater sizes.
8658eecd9ceSAlan Somers 	 * The amount of clustering is adaptive, so the first write actually
8668eecd9ceSAlan Somers 	 * issued will be 2x bufsize and subsequent writes may be larger
8678eecd9ceSAlan Somers 	 */
8688eecd9ceSAlan Somers 	expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
8698eecd9ceSAlan Somers 	expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
8708eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
8718eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
8728eecd9ceSAlan Somers 
8738eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
8748eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8758eecd9ceSAlan Somers 
8768eecd9ceSAlan Somers 	for (i = 0; i < 4; i++) {
8778eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
8788eecd9ceSAlan Somers 			<< strerror(errno);
8798eecd9ceSAlan Somers 	}
8808eecd9ceSAlan Somers 	close(fd);
8817ee7e409SAlan Somers 	free(wbuf2x);
8827ee7e409SAlan Somers 	free(wbuf);
8838eecd9ceSAlan Somers }
8848eecd9ceSAlan Somers 
8858eecd9ceSAlan Somers /*
8868eecd9ceSAlan Somers  * When clustering writes, an I/O error to any of the cluster's children should
8878eecd9ceSAlan Somers  * not panic the system on unmount
8888eecd9ceSAlan Somers  */
8898eecd9ceSAlan Somers /*
890425bbe9eSAlan Somers  * Regression test for bug 238585
8918eecd9ceSAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
8928eecd9ceSAlan Somers  */
893425bbe9eSAlan Somers TEST_F(WriteCluster, cluster_write_err)
8948eecd9ceSAlan Somers {
8958eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8968eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
8978eecd9ceSAlan Somers 	uint64_t ino = 42;
8988eecd9ceSAlan Somers 	int i, fd;
8998eecd9ceSAlan Somers 	void *wbuf;
9006ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
9016ca3b02bSAlan Somers 	off_t filesize = 4 * bufsize;
9028eecd9ceSAlan Somers 
9038eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
9045a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf) << strerror(errno);
9058eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
9068eecd9ceSAlan Somers 
9078eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
9088eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
9098eecd9ceSAlan Somers 	EXPECT_CALL(*m_mock, process(
9108eecd9ceSAlan Somers 		ResultOf([=](auto in) {
9118eecd9ceSAlan Somers 			return (in.header.opcode == FUSE_WRITE);
9128eecd9ceSAlan Somers 		}, Eq(true)),
9138eecd9ceSAlan Somers 		_)
9148eecd9ceSAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
9158eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
9168eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
9178eecd9ceSAlan Somers 
9188eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
9198eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
9208eecd9ceSAlan Somers 
9218eecd9ceSAlan Somers 	for (i = 0; i < 3; i++) {
9228eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
9238eecd9ceSAlan Somers 			<< strerror(errno);
9248eecd9ceSAlan Somers 	}
9258eecd9ceSAlan Somers 	close(fd);
9267ee7e409SAlan Somers 	free(wbuf);
9278eecd9ceSAlan Somers }
9288eecd9ceSAlan Somers 
9299821f1d3SAlan Somers /*
9305fccbf31SAlan Somers  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
9315fccbf31SAlan Somers  * server.  The FUSE protocol explicitly allows that.
9325fccbf31SAlan Somers  */
9335fccbf31SAlan Somers TEST_F(WriteBack, rmw)
9345fccbf31SAlan Somers {
9355fccbf31SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9365fccbf31SAlan Somers 	const char RELPATH[] = "some_file.txt";
9375fccbf31SAlan Somers 	const char *CONTENTS = "abcdefgh";
9385fccbf31SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
9395fccbf31SAlan Somers 	uint64_t ino = 42;
9405fccbf31SAlan Somers 	uint64_t offset = 1;
9415fccbf31SAlan Somers 	off_t fsize = 10;
9425fccbf31SAlan Somers 	int fd;
9435fccbf31SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
9445fccbf31SAlan Somers 
945cad67791SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
9465fccbf31SAlan Somers 	expect_open(ino, 0, 1);
947d4fd0c81SAlan Somers 	expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
94884879e46SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
9495fccbf31SAlan Somers 
9505fccbf31SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
951d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
9525fccbf31SAlan Somers 
9535fccbf31SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
9545fccbf31SAlan Somers 		<< strerror(errno);
9557fc0921dSAlan Somers 	leak(fd);
9565fccbf31SAlan Somers }
9575fccbf31SAlan Somers 
9585fccbf31SAlan Somers /*
9599821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
9609821f1d3SAlan Somers  */
96184879e46SAlan Somers TEST_F(WriteBack, cache)
9629821f1d3SAlan Somers {
9639821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9649821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
9659821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
9669821f1d3SAlan Somers 	uint64_t ino = 42;
9679821f1d3SAlan Somers 	int fd;
9689821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
9698e765737SAlan Somers 	uint8_t readbuf[bufsize];
9709821f1d3SAlan Somers 
9719821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
9729821f1d3SAlan Somers 	expect_open(ino, 0, 1);
973bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
9749821f1d3SAlan Somers 
9759821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
976d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
9779821f1d3SAlan Somers 
9789821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9799821f1d3SAlan Somers 	/*
9809821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
9819821f1d3SAlan Somers 	 * filesystem daemon
9829821f1d3SAlan Somers 	 */
9839821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
9849821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
9857fc0921dSAlan Somers 	leak(fd);
9869821f1d3SAlan Somers }
9879821f1d3SAlan Somers 
9889821f1d3SAlan Somers /*
9899821f1d3SAlan Somers  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
9909821f1d3SAlan Somers  * an odd test, because it would be unusual to use O_DIRECT for writes but not
9919821f1d3SAlan Somers  * reads.
9929821f1d3SAlan Somers  */
9939821f1d3SAlan Somers TEST_F(WriteBack, o_direct)
9949821f1d3SAlan Somers {
9959821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9969821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
9979821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
9989821f1d3SAlan Somers 	uint64_t ino = 42;
9999821f1d3SAlan Somers 	int fd;
10009821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
10018e765737SAlan Somers 	uint8_t readbuf[bufsize];
10029821f1d3SAlan Somers 
10039821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
10049821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1005bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1006bda39894SAlan Somers 		CONTENTS);
10079821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
10089821f1d3SAlan Somers 
10099821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
1010d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
10119821f1d3SAlan Somers 
10129821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
10139821f1d3SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
10149821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
10159821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
10169821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
10177fc0921dSAlan Somers 	leak(fd);
10189821f1d3SAlan Somers }
10199821f1d3SAlan Somers 
1020a62772a7SAlan Somers TEST_F(WriteBack, direct_io)
1021a62772a7SAlan Somers {
1022a62772a7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1023a62772a7SAlan Somers 	const char RELPATH[] = "some_file.txt";
1024a62772a7SAlan Somers 	const char *CONTENTS = "abcdefgh";
1025a62772a7SAlan Somers 	uint64_t ino = 42;
1026a62772a7SAlan Somers 	int fd;
1027a62772a7SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1028a62772a7SAlan Somers 	uint8_t readbuf[bufsize];
1029a62772a7SAlan Somers 
1030a62772a7SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1031a62772a7SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
1032a62772a7SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1033a62772a7SAlan Somers 		CONTENTS);
1034a62772a7SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1035a62772a7SAlan Somers 
1036a62772a7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1037d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1038a62772a7SAlan Somers 
1039a62772a7SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1040a62772a7SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
1041a62772a7SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1042a62772a7SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1043a62772a7SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1044a62772a7SAlan Somers 	leak(fd);
1045a62772a7SAlan Somers }
1046a62772a7SAlan Somers 
1047a62772a7SAlan Somers /*
1048a62772a7SAlan Somers  * mmap should still be possible even if the server used direct_io.  Mmap will
1049a62772a7SAlan Somers  * still use the cache, though.
1050a62772a7SAlan Somers  *
1051a62772a7SAlan Somers  * Regression test for bug 247276
1052a62772a7SAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276
1053a62772a7SAlan Somers  */
1054a62772a7SAlan Somers TEST_F(WriteBack, mmap_direct_io)
1055a62772a7SAlan Somers {
1056a62772a7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1057a62772a7SAlan Somers 	const char RELPATH[] = "some_file.txt";
1058a62772a7SAlan Somers 	const char *CONTENTS = "abcdefgh";
1059a62772a7SAlan Somers 	uint64_t ino = 42;
1060a62772a7SAlan Somers 	int fd;
1061a62772a7SAlan Somers 	size_t len;
1062a62772a7SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1063a62772a7SAlan Somers 	void *p, *zeros;
1064a62772a7SAlan Somers 
1065a62772a7SAlan Somers 	len = getpagesize();
1066a62772a7SAlan Somers 	zeros = calloc(1, len);
1067a62772a7SAlan Somers 	ASSERT_NE(nullptr, zeros);
1068a62772a7SAlan Somers 
1069a62772a7SAlan Somers 	expect_lookup(RELPATH, ino, len);
1070a62772a7SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
1071a62772a7SAlan Somers 	expect_read(ino, 0, len, len, zeros);
1072a62772a7SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
1073a62772a7SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros);
1074a62772a7SAlan Somers 	expect_release(ino, ReturnErrno(0));
1075a62772a7SAlan Somers 
1076a62772a7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1077d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1078a62772a7SAlan Somers 
1079a62772a7SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1080a62772a7SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1081a62772a7SAlan Somers 
1082a62772a7SAlan Somers 	memmove((uint8_t*)p, CONTENTS, bufsize);
1083a62772a7SAlan Somers 
1084a62772a7SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
1085a62772a7SAlan Somers 	close(fd);	// Write mmap'd data on close
1086a62772a7SAlan Somers 
1087a62772a7SAlan Somers 	free(zeros);
1088a62772a7SAlan Somers }
1089a62772a7SAlan Somers 
10909821f1d3SAlan Somers /*
109184879e46SAlan Somers  * When mounted with -o async, the writeback cache mode should delay writes
109284879e46SAlan Somers  */
109384879e46SAlan Somers TEST_F(WriteBackAsync, delay)
109484879e46SAlan Somers {
109584879e46SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
109684879e46SAlan Somers 	const char RELPATH[] = "some_file.txt";
109784879e46SAlan Somers 	const char *CONTENTS = "abcdefgh";
109884879e46SAlan Somers 	uint64_t ino = 42;
109984879e46SAlan Somers 	int fd;
110084879e46SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
110184879e46SAlan Somers 
110284879e46SAlan Somers 	expect_lookup(RELPATH, ino, 0);
110384879e46SAlan Somers 	expect_open(ino, 0, 1);
110484879e46SAlan Somers 	/* Write should be cached, but FUSE_WRITE shouldn't be sent */
110584879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
110684879e46SAlan Somers 		ResultOf([=](auto in) {
110784879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE);
110884879e46SAlan Somers 		}, Eq(true)),
110984879e46SAlan Somers 		_)
111084879e46SAlan Somers 	).Times(0);
111184879e46SAlan Somers 
111284879e46SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1113d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
111484879e46SAlan Somers 
111584879e46SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
111684879e46SAlan Somers 
111784879e46SAlan Somers 	/* Don't close the file because that would flush the cache */
11188e765737SAlan Somers 	leak(fd);
111984879e46SAlan Somers }
112084879e46SAlan Somers 
112184879e46SAlan Somers /*
1122669a092aSAlan Somers  * A direct write should not evict dirty cached data from outside of its own
1123669a092aSAlan Somers  * byte range.
1124669a092aSAlan Somers  */
1125669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached)
1126669a092aSAlan Somers {
1127669a092aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1128669a092aSAlan Somers 	const char RELPATH[] = "some_file.txt";
1129669a092aSAlan Somers 	const char CONTENTS0[] = "abcdefgh";
1130669a092aSAlan Somers 	const char CONTENTS1[] = "ijklmnop";
1131669a092aSAlan Somers 	uint64_t ino = 42;
1132669a092aSAlan Somers 	int fd;
1133669a092aSAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
1134669a092aSAlan Somers 	ssize_t fsize = 2 * m_maxbcachebuf;
1135669a092aSAlan Somers 	char readbuf[bufsize];
1136669a092aSAlan Somers 	void *zeros;
1137669a092aSAlan Somers 
1138669a092aSAlan Somers 	zeros = calloc(1, m_maxbcachebuf);
1139669a092aSAlan Somers 	ASSERT_NE(nullptr, zeros);
1140669a092aSAlan Somers 
1141669a092aSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
1142669a092aSAlan Somers 	expect_open(ino, 0, 1);
1143669a092aSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros);
1144669a092aSAlan Somers 	FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0,
1145669a092aSAlan Somers 		CONTENTS1);
1146669a092aSAlan Somers 
1147669a092aSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1148d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1149669a092aSAlan Somers 
1150669a092aSAlan Somers 	// Cache first block with dirty data.  This will entail first reading
1151669a092aSAlan Somers 	// the existing data.
1152669a092aSAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0))
1153669a092aSAlan Somers 		<< strerror(errno);
1154669a092aSAlan Somers 
1155669a092aSAlan Somers 	// Write directly to second block
1156669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1157669a092aSAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf))
1158669a092aSAlan Somers 		<< strerror(errno);
1159669a092aSAlan Somers 
1160669a092aSAlan Somers 	// Read from the first block again.  Should be serviced by cache.
1161669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1162669a092aSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno);
1163669a092aSAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS0);
1164669a092aSAlan Somers 
1165669a092aSAlan Somers 	leak(fd);
1166669a092aSAlan Somers 	free(zeros);
1167669a092aSAlan Somers }
1168669a092aSAlan Somers 
1169669a092aSAlan Somers /*
1170669a092aSAlan Somers  * If a direct io write partially overlaps one or two blocks of dirty cached
1171669a092aSAlan Somers  * data, No dirty data should be lost.  Admittedly this is a weird test,
1172669a092aSAlan Somers  * because it would be unusual to use O_DIRECT and the writeback cache.
1173669a092aSAlan Somers  */
1174669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block)
1175669a092aSAlan Somers {
1176669a092aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1177669a092aSAlan Somers 	const char RELPATH[] = "some_file.txt";
1178669a092aSAlan Somers 	uint64_t ino = 42;
1179669a092aSAlan Somers 	int fd;
1180669a092aSAlan Somers 	off_t bs = m_maxbcachebuf;
1181669a092aSAlan Somers 	ssize_t fsize = 3 * bs;
1182669a092aSAlan Somers 	void *readbuf, *zeros, *ones, *zeroones, *onezeros;
1183669a092aSAlan Somers 
1184669a092aSAlan Somers 	readbuf = malloc(bs);
1185669a092aSAlan Somers 	ASSERT_NE(nullptr, readbuf) << strerror(errno);
1186669a092aSAlan Somers 	zeros = calloc(1, 3 * bs);
1187669a092aSAlan Somers 	ASSERT_NE(nullptr, zeros);
1188669a092aSAlan Somers 	ones = calloc(1, 2 * bs);
1189669a092aSAlan Somers 	ASSERT_NE(nullptr, ones);
1190669a092aSAlan Somers 	memset(ones, 1, 2 * bs);
1191669a092aSAlan Somers 	zeroones = calloc(1, bs);
1192669a092aSAlan Somers 	ASSERT_NE(nullptr, zeroones);
1193669a092aSAlan Somers 	memset((uint8_t*)zeroones + bs / 2, 1, bs / 2);
1194669a092aSAlan Somers 	onezeros = calloc(1, bs);
1195669a092aSAlan Somers 	ASSERT_NE(nullptr, onezeros);
1196669a092aSAlan Somers 	memset(onezeros, 1, bs / 2);
1197669a092aSAlan Somers 
1198669a092aSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
1199669a092aSAlan Somers 	expect_open(ino, 0, 1);
1200669a092aSAlan Somers 
1201669a092aSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1202d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1203669a092aSAlan Somers 
1204669a092aSAlan Somers 	/* Cache first and third blocks with dirty data.  */
1205669a092aSAlan Somers 	ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno);
1206669a092aSAlan Somers 
1207669a092aSAlan Somers 	/*
1208669a092aSAlan Somers 	 * Write directly to all three blocks.  The partially written blocks
1209669a092aSAlan Somers 	 * will be flushed because they're dirty.
1210669a092aSAlan Somers 	 */
1211669a092aSAlan Somers 	FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros);
1212669a092aSAlan Somers 	FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros);
1213669a092aSAlan Somers 	/* The direct write is split in two because of the m_maxwrite value */
1214669a092aSAlan Somers 	FuseTest::expect_write(ino,     bs / 2, bs, bs, 0, 0, ones);
1215669a092aSAlan Somers 	FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones);
1216669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1217669a092aSAlan Somers 	ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno);
1218669a092aSAlan Somers 
1219669a092aSAlan Somers 	/*
1220669a092aSAlan Somers 	 * Read from both the valid and invalid portions of the first and third
1221669a092aSAlan Somers 	 * blocks again.  This will entail FUSE_READ operations because these
1222669a092aSAlan Somers 	 * blocks were invalidated by the direct write.
1223669a092aSAlan Somers 	 */
1224669a092aSAlan Somers 	expect_read(ino, 0, bs, bs, zeroones);
1225669a092aSAlan Somers 	expect_read(ino, 2 * bs, bs, bs, onezeros);
1226669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1227669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno);
1228669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1229669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2))
1230669a092aSAlan Somers 		<< strerror(errno);
1231669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1232669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2))
1233669a092aSAlan Somers 		<< strerror(errno);
1234669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1235669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs))
1236669a092aSAlan Somers 		<< strerror(errno);
1237669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1238669a092aSAlan Somers 
1239669a092aSAlan Somers 	leak(fd);
1240669a092aSAlan Somers 	free(zeroones);
1241669a092aSAlan Somers 	free(onezeros);
1242669a092aSAlan Somers 	free(ones);
1243669a092aSAlan Somers 	free(zeros);
1244669a092aSAlan Somers 	free(readbuf);
1245669a092aSAlan Somers }
1246669a092aSAlan Somers 
1247669a092aSAlan Somers /*
1248aef22f2dSAlan Somers  * In WriteBack mode, writes may be cached beyond what the server thinks is the
1249aef22f2dSAlan Somers  * EOF.  In this case, a short read at EOF should _not_ cause fusefs to update
1250aef22f2dSAlan Somers  * the file's size.
1251aef22f2dSAlan Somers  */
1252aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof)
1253aef22f2dSAlan Somers {
1254aef22f2dSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1255aef22f2dSAlan Somers 	const char RELPATH[] = "some_file.txt";
1256aef22f2dSAlan Somers 	const char *CONTENTS0 = "abcdefgh";
1257aef22f2dSAlan Somers 	const char *CONTENTS1 = "ijklmnop";
1258aef22f2dSAlan Somers 	uint64_t ino = 42;
1259aef22f2dSAlan Somers 	int fd;
1260aef22f2dSAlan Somers 	off_t offset = m_maxbcachebuf;
1261aef22f2dSAlan Somers 	ssize_t wbufsize = strlen(CONTENTS1);
1262aef22f2dSAlan Somers 	off_t old_filesize = (off_t)strlen(CONTENTS0);
1263aef22f2dSAlan Somers 	ssize_t rbufsize = 2 * old_filesize;
1264aef22f2dSAlan Somers 	char readbuf[rbufsize];
1265aef22f2dSAlan Somers 	size_t holesize = rbufsize - old_filesize;
1266aef22f2dSAlan Somers 	char hole[holesize];
1267aef22f2dSAlan Somers 	struct stat sb;
1268aef22f2dSAlan Somers 	ssize_t r;
1269aef22f2dSAlan Somers 
1270aef22f2dSAlan Somers 	expect_lookup(RELPATH, ino, 0);
1271aef22f2dSAlan Somers 	expect_open(ino, 0, 1);
1272aef22f2dSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
1273aef22f2dSAlan Somers 
1274aef22f2dSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1275d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1276aef22f2dSAlan Somers 
1277aef22f2dSAlan Somers 	/* Write and cache data beyond EOF */
1278aef22f2dSAlan Somers 	ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
1279aef22f2dSAlan Somers 		<< strerror(errno);
1280aef22f2dSAlan Somers 
1281aef22f2dSAlan Somers 	/* Read from the old EOF */
1282aef22f2dSAlan Somers 	r = pread(fd, readbuf, rbufsize, 0);
1283aef22f2dSAlan Somers 	ASSERT_LE(0, r) << strerror(errno);
1284aef22f2dSAlan Somers 	EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
1285aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
1286aef22f2dSAlan Somers 	bzero(hole, holesize);
1287aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
1288aef22f2dSAlan Somers 
1289aef22f2dSAlan Somers 	/* The file's size should still be what was established by pwrite */
1290aef22f2dSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1291aef22f2dSAlan Somers 	EXPECT_EQ(offset + wbufsize, sb.st_size);
12927fc0921dSAlan Somers 	leak(fd);
1293aef22f2dSAlan Somers }
1294aef22f2dSAlan Somers 
1295aef22f2dSAlan Somers /*
1296788af953SAlan Somers  * When a file has dirty writes that haven't been flushed, the server's notion
1297788af953SAlan Somers  * of its mtime and ctime will be wrong.  The kernel should ignore those if it
1298788af953SAlan Somers  * gets them from a FUSE_GETATTR before flushing.
1299788af953SAlan Somers  */
1300788af953SAlan Somers TEST_F(WriteBackAsync, timestamps)
1301788af953SAlan Somers {
1302788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1303788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1304788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1305788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1306788af953SAlan Somers 	uint64_t ino = 42;
1307788af953SAlan Somers 	uint64_t attr_valid = 0;
1308788af953SAlan Somers 	uint64_t attr_valid_nsec = 0;
1309788af953SAlan Somers 	uint64_t server_time = 12345;
1310788af953SAlan Somers 	mode_t mode = S_IFREG | 0644;
1311788af953SAlan Somers 	int fd;
1312788af953SAlan Somers 
1313788af953SAlan Somers 	struct stat sb;
1314788af953SAlan Somers 
1315788af953SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
1316788af953SAlan Somers 	.WillRepeatedly(Invoke(
1317788af953SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
1318788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
1319788af953SAlan Somers 		out.body.entry.attr.mode = mode;
1320788af953SAlan Somers 		out.body.entry.nodeid = ino;
1321788af953SAlan Somers 		out.body.entry.attr.nlink = 1;
1322788af953SAlan Somers 		out.body.entry.attr_valid = attr_valid;
1323788af953SAlan Somers 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
1324788af953SAlan Somers 	})));
1325788af953SAlan Somers 	expect_open(ino, 0, 1);
1326788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1327788af953SAlan Somers 		ResultOf([=](auto in) {
1328788af953SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
1329788af953SAlan Somers 				in.header.nodeid == ino);
1330788af953SAlan Somers 		}, Eq(true)),
1331788af953SAlan Somers 		_)
1332788af953SAlan Somers 	).WillRepeatedly(Invoke(
1333788af953SAlan Somers 	ReturnImmediate([=](auto i __unused, auto& out) {
1334788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1335788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1336788af953SAlan Somers 		out.body.attr.attr.mode = mode;
1337788af953SAlan Somers 		out.body.attr.attr_valid = attr_valid;
1338788af953SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
1339788af953SAlan Somers 		out.body.attr.attr.atime = server_time;
1340788af953SAlan Somers 		out.body.attr.attr.mtime = server_time;
1341788af953SAlan Somers 		out.body.attr.attr.ctime = server_time;
1342788af953SAlan Somers 	})));
1343788af953SAlan Somers 
1344788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1345d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1346788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1347788af953SAlan Somers 
1348788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1349788af953SAlan Somers 	EXPECT_EQ((time_t)server_time, sb.st_atime);
1350788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_mtime);
1351788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_ctime);
13528e765737SAlan Somers 
13538e765737SAlan Somers 	leak(fd);
1354788af953SAlan Somers }
1355788af953SAlan Somers 
1356788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */
1357788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr)
1358788af953SAlan Somers {
1359788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1360788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1361788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1362788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1363788af953SAlan Somers 	uint64_t ino = 42;
1364788af953SAlan Somers 	const mode_t newmode = 0755;
1365788af953SAlan Somers 	int fd;
1366788af953SAlan Somers 
1367788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1368788af953SAlan Somers 	expect_open(ino, 0, 1);
1369788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1370788af953SAlan Somers 		ResultOf([=](auto in) {
13710a8fe2d3SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1372788af953SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1373788af953SAlan Somers 				in.header.nodeid == ino &&
1374788af953SAlan Somers 				in.body.setattr.valid == valid);
1375788af953SAlan Somers 		}, Eq(true)),
1376788af953SAlan Somers 		_)
1377788af953SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1378788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1379788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1380788af953SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1381788af953SAlan Somers 	})));
1382788af953SAlan Somers 
1383788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1384d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1385788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1386788af953SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
13878e765737SAlan Somers 
13888e765737SAlan Somers 	leak(fd);
1389788af953SAlan Somers }
1390788af953SAlan Somers 
1391fef46454SAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */
1392fef46454SAlan Somers TEST_P(TimeGran, timestamps_during_setattr)
1393fef46454SAlan Somers {
1394fef46454SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1395fef46454SAlan Somers 	const char RELPATH[] = "some_file.txt";
1396fef46454SAlan Somers 	const char *CONTENTS = "abcdefgh";
1397fef46454SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1398fef46454SAlan Somers 	uint64_t ino = 42;
1399fef46454SAlan Somers 	const mode_t newmode = 0755;
1400fef46454SAlan Somers 	int fd;
1401fef46454SAlan Somers 
1402fef46454SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1403fef46454SAlan Somers 	expect_open(ino, 0, 1);
1404fef46454SAlan Somers 	EXPECT_CALL(*m_mock, process(
1405fef46454SAlan Somers 		ResultOf([=](auto in) {
1406fef46454SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1407fef46454SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1408fef46454SAlan Somers 				in.header.nodeid == ino &&
1409fef46454SAlan Somers 				in.body.setattr.valid == valid &&
1410fef46454SAlan Somers 				in.body.setattr.mtimensec % m_time_gran == 0 &&
1411fef46454SAlan Somers 				in.body.setattr.ctimensec % m_time_gran == 0);
1412fef46454SAlan Somers 		}, Eq(true)),
1413fef46454SAlan Somers 		_)
1414fef46454SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1415fef46454SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1416fef46454SAlan Somers 		out.body.attr.attr.ino = ino;
1417fef46454SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1418fef46454SAlan Somers 	})));
1419fef46454SAlan Somers 
1420fef46454SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1421d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1422fef46454SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1423fef46454SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
14248e765737SAlan Somers 
14258e765737SAlan Somers 	leak(fd);
1426fef46454SAlan Somers }
1427fef46454SAlan Somers 
1428fef46454SAlan Somers INSTANTIATE_TEST_CASE_P(RA, TimeGran, Range(0u, 10u));
1429fef46454SAlan Somers 
1430788af953SAlan Somers /*
14319821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
14329821f1d3SAlan Somers  */
1433f8ebf1cdSAlan Somers TEST_F(Write, writethrough)
14349821f1d3SAlan Somers {
14359821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
14369821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
14379821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
14389821f1d3SAlan Somers 	uint64_t ino = 42;
14399821f1d3SAlan Somers 	int fd;
14409821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
14418e765737SAlan Somers 	uint8_t readbuf[bufsize];
14429821f1d3SAlan Somers 
14439821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
14449821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1445bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
14469821f1d3SAlan Somers 
14479821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1448d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
14499821f1d3SAlan Somers 
14509821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
14519821f1d3SAlan Somers 	/*
14529821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
14539821f1d3SAlan Somers 	 * filesystem daemon
14549821f1d3SAlan Somers 	 */
1455b5aaf286SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
14569821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
14577fc0921dSAlan Somers 	leak(fd);
14589821f1d3SAlan Somers }
14599821f1d3SAlan Somers 
14600d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */
14610d3a88d7SAlan Somers TEST_F(Write, update_file_size)
14629821f1d3SAlan Somers {
14639821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
14649821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
14659821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
14669821f1d3SAlan Somers 	struct stat sb;
14679821f1d3SAlan Somers 	uint64_t ino = 42;
14689821f1d3SAlan Somers 	int fd;
14699821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
14709821f1d3SAlan Somers 
14719821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
14729821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1473bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
14749821f1d3SAlan Somers 
14759821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1476d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
14779821f1d3SAlan Somers 
14789821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
14799821f1d3SAlan Somers 	/* Get cached attributes */
14809821f1d3SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
14819821f1d3SAlan Somers 	ASSERT_EQ(bufsize, sb.st_size);
14827fc0921dSAlan Somers 	leak(fd);
14839821f1d3SAlan Somers }
1484