xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision ae39db74066a0ff1682c1c841be030099d9d4557)
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 
211a639731bSAlan Somers void sigxfsz_handler(int __unused sig) {
212a639731bSAlan Somers 	Write::s_sigxfsz = 1;
213a639731bSAlan Somers }
214a639731bSAlan Somers 
2159821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */
2169821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
2179821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write)
2189821f1d3SAlan Somers {
2199821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2209821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2219821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
2229821f1d3SAlan Somers 	uint64_t ino = 42;
2239821f1d3SAlan Somers 	uint64_t offset = 4096;
2249821f1d3SAlan Somers 	int fd;
2259821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
2269821f1d3SAlan Somers 	struct aiocb iocb, *piocb;
2279821f1d3SAlan Somers 
2289821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
2299821f1d3SAlan Somers 	expect_open(ino, 0, 1);
230bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
2319821f1d3SAlan Somers 
2329821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
233d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2349821f1d3SAlan Somers 
2359821f1d3SAlan Somers 	iocb.aio_nbytes = bufsize;
2369821f1d3SAlan Somers 	iocb.aio_fildes = fd;
2375a0b9a27SAlan Somers 	iocb.aio_buf = __DECONST(void *, CONTENTS);
2389821f1d3SAlan Somers 	iocb.aio_offset = offset;
2399821f1d3SAlan Somers 	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
2409821f1d3SAlan Somers 	ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
2419821f1d3SAlan Somers 	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
2427fc0921dSAlan Somers 	leak(fd);
2439821f1d3SAlan Somers }
2449821f1d3SAlan Somers 
2459821f1d3SAlan Somers /*
2469821f1d3SAlan Somers  * When a file is opened with O_APPEND, we should forward that flag to
2479821f1d3SAlan Somers  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
2489821f1d3SAlan Somers  * offset internally.  That way we'll work both with filesystems that
2499821f1d3SAlan Somers  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
2509821f1d3SAlan Somers  * simply use the offset).
2519821f1d3SAlan Somers  *
2529821f1d3SAlan Somers  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
2539821f1d3SAlan Somers  * Open.o_append test.
2549821f1d3SAlan Somers  */
2559821f1d3SAlan Somers TEST_F(Write, append)
2569821f1d3SAlan Somers {
2579821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
2589821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2599821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2609821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
2619821f1d3SAlan Somers 	uint64_t ino = 42;
2629821f1d3SAlan Somers 	/*
2639821f1d3SAlan Somers 	 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
2649821f1d3SAlan Somers 	 * using writeback caching
2659821f1d3SAlan Somers 	 */
2669821f1d3SAlan Somers 	uint64_t initial_offset = m_maxbcachebuf;
2679821f1d3SAlan Somers 	int fd;
2689821f1d3SAlan Somers 
2699821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
2709821f1d3SAlan Somers 	expect_open(ino, 0, 1);
271bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
2729821f1d3SAlan Somers 
2739821f1d3SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
2749821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
275d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
2769821f1d3SAlan Somers 
2779821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
2787fc0921dSAlan Somers 	leak(fd);
2799821f1d3SAlan Somers }
2809821f1d3SAlan Somers 
281a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */
282a87e0831SAlan Somers TEST_F(Write, append_to_cached)
283a87e0831SAlan Somers {
284a87e0831SAlan Somers 	const ssize_t BUFSIZE = 9;
285a87e0831SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
286a87e0831SAlan Somers 	const char RELPATH[] = "some_file.txt";
287a87e0831SAlan Somers 	char *oldcontents, *oldbuf;
288a87e0831SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
289a87e0831SAlan Somers 	uint64_t ino = 42;
290a87e0831SAlan Somers 	/*
291a87e0831SAlan Somers 	 * Set offset in between maxbcachebuf boundary to test buffer handling
292a87e0831SAlan Somers 	 */
293a87e0831SAlan Somers 	uint64_t oldsize = m_maxbcachebuf / 2;
294a87e0831SAlan Somers 	int fd;
295a87e0831SAlan Somers 
296a87e0831SAlan Somers 	oldcontents = (char*)calloc(1, oldsize);
2975a0b9a27SAlan Somers 	ASSERT_NE(nullptr, oldcontents) << strerror(errno);
298a87e0831SAlan Somers 	oldbuf = (char*)malloc(oldsize);
2995a0b9a27SAlan Somers 	ASSERT_NE(nullptr, oldbuf) << strerror(errno);
300a87e0831SAlan Somers 
301a87e0831SAlan Somers 	expect_lookup(RELPATH, ino, oldsize);
302a87e0831SAlan Somers 	expect_open(ino, 0, 1);
303a87e0831SAlan Somers 	expect_read(ino, 0, oldsize, oldsize, oldcontents);
30484879e46SAlan Somers 	maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
305a87e0831SAlan Somers 
306a87e0831SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
307a87e0831SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
308d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
309a87e0831SAlan Somers 
310a87e0831SAlan Somers 	/* Read the old data into the cache */
311a87e0831SAlan Somers 	ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
312a87e0831SAlan Somers 		<< strerror(errno);
313a87e0831SAlan Somers 
314a87e0831SAlan Somers 	/* Write the new data.  There should be no more read operations */
315a87e0831SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3167fc0921dSAlan Somers 	leak(fd);
3177ee7e409SAlan Somers 	free(oldbuf);
3187ee7e409SAlan Somers 	free(oldcontents);
319a87e0831SAlan Somers }
320a87e0831SAlan Somers 
3219821f1d3SAlan Somers TEST_F(Write, append_direct_io)
3229821f1d3SAlan Somers {
3239821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
3249821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3259821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3269821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
3279821f1d3SAlan Somers 	uint64_t ino = 42;
3289821f1d3SAlan Somers 	uint64_t initial_offset = 4096;
3299821f1d3SAlan Somers 	int fd;
3309821f1d3SAlan Somers 
3319821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
3329821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
333bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
3349821f1d3SAlan Somers 
3359821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY | O_APPEND);
336d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
3379821f1d3SAlan Somers 
3389821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3397fc0921dSAlan Somers 	leak(fd);
3409821f1d3SAlan Somers }
3419821f1d3SAlan Somers 
3429821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */
3436af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache)
3449821f1d3SAlan Somers {
3459821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3469821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3479821f1d3SAlan Somers 	const char CONTENTS0[] = "abcdefgh";
3489821f1d3SAlan Somers 	const char CONTENTS1[] = "ijklmnop";
3499821f1d3SAlan Somers 	uint64_t ino = 42;
3509821f1d3SAlan Somers 	int fd;
3519821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
3529821f1d3SAlan Somers 	char readbuf[bufsize];
3539821f1d3SAlan Somers 
3549821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
3559821f1d3SAlan Somers 	expect_open(ino, 0, 1);
3569821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
357bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
3589821f1d3SAlan Somers 
3599821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
360d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
3619821f1d3SAlan Somers 
3629821f1d3SAlan Somers 	// Prime cache
3639821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3649821f1d3SAlan Somers 
3659821f1d3SAlan Somers 	// Write directly, evicting cache
3669821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
3679821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3689821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
3699821f1d3SAlan Somers 
3709821f1d3SAlan Somers 	// Read again.  Cache should be bypassed
3719821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
3729821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
3739821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3749821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3759821f1d3SAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS1);
3769821f1d3SAlan Somers 
3777fc0921dSAlan Somers 	leak(fd);
3789821f1d3SAlan Somers }
3799821f1d3SAlan Somers 
3809821f1d3SAlan Somers /*
38112292a99SAlan Somers  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
38212292a99SAlan Somers  * allowed to return a short write for that file handle.  However, if it does
38312292a99SAlan Somers  * then we should still do our darndest to handle it by resending the unwritten
38412292a99SAlan Somers  * portion.
3859821f1d3SAlan Somers  */
38612292a99SAlan Somers TEST_F(Write, indirect_io_short_write)
3879821f1d3SAlan Somers {
3889821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3899821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3909821f1d3SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
3919821f1d3SAlan Somers 	uint64_t ino = 42;
3929821f1d3SAlan Somers 	int fd;
3939821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
39412292a99SAlan Somers 	ssize_t bufsize0 = 11;
39512292a99SAlan Somers 	ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
39612292a99SAlan Somers 	const char *contents1 = CONTENTS + bufsize0;
3979821f1d3SAlan Somers 
3989821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
39912292a99SAlan Somers 	expect_open(ino, 0, 1);
400bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
401bda39894SAlan Somers 	expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
4029821f1d3SAlan Somers 
4039821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
404d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
4059821f1d3SAlan Somers 
4069821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4077fc0921dSAlan Somers 	leak(fd);
4089821f1d3SAlan Somers }
4099821f1d3SAlan Somers 
4109821f1d3SAlan Somers /*
41112292a99SAlan Somers  * When the direct_io option is used, filesystems are allowed to write less
41212292a99SAlan Somers  * data than requested.  We should return the short write to userland.
41312292a99SAlan Somers  */
41412292a99SAlan Somers TEST_F(Write, direct_io_short_write)
41512292a99SAlan Somers {
41612292a99SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
41712292a99SAlan Somers 	const char RELPATH[] = "some_file.txt";
41812292a99SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
41912292a99SAlan Somers 	uint64_t ino = 42;
42012292a99SAlan Somers 	int fd;
42112292a99SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
42212292a99SAlan Somers 	ssize_t halfbufsize = bufsize / 2;
42312292a99SAlan Somers 
42412292a99SAlan Somers 	expect_lookup(RELPATH, ino, 0);
42512292a99SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
426bda39894SAlan Somers 	expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
42712292a99SAlan Somers 
42812292a99SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
429d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
43012292a99SAlan Somers 
43112292a99SAlan Somers 	ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4327fc0921dSAlan Somers 	leak(fd);
43312292a99SAlan Somers }
43412292a99SAlan Somers 
43512292a99SAlan Somers /*
4369821f1d3SAlan Somers  * An insidious edge case: the filesystem returns a short write, and the
4379821f1d3SAlan Somers  * difference between what we requested and what it actually wrote crosses an
4389821f1d3SAlan Somers  * iov element boundary
4399821f1d3SAlan Somers  */
44012292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov)
4419821f1d3SAlan Somers {
4429821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4439821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4449821f1d3SAlan Somers 	const char *CONTENTS0 = "abcdefgh";
4459821f1d3SAlan Somers 	const char *CONTENTS1 = "ijklmnop";
4469821f1d3SAlan Somers 	const char *EXPECTED0 = "abcdefghijklmnop";
4479821f1d3SAlan Somers 	uint64_t ino = 42;
4489821f1d3SAlan Somers 	int fd;
4499821f1d3SAlan Somers 	ssize_t size0 = strlen(CONTENTS0) - 1;
4509821f1d3SAlan Somers 	ssize_t size1 = strlen(CONTENTS1) + 1;
4519821f1d3SAlan Somers 	ssize_t totalsize = size0 + size1;
4529821f1d3SAlan Somers 	struct iovec iov[2];
4539821f1d3SAlan Somers 
4549821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
4559821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
456bda39894SAlan Somers 	expect_write(ino, 0, totalsize, size0, EXPECTED0);
4579821f1d3SAlan Somers 
4589821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
459d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
4609821f1d3SAlan Somers 
4615a0b9a27SAlan Somers 	iov[0].iov_base = __DECONST(void*, CONTENTS0);
4629821f1d3SAlan Somers 	iov[0].iov_len = strlen(CONTENTS0);
4635a0b9a27SAlan Somers 	iov[1].iov_base = __DECONST(void*, CONTENTS1);
4649821f1d3SAlan Somers 	iov[1].iov_len = strlen(CONTENTS1);
46512292a99SAlan Somers 	ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
4667fc0921dSAlan Somers 	leak(fd);
4679821f1d3SAlan Somers }
4689821f1d3SAlan Somers 
469a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */
470a639731bSAlan Somers TEST_F(Write, rlimit_fsize)
471a639731bSAlan Somers {
472a639731bSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
473a639731bSAlan Somers 	const char RELPATH[] = "some_file.txt";
474a639731bSAlan Somers 	const char *CONTENTS = "abcdefgh";
475a639731bSAlan Somers 	struct rlimit rl;
476a639731bSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
477a639731bSAlan Somers 	off_t offset = 1'000'000'000;
478a639731bSAlan Somers 	uint64_t ino = 42;
479a639731bSAlan Somers 	int fd;
480a639731bSAlan Somers 
481a639731bSAlan Somers 	expect_lookup(RELPATH, ino, 0);
482a639731bSAlan Somers 	expect_open(ino, 0, 1);
483a639731bSAlan Somers 
484a639731bSAlan Somers 	rl.rlim_cur = offset;
485a639731bSAlan Somers 	rl.rlim_max = 10 * offset;
486a639731bSAlan Somers 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
487a639731bSAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
488a639731bSAlan Somers 
489a639731bSAlan Somers 	fd = open(FULLPATH, O_WRONLY);
490a639731bSAlan Somers 
491d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
492a639731bSAlan Somers 
493a639731bSAlan Somers 	ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
494a639731bSAlan Somers 	EXPECT_EQ(EFBIG, errno);
495a639731bSAlan Somers 	EXPECT_EQ(1, s_sigxfsz);
4967fc0921dSAlan Somers 	leak(fd);
497a639731bSAlan Somers }
498a639731bSAlan Somers 
4999821f1d3SAlan Somers /*
500b9e20197SAlan Somers  * A short read indicates EOF.  Test that nothing bad happens if we get EOF
501b9e20197SAlan Somers  * during the R of a RMW operation.
502b9e20197SAlan Somers  */
503f8ebf1cdSAlan Somers TEST_F(Write, eof_during_rmw)
504b9e20197SAlan Somers {
505b9e20197SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
506b9e20197SAlan Somers 	const char RELPATH[] = "some_file.txt";
507b9e20197SAlan Somers 	const char *CONTENTS = "abcdefgh";
508b9e20197SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
509b9e20197SAlan Somers 	uint64_t ino = 42;
510b9e20197SAlan Somers 	uint64_t offset = 1;
511*ae39db74SAlan Somers 	ssize_t bufsize = strlen(CONTENTS) + 1;
512b9e20197SAlan Somers 	off_t orig_fsize = 10;
513b9e20197SAlan Somers 	off_t truncated_fsize = 5;
514b9e20197SAlan Somers 	off_t final_fsize = bufsize;
515b9e20197SAlan Somers 	int fd;
516b9e20197SAlan Somers 
517b9e20197SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
518b9e20197SAlan Somers 	expect_open(ino, 0, 1);
519b9e20197SAlan Somers 	expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
520b9e20197SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
521b9e20197SAlan Somers 
522b9e20197SAlan Somers 	fd = open(FULLPATH, O_RDWR);
523d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
524b9e20197SAlan Somers 
525b9e20197SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
526b9e20197SAlan Somers 		<< strerror(errno);
5277fc0921dSAlan Somers 	leak(fd);
528b9e20197SAlan Somers }
529b9e20197SAlan Somers 
530b9e20197SAlan Somers /*
5319821f1d3SAlan Somers  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
5329821f1d3SAlan Somers  * write, then it must set the FUSE_WRITE_CACHE bit
5339821f1d3SAlan Somers  */
5349821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
535f8ebf1cdSAlan Somers TEST_F(Write, mmap)
5369821f1d3SAlan Somers {
5379821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5389821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5399821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
5409821f1d3SAlan Somers 	uint64_t ino = 42;
5419821f1d3SAlan Somers 	int fd;
5429821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5439821f1d3SAlan Somers 	void *p;
5449821f1d3SAlan Somers 	uint64_t offset = 10;
5459821f1d3SAlan Somers 	size_t len;
5469821f1d3SAlan Somers 	void *zeros, *expected;
5479821f1d3SAlan Somers 
5489821f1d3SAlan Somers 	len = getpagesize();
5499821f1d3SAlan Somers 
5509821f1d3SAlan Somers 	zeros = calloc(1, len);
5515a0b9a27SAlan Somers 	ASSERT_NE(nullptr, zeros);
5529821f1d3SAlan Somers 	expected = calloc(1, len);
5535a0b9a27SAlan Somers 	ASSERT_NE(nullptr, expected);
5549821f1d3SAlan Somers 	memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
5559821f1d3SAlan Somers 
5569821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, len);
5579821f1d3SAlan Somers 	expect_open(ino, 0, 1);
5589821f1d3SAlan Somers 	expect_read(ino, 0, len, len, zeros);
5599821f1d3SAlan Somers 	/*
5609821f1d3SAlan Somers 	 * Writes from the pager may or may not be associated with the correct
561cf437e2aSAlan Somers 	 * pid, so they must set FUSE_WRITE_CACHE.
5629821f1d3SAlan Somers 	 */
563bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
5649f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
5659821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
5669821f1d3SAlan Somers 
5679821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
568d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
5699821f1d3SAlan Somers 
5709821f1d3SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
5719821f1d3SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
5729821f1d3SAlan Somers 
5739821f1d3SAlan Somers 	memmove((uint8_t*)p + offset, CONTENTS, bufsize);
5749821f1d3SAlan Somers 
5759821f1d3SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
5769821f1d3SAlan Somers 	close(fd);	// Write mmap'd data on close
5779821f1d3SAlan Somers 
5789821f1d3SAlan Somers 	free(expected);
5799821f1d3SAlan Somers 	free(zeros);
5808e765737SAlan Somers 
5818e765737SAlan Somers 	leak(fd);
5829821f1d3SAlan Somers }
5839821f1d3SAlan Somers 
584f8ebf1cdSAlan Somers TEST_F(Write, pwrite)
5859821f1d3SAlan Somers {
5869821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5879821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5889821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
5899821f1d3SAlan Somers 	uint64_t ino = 42;
5906ca3b02bSAlan Somers 	uint64_t offset = m_maxbcachebuf;
5919821f1d3SAlan Somers 	int fd;
5929821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5939821f1d3SAlan Somers 
5949821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
5959821f1d3SAlan Somers 	expect_open(ino, 0, 1);
596bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
5979821f1d3SAlan Somers 
5989821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
599d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
6009821f1d3SAlan Somers 
6019821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
6029821f1d3SAlan Somers 		<< strerror(errno);
6037fc0921dSAlan Somers 	leak(fd);
6049821f1d3SAlan Somers }
6059821f1d3SAlan Somers 
606788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */
607788af953SAlan Somers TEST_F(Write, timestamps)
608788af953SAlan Somers {
609788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
610788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
611788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
612788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
613788af953SAlan Somers 	uint64_t ino = 42;
614788af953SAlan Somers 	struct stat sb0, sb1;
615788af953SAlan Somers 	int fd;
616788af953SAlan Somers 
617788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
618788af953SAlan Somers 	expect_open(ino, 0, 1);
619788af953SAlan Somers 	maybe_expect_write(ino, 0, bufsize, CONTENTS);
620788af953SAlan Somers 
621788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
622d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
623788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
624788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
625788af953SAlan Somers 
626788af953SAlan Somers 	nap();
627788af953SAlan Somers 
628788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno);
629788af953SAlan Somers 
630788af953SAlan Somers 	EXPECT_EQ(sb0.st_atime, sb1.st_atime);
631788af953SAlan Somers 	EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
632788af953SAlan Somers 	EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
6338e765737SAlan Somers 
6348e765737SAlan Somers 	leak(fd);
635788af953SAlan Somers }
636788af953SAlan Somers 
6379821f1d3SAlan Somers TEST_F(Write, write)
6389821f1d3SAlan Somers {
6399821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6409821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6419821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6429821f1d3SAlan Somers 	uint64_t ino = 42;
6439821f1d3SAlan Somers 	int fd;
6449821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6459821f1d3SAlan Somers 
6469821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6479821f1d3SAlan Somers 	expect_open(ino, 0, 1);
648bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
6499821f1d3SAlan Somers 
6509821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
651d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
6529821f1d3SAlan Somers 
6539821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6547fc0921dSAlan Somers 	leak(fd);
6559821f1d3SAlan Somers }
6569821f1d3SAlan Somers 
6579821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */
658f928dbcbSAlan Somers TEST_F(WriteMaxWrite, write)
6599821f1d3SAlan Somers {
6609821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6619821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6629821f1d3SAlan Somers 	int *contents;
6639821f1d3SAlan Somers 	uint64_t ino = 42;
6649821f1d3SAlan Somers 	int fd;
6659821f1d3SAlan Somers 	ssize_t halfbufsize, bufsize;
6669821f1d3SAlan Somers 
6678eecd9ceSAlan Somers 	halfbufsize = m_mock->m_maxwrite;
668f928dbcbSAlan Somers 	if (halfbufsize >= m_maxbcachebuf || halfbufsize >= m_maxphys)
669f928dbcbSAlan Somers 		GTEST_SKIP() << "Must lower m_maxwrite for this test";
6709821f1d3SAlan Somers 	bufsize = halfbufsize * 2;
6719821f1d3SAlan Somers 	contents = (int*)malloc(bufsize);
6725a0b9a27SAlan Somers 	ASSERT_NE(nullptr, contents);
6739821f1d3SAlan Somers 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
6749821f1d3SAlan Somers 		contents[i] = i;
6759821f1d3SAlan Somers 	}
6769821f1d3SAlan Somers 
6779821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6789821f1d3SAlan Somers 	expect_open(ino, 0, 1);
679f2704f05SAlan Somers 	maybe_expect_write(ino, 0, halfbufsize, contents);
68084879e46SAlan Somers 	maybe_expect_write(ino, halfbufsize, halfbufsize,
6819821f1d3SAlan Somers 		&contents[halfbufsize / sizeof(int)]);
6829821f1d3SAlan Somers 
6839821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
684d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
6859821f1d3SAlan Somers 
6869821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
6877fc0921dSAlan Somers 	leak(fd);
6889821f1d3SAlan Somers 
6899821f1d3SAlan Somers 	free(contents);
6909821f1d3SAlan Somers }
6919821f1d3SAlan Somers 
6929821f1d3SAlan Somers TEST_F(Write, write_nothing)
6939821f1d3SAlan Somers {
6949821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6959821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6969821f1d3SAlan Somers 	const char *CONTENTS = "";
6979821f1d3SAlan Somers 	uint64_t ino = 42;
6989821f1d3SAlan Somers 	int fd;
6999821f1d3SAlan Somers 	ssize_t bufsize = 0;
7009821f1d3SAlan Somers 
7019821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7029821f1d3SAlan Somers 	expect_open(ino, 0, 1);
7039821f1d3SAlan Somers 
7049821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
705d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7069821f1d3SAlan Somers 
7079821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7087fc0921dSAlan Somers 	leak(fd);
7099821f1d3SAlan Somers }
7109821f1d3SAlan Somers 
71116bd2d47SAlan Somers TEST_F(Write_7_8, write)
71216bd2d47SAlan Somers {
71316bd2d47SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
71416bd2d47SAlan Somers 	const char RELPATH[] = "some_file.txt";
71516bd2d47SAlan Somers 	const char *CONTENTS = "abcdefgh";
71616bd2d47SAlan Somers 	uint64_t ino = 42;
71716bd2d47SAlan Somers 	int fd;
71816bd2d47SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
71916bd2d47SAlan Somers 
72016bd2d47SAlan Somers 	expect_lookup(RELPATH, ino, 0);
72116bd2d47SAlan Somers 	expect_open(ino, 0, 1);
722bda39894SAlan Somers 	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
72316bd2d47SAlan Somers 
72416bd2d47SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
725d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
72616bd2d47SAlan Somers 
72716bd2d47SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7287fc0921dSAlan Somers 	leak(fd);
72916bd2d47SAlan Somers }
73016bd2d47SAlan Somers 
7319821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */
73284879e46SAlan Somers TEST_F(WriteBackAsync, close)
7339821f1d3SAlan Somers {
7349821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7359821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7369821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7379821f1d3SAlan Somers 	uint64_t ino = 42;
7389821f1d3SAlan Somers 	int fd;
7399821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7409821f1d3SAlan Somers 
7419821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7429821f1d3SAlan Somers 	expect_open(ino, 0, 1);
743bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
7449821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
7459821f1d3SAlan Somers 		ResultOf([=](auto in) {
74629edc611SAlan Somers 			return (in.header.opcode == FUSE_SETATTR);
7479821f1d3SAlan Somers 		}, Eq(true)),
7489821f1d3SAlan Somers 		_)
74929edc611SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
7509821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
75129edc611SAlan Somers 		out.body.attr.attr.ino = ino;	// Must match nodeid
7529821f1d3SAlan Somers 	})));
7539f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
7549821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
7559821f1d3SAlan Somers 
7569821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
7579821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7589821f1d3SAlan Somers 
7599821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7609821f1d3SAlan Somers 	close(fd);
7619821f1d3SAlan Somers }
7629821f1d3SAlan Somers 
7638eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */
7648eecd9ceSAlan Somers TEST_F(WriteCluster, clustering)
7658eecd9ceSAlan Somers {
7668eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7678eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
7688eecd9ceSAlan Somers 	uint64_t ino = 42;
7698eecd9ceSAlan Somers 	int i, fd;
7708eecd9ceSAlan Somers 	void *wbuf, *wbuf2x;
7716ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
7726ca3b02bSAlan Somers 	off_t filesize = 5 * bufsize;
7738eecd9ceSAlan Somers 
7748eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
7755a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf) << strerror(errno);
7768eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
7778eecd9ceSAlan Somers 	wbuf2x = malloc(2 * bufsize);
7785a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf2x) << strerror(errno);
7798eecd9ceSAlan Somers 	memset(wbuf2x, 'X', 2 * bufsize);
7808eecd9ceSAlan Somers 
7818eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
7828eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
7838eecd9ceSAlan Somers 	/*
7848eecd9ceSAlan Somers 	 * Writes of bufsize-bytes each should be clustered into greater sizes.
7858eecd9ceSAlan Somers 	 * The amount of clustering is adaptive, so the first write actually
7868eecd9ceSAlan Somers 	 * issued will be 2x bufsize and subsequent writes may be larger
7878eecd9ceSAlan Somers 	 */
7888eecd9ceSAlan Somers 	expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
7898eecd9ceSAlan Somers 	expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
7908eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
7918eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
7928eecd9ceSAlan Somers 
7938eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
7948eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7958eecd9ceSAlan Somers 
7968eecd9ceSAlan Somers 	for (i = 0; i < 4; i++) {
7978eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
7988eecd9ceSAlan Somers 			<< strerror(errno);
7998eecd9ceSAlan Somers 	}
8008eecd9ceSAlan Somers 	close(fd);
8017ee7e409SAlan Somers 	free(wbuf2x);
8027ee7e409SAlan Somers 	free(wbuf);
8038eecd9ceSAlan Somers }
8048eecd9ceSAlan Somers 
8058eecd9ceSAlan Somers /*
8068eecd9ceSAlan Somers  * When clustering writes, an I/O error to any of the cluster's children should
8078eecd9ceSAlan Somers  * not panic the system on unmount
8088eecd9ceSAlan Somers  */
8098eecd9ceSAlan Somers /*
8108eecd9ceSAlan Somers  * Disabled because it panics.
8118eecd9ceSAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
8128eecd9ceSAlan Somers  */
8138eecd9ceSAlan Somers TEST_F(WriteCluster, DISABLED_cluster_write_err)
8148eecd9ceSAlan Somers {
8158eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8168eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
8178eecd9ceSAlan Somers 	uint64_t ino = 42;
8188eecd9ceSAlan Somers 	int i, fd;
8198eecd9ceSAlan Somers 	void *wbuf;
8206ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
8216ca3b02bSAlan Somers 	off_t filesize = 4 * bufsize;
8228eecd9ceSAlan Somers 
8238eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
8245a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf) << strerror(errno);
8258eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
8268eecd9ceSAlan Somers 
8278eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
8288eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
8298eecd9ceSAlan Somers 	EXPECT_CALL(*m_mock, process(
8308eecd9ceSAlan Somers 		ResultOf([=](auto in) {
8318eecd9ceSAlan Somers 			return (in.header.opcode == FUSE_WRITE);
8328eecd9ceSAlan Somers 		}, Eq(true)),
8338eecd9ceSAlan Somers 		_)
8348eecd9ceSAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
8358eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
8368eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
8378eecd9ceSAlan Somers 
8388eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
8398eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8408eecd9ceSAlan Somers 
8418eecd9ceSAlan Somers 	for (i = 0; i < 3; i++) {
8428eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
8438eecd9ceSAlan Somers 			<< strerror(errno);
8448eecd9ceSAlan Somers 	}
8458eecd9ceSAlan Somers 	close(fd);
8467ee7e409SAlan Somers 	free(wbuf);
8478eecd9ceSAlan Somers }
8488eecd9ceSAlan Somers 
8499821f1d3SAlan Somers /*
8505fccbf31SAlan Somers  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
8515fccbf31SAlan Somers  * server.  The FUSE protocol explicitly allows that.
8525fccbf31SAlan Somers  */
8535fccbf31SAlan Somers TEST_F(WriteBack, rmw)
8545fccbf31SAlan Somers {
8555fccbf31SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8565fccbf31SAlan Somers 	const char RELPATH[] = "some_file.txt";
8575fccbf31SAlan Somers 	const char *CONTENTS = "abcdefgh";
8585fccbf31SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
8595fccbf31SAlan Somers 	uint64_t ino = 42;
8605fccbf31SAlan Somers 	uint64_t offset = 1;
8615fccbf31SAlan Somers 	off_t fsize = 10;
8625fccbf31SAlan Somers 	int fd;
8635fccbf31SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8645fccbf31SAlan Somers 
865cad67791SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
8665fccbf31SAlan Somers 	expect_open(ino, 0, 1);
867d4fd0c81SAlan Somers 	expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
86884879e46SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
8695fccbf31SAlan Somers 
8705fccbf31SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
871d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8725fccbf31SAlan Somers 
8735fccbf31SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
8745fccbf31SAlan Somers 		<< strerror(errno);
8757fc0921dSAlan Somers 	leak(fd);
8765fccbf31SAlan Somers }
8775fccbf31SAlan Somers 
8785fccbf31SAlan Somers /*
8799821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
8809821f1d3SAlan Somers  */
88184879e46SAlan Somers TEST_F(WriteBack, cache)
8829821f1d3SAlan Somers {
8839821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8849821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8859821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
8869821f1d3SAlan Somers 	uint64_t ino = 42;
8879821f1d3SAlan Somers 	int fd;
8889821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8898e765737SAlan Somers 	uint8_t readbuf[bufsize];
8909821f1d3SAlan Somers 
8919821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
8929821f1d3SAlan Somers 	expect_open(ino, 0, 1);
893bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
8949821f1d3SAlan Somers 
8959821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
896d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8979821f1d3SAlan Somers 
8989821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
8999821f1d3SAlan Somers 	/*
9009821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
9019821f1d3SAlan Somers 	 * filesystem daemon
9029821f1d3SAlan Somers 	 */
9039821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
9049821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
9057fc0921dSAlan Somers 	leak(fd);
9069821f1d3SAlan Somers }
9079821f1d3SAlan Somers 
9089821f1d3SAlan Somers /*
9099821f1d3SAlan Somers  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
9109821f1d3SAlan Somers  * an odd test, because it would be unusual to use O_DIRECT for writes but not
9119821f1d3SAlan Somers  * reads.
9129821f1d3SAlan Somers  */
9139821f1d3SAlan Somers TEST_F(WriteBack, o_direct)
9149821f1d3SAlan Somers {
9159821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9169821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
9179821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
9189821f1d3SAlan Somers 	uint64_t ino = 42;
9199821f1d3SAlan Somers 	int fd;
9209821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
9218e765737SAlan Somers 	uint8_t readbuf[bufsize];
9229821f1d3SAlan Somers 
9239821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
9249821f1d3SAlan Somers 	expect_open(ino, 0, 1);
925bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
926bda39894SAlan Somers 		CONTENTS);
9279821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
9289821f1d3SAlan Somers 
9299821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
930d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
9319821f1d3SAlan Somers 
9329821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9339821f1d3SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
9349821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
9359821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
9369821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
9377fc0921dSAlan Somers 	leak(fd);
9389821f1d3SAlan Somers }
9399821f1d3SAlan Somers 
940a62772a7SAlan Somers TEST_F(WriteBack, direct_io)
941a62772a7SAlan Somers {
942a62772a7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
943a62772a7SAlan Somers 	const char RELPATH[] = "some_file.txt";
944a62772a7SAlan Somers 	const char *CONTENTS = "abcdefgh";
945a62772a7SAlan Somers 	uint64_t ino = 42;
946a62772a7SAlan Somers 	int fd;
947a62772a7SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
948a62772a7SAlan Somers 	uint8_t readbuf[bufsize];
949a62772a7SAlan Somers 
950a62772a7SAlan Somers 	expect_lookup(RELPATH, ino, 0);
951a62772a7SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
952a62772a7SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
953a62772a7SAlan Somers 		CONTENTS);
954a62772a7SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
955a62772a7SAlan Somers 
956a62772a7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
957d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
958a62772a7SAlan Somers 
959a62772a7SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
960a62772a7SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
961a62772a7SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
962a62772a7SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
963a62772a7SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
964a62772a7SAlan Somers 	leak(fd);
965a62772a7SAlan Somers }
966a62772a7SAlan Somers 
967a62772a7SAlan Somers /*
968a62772a7SAlan Somers  * mmap should still be possible even if the server used direct_io.  Mmap will
969a62772a7SAlan Somers  * still use the cache, though.
970a62772a7SAlan Somers  *
971a62772a7SAlan Somers  * Regression test for bug 247276
972a62772a7SAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276
973a62772a7SAlan Somers  */
974a62772a7SAlan Somers TEST_F(WriteBack, mmap_direct_io)
975a62772a7SAlan Somers {
976a62772a7SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
977a62772a7SAlan Somers 	const char RELPATH[] = "some_file.txt";
978a62772a7SAlan Somers 	const char *CONTENTS = "abcdefgh";
979a62772a7SAlan Somers 	uint64_t ino = 42;
980a62772a7SAlan Somers 	int fd;
981a62772a7SAlan Somers 	size_t len;
982a62772a7SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
983a62772a7SAlan Somers 	void *p, *zeros;
984a62772a7SAlan Somers 
985a62772a7SAlan Somers 	len = getpagesize();
986a62772a7SAlan Somers 	zeros = calloc(1, len);
987a62772a7SAlan Somers 	ASSERT_NE(nullptr, zeros);
988a62772a7SAlan Somers 
989a62772a7SAlan Somers 	expect_lookup(RELPATH, ino, len);
990a62772a7SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
991a62772a7SAlan Somers 	expect_read(ino, 0, len, len, zeros);
992a62772a7SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
993a62772a7SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros);
994a62772a7SAlan Somers 	expect_release(ino, ReturnErrno(0));
995a62772a7SAlan Somers 
996a62772a7SAlan Somers 	fd = open(FULLPATH, O_RDWR);
997d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
998a62772a7SAlan Somers 
999a62772a7SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1000a62772a7SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1001a62772a7SAlan Somers 
1002a62772a7SAlan Somers 	memmove((uint8_t*)p, CONTENTS, bufsize);
1003a62772a7SAlan Somers 
1004a62772a7SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
1005a62772a7SAlan Somers 	close(fd);	// Write mmap'd data on close
1006a62772a7SAlan Somers 
1007a62772a7SAlan Somers 	free(zeros);
1008a62772a7SAlan Somers }
1009a62772a7SAlan Somers 
10109821f1d3SAlan Somers /*
101184879e46SAlan Somers  * When mounted with -o async, the writeback cache mode should delay writes
101284879e46SAlan Somers  */
101384879e46SAlan Somers TEST_F(WriteBackAsync, delay)
101484879e46SAlan Somers {
101584879e46SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
101684879e46SAlan Somers 	const char RELPATH[] = "some_file.txt";
101784879e46SAlan Somers 	const char *CONTENTS = "abcdefgh";
101884879e46SAlan Somers 	uint64_t ino = 42;
101984879e46SAlan Somers 	int fd;
102084879e46SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
102184879e46SAlan Somers 
102284879e46SAlan Somers 	expect_lookup(RELPATH, ino, 0);
102384879e46SAlan Somers 	expect_open(ino, 0, 1);
102484879e46SAlan Somers 	/* Write should be cached, but FUSE_WRITE shouldn't be sent */
102584879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
102684879e46SAlan Somers 		ResultOf([=](auto in) {
102784879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE);
102884879e46SAlan Somers 		}, Eq(true)),
102984879e46SAlan Somers 		_)
103084879e46SAlan Somers 	).Times(0);
103184879e46SAlan Somers 
103284879e46SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1033d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
103484879e46SAlan Somers 
103584879e46SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
103684879e46SAlan Somers 
103784879e46SAlan Somers 	/* Don't close the file because that would flush the cache */
10388e765737SAlan Somers 	leak(fd);
103984879e46SAlan Somers }
104084879e46SAlan Somers 
104184879e46SAlan Somers /*
1042669a092aSAlan Somers  * A direct write should not evict dirty cached data from outside of its own
1043669a092aSAlan Somers  * byte range.
1044669a092aSAlan Somers  */
1045669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached)
1046669a092aSAlan Somers {
1047669a092aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1048669a092aSAlan Somers 	const char RELPATH[] = "some_file.txt";
1049669a092aSAlan Somers 	const char CONTENTS0[] = "abcdefgh";
1050669a092aSAlan Somers 	const char CONTENTS1[] = "ijklmnop";
1051669a092aSAlan Somers 	uint64_t ino = 42;
1052669a092aSAlan Somers 	int fd;
1053669a092aSAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
1054669a092aSAlan Somers 	ssize_t fsize = 2 * m_maxbcachebuf;
1055669a092aSAlan Somers 	char readbuf[bufsize];
1056669a092aSAlan Somers 	void *zeros;
1057669a092aSAlan Somers 
1058669a092aSAlan Somers 	zeros = calloc(1, m_maxbcachebuf);
1059669a092aSAlan Somers 	ASSERT_NE(nullptr, zeros);
1060669a092aSAlan Somers 
1061669a092aSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
1062669a092aSAlan Somers 	expect_open(ino, 0, 1);
1063669a092aSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros);
1064669a092aSAlan Somers 	FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0,
1065669a092aSAlan Somers 		CONTENTS1);
1066669a092aSAlan Somers 
1067669a092aSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1068d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1069669a092aSAlan Somers 
1070669a092aSAlan Somers 	// Cache first block with dirty data.  This will entail first reading
1071669a092aSAlan Somers 	// the existing data.
1072669a092aSAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0))
1073669a092aSAlan Somers 		<< strerror(errno);
1074669a092aSAlan Somers 
1075669a092aSAlan Somers 	// Write directly to second block
1076669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1077669a092aSAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf))
1078669a092aSAlan Somers 		<< strerror(errno);
1079669a092aSAlan Somers 
1080669a092aSAlan Somers 	// Read from the first block again.  Should be serviced by cache.
1081669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1082669a092aSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno);
1083669a092aSAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS0);
1084669a092aSAlan Somers 
1085669a092aSAlan Somers 	leak(fd);
1086669a092aSAlan Somers 	free(zeros);
1087669a092aSAlan Somers }
1088669a092aSAlan Somers 
1089669a092aSAlan Somers /*
1090669a092aSAlan Somers  * If a direct io write partially overlaps one or two blocks of dirty cached
1091669a092aSAlan Somers  * data, No dirty data should be lost.  Admittedly this is a weird test,
1092669a092aSAlan Somers  * because it would be unusual to use O_DIRECT and the writeback cache.
1093669a092aSAlan Somers  */
1094669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block)
1095669a092aSAlan Somers {
1096669a092aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1097669a092aSAlan Somers 	const char RELPATH[] = "some_file.txt";
1098669a092aSAlan Somers 	uint64_t ino = 42;
1099669a092aSAlan Somers 	int fd;
1100669a092aSAlan Somers 	off_t bs = m_maxbcachebuf;
1101669a092aSAlan Somers 	ssize_t fsize = 3 * bs;
1102669a092aSAlan Somers 	void *readbuf, *zeros, *ones, *zeroones, *onezeros;
1103669a092aSAlan Somers 
1104669a092aSAlan Somers 	readbuf = malloc(bs);
1105669a092aSAlan Somers 	ASSERT_NE(nullptr, readbuf) << strerror(errno);
1106669a092aSAlan Somers 	zeros = calloc(1, 3 * bs);
1107669a092aSAlan Somers 	ASSERT_NE(nullptr, zeros);
1108669a092aSAlan Somers 	ones = calloc(1, 2 * bs);
1109669a092aSAlan Somers 	ASSERT_NE(nullptr, ones);
1110669a092aSAlan Somers 	memset(ones, 1, 2 * bs);
1111669a092aSAlan Somers 	zeroones = calloc(1, bs);
1112669a092aSAlan Somers 	ASSERT_NE(nullptr, zeroones);
1113669a092aSAlan Somers 	memset((uint8_t*)zeroones + bs / 2, 1, bs / 2);
1114669a092aSAlan Somers 	onezeros = calloc(1, bs);
1115669a092aSAlan Somers 	ASSERT_NE(nullptr, onezeros);
1116669a092aSAlan Somers 	memset(onezeros, 1, bs / 2);
1117669a092aSAlan Somers 
1118669a092aSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
1119669a092aSAlan Somers 	expect_open(ino, 0, 1);
1120669a092aSAlan Somers 
1121669a092aSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1122d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1123669a092aSAlan Somers 
1124669a092aSAlan Somers 	/* Cache first and third blocks with dirty data.  */
1125669a092aSAlan Somers 	ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno);
1126669a092aSAlan Somers 
1127669a092aSAlan Somers 	/*
1128669a092aSAlan Somers 	 * Write directly to all three blocks.  The partially written blocks
1129669a092aSAlan Somers 	 * will be flushed because they're dirty.
1130669a092aSAlan Somers 	 */
1131669a092aSAlan Somers 	FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros);
1132669a092aSAlan Somers 	FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros);
1133669a092aSAlan Somers 	/* The direct write is split in two because of the m_maxwrite value */
1134669a092aSAlan Somers 	FuseTest::expect_write(ino,     bs / 2, bs, bs, 0, 0, ones);
1135669a092aSAlan Somers 	FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones);
1136669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1137669a092aSAlan Somers 	ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno);
1138669a092aSAlan Somers 
1139669a092aSAlan Somers 	/*
1140669a092aSAlan Somers 	 * Read from both the valid and invalid portions of the first and third
1141669a092aSAlan Somers 	 * blocks again.  This will entail FUSE_READ operations because these
1142669a092aSAlan Somers 	 * blocks were invalidated by the direct write.
1143669a092aSAlan Somers 	 */
1144669a092aSAlan Somers 	expect_read(ino, 0, bs, bs, zeroones);
1145669a092aSAlan Somers 	expect_read(ino, 2 * bs, bs, bs, onezeros);
1146669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1147669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno);
1148669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1149669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2))
1150669a092aSAlan Somers 		<< strerror(errno);
1151669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1152669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2))
1153669a092aSAlan Somers 		<< strerror(errno);
1154669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1155669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs))
1156669a092aSAlan Somers 		<< strerror(errno);
1157669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1158669a092aSAlan Somers 
1159669a092aSAlan Somers 	leak(fd);
1160669a092aSAlan Somers 	free(zeroones);
1161669a092aSAlan Somers 	free(onezeros);
1162669a092aSAlan Somers 	free(ones);
1163669a092aSAlan Somers 	free(zeros);
1164669a092aSAlan Somers 	free(readbuf);
1165669a092aSAlan Somers }
1166669a092aSAlan Somers 
1167669a092aSAlan Somers /*
1168aef22f2dSAlan Somers  * In WriteBack mode, writes may be cached beyond what the server thinks is the
1169aef22f2dSAlan Somers  * EOF.  In this case, a short read at EOF should _not_ cause fusefs to update
1170aef22f2dSAlan Somers  * the file's size.
1171aef22f2dSAlan Somers  */
1172aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof)
1173aef22f2dSAlan Somers {
1174aef22f2dSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1175aef22f2dSAlan Somers 	const char RELPATH[] = "some_file.txt";
1176aef22f2dSAlan Somers 	const char *CONTENTS0 = "abcdefgh";
1177aef22f2dSAlan Somers 	const char *CONTENTS1 = "ijklmnop";
1178aef22f2dSAlan Somers 	uint64_t ino = 42;
1179aef22f2dSAlan Somers 	int fd;
1180aef22f2dSAlan Somers 	off_t offset = m_maxbcachebuf;
1181aef22f2dSAlan Somers 	ssize_t wbufsize = strlen(CONTENTS1);
1182aef22f2dSAlan Somers 	off_t old_filesize = (off_t)strlen(CONTENTS0);
1183aef22f2dSAlan Somers 	ssize_t rbufsize = 2 * old_filesize;
1184aef22f2dSAlan Somers 	char readbuf[rbufsize];
1185aef22f2dSAlan Somers 	size_t holesize = rbufsize - old_filesize;
1186aef22f2dSAlan Somers 	char hole[holesize];
1187aef22f2dSAlan Somers 	struct stat sb;
1188aef22f2dSAlan Somers 	ssize_t r;
1189aef22f2dSAlan Somers 
1190aef22f2dSAlan Somers 	expect_lookup(RELPATH, ino, 0);
1191aef22f2dSAlan Somers 	expect_open(ino, 0, 1);
1192aef22f2dSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
1193aef22f2dSAlan Somers 
1194aef22f2dSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1195d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1196aef22f2dSAlan Somers 
1197aef22f2dSAlan Somers 	/* Write and cache data beyond EOF */
1198aef22f2dSAlan Somers 	ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
1199aef22f2dSAlan Somers 		<< strerror(errno);
1200aef22f2dSAlan Somers 
1201aef22f2dSAlan Somers 	/* Read from the old EOF */
1202aef22f2dSAlan Somers 	r = pread(fd, readbuf, rbufsize, 0);
1203aef22f2dSAlan Somers 	ASSERT_LE(0, r) << strerror(errno);
1204aef22f2dSAlan Somers 	EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
1205aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
1206aef22f2dSAlan Somers 	bzero(hole, holesize);
1207aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
1208aef22f2dSAlan Somers 
1209aef22f2dSAlan Somers 	/* The file's size should still be what was established by pwrite */
1210aef22f2dSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1211aef22f2dSAlan Somers 	EXPECT_EQ(offset + wbufsize, sb.st_size);
12127fc0921dSAlan Somers 	leak(fd);
1213aef22f2dSAlan Somers }
1214aef22f2dSAlan Somers 
1215aef22f2dSAlan Somers /*
1216788af953SAlan Somers  * When a file has dirty writes that haven't been flushed, the server's notion
1217788af953SAlan Somers  * of its mtime and ctime will be wrong.  The kernel should ignore those if it
1218788af953SAlan Somers  * gets them from a FUSE_GETATTR before flushing.
1219788af953SAlan Somers  */
1220788af953SAlan Somers TEST_F(WriteBackAsync, timestamps)
1221788af953SAlan Somers {
1222788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1223788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1224788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1225788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1226788af953SAlan Somers 	uint64_t ino = 42;
1227788af953SAlan Somers 	uint64_t attr_valid = 0;
1228788af953SAlan Somers 	uint64_t attr_valid_nsec = 0;
1229788af953SAlan Somers 	uint64_t server_time = 12345;
1230788af953SAlan Somers 	mode_t mode = S_IFREG | 0644;
1231788af953SAlan Somers 	int fd;
1232788af953SAlan Somers 
1233788af953SAlan Somers 	struct stat sb;
1234788af953SAlan Somers 
1235788af953SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
1236788af953SAlan Somers 	.WillRepeatedly(Invoke(
1237788af953SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
1238788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
1239788af953SAlan Somers 		out.body.entry.attr.mode = mode;
1240788af953SAlan Somers 		out.body.entry.nodeid = ino;
1241788af953SAlan Somers 		out.body.entry.attr.nlink = 1;
1242788af953SAlan Somers 		out.body.entry.attr_valid = attr_valid;
1243788af953SAlan Somers 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
1244788af953SAlan Somers 	})));
1245788af953SAlan Somers 	expect_open(ino, 0, 1);
1246788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1247788af953SAlan Somers 		ResultOf([=](auto in) {
1248788af953SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
1249788af953SAlan Somers 				in.header.nodeid == ino);
1250788af953SAlan Somers 		}, Eq(true)),
1251788af953SAlan Somers 		_)
1252788af953SAlan Somers 	).WillRepeatedly(Invoke(
1253788af953SAlan Somers 	ReturnImmediate([=](auto i __unused, auto& out) {
1254788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1255788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1256788af953SAlan Somers 		out.body.attr.attr.mode = mode;
1257788af953SAlan Somers 		out.body.attr.attr_valid = attr_valid;
1258788af953SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
1259788af953SAlan Somers 		out.body.attr.attr.atime = server_time;
1260788af953SAlan Somers 		out.body.attr.attr.mtime = server_time;
1261788af953SAlan Somers 		out.body.attr.attr.ctime = server_time;
1262788af953SAlan Somers 	})));
1263788af953SAlan Somers 
1264788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1265d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1266788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1267788af953SAlan Somers 
1268788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1269788af953SAlan Somers 	EXPECT_EQ((time_t)server_time, sb.st_atime);
1270788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_mtime);
1271788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_ctime);
12728e765737SAlan Somers 
12738e765737SAlan Somers 	leak(fd);
1274788af953SAlan Somers }
1275788af953SAlan Somers 
1276788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */
1277788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr)
1278788af953SAlan Somers {
1279788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1280788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1281788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1282788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1283788af953SAlan Somers 	uint64_t ino = 42;
1284788af953SAlan Somers 	const mode_t newmode = 0755;
1285788af953SAlan Somers 	int fd;
1286788af953SAlan Somers 
1287788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1288788af953SAlan Somers 	expect_open(ino, 0, 1);
1289788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1290788af953SAlan Somers 		ResultOf([=](auto in) {
12910a8fe2d3SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1292788af953SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1293788af953SAlan Somers 				in.header.nodeid == ino &&
1294788af953SAlan Somers 				in.body.setattr.valid == valid);
1295788af953SAlan Somers 		}, Eq(true)),
1296788af953SAlan Somers 		_)
1297788af953SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1298788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1299788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1300788af953SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1301788af953SAlan Somers 	})));
1302788af953SAlan Somers 
1303788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1304d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1305788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1306788af953SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
13078e765737SAlan Somers 
13088e765737SAlan Somers 	leak(fd);
1309788af953SAlan Somers }
1310788af953SAlan Somers 
1311fef46454SAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */
1312fef46454SAlan Somers TEST_P(TimeGran, timestamps_during_setattr)
1313fef46454SAlan Somers {
1314fef46454SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1315fef46454SAlan Somers 	const char RELPATH[] = "some_file.txt";
1316fef46454SAlan Somers 	const char *CONTENTS = "abcdefgh";
1317fef46454SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1318fef46454SAlan Somers 	uint64_t ino = 42;
1319fef46454SAlan Somers 	const mode_t newmode = 0755;
1320fef46454SAlan Somers 	int fd;
1321fef46454SAlan Somers 
1322fef46454SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1323fef46454SAlan Somers 	expect_open(ino, 0, 1);
1324fef46454SAlan Somers 	EXPECT_CALL(*m_mock, process(
1325fef46454SAlan Somers 		ResultOf([=](auto in) {
1326fef46454SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1327fef46454SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1328fef46454SAlan Somers 				in.header.nodeid == ino &&
1329fef46454SAlan Somers 				in.body.setattr.valid == valid &&
1330fef46454SAlan Somers 				in.body.setattr.mtimensec % m_time_gran == 0 &&
1331fef46454SAlan Somers 				in.body.setattr.ctimensec % m_time_gran == 0);
1332fef46454SAlan Somers 		}, Eq(true)),
1333fef46454SAlan Somers 		_)
1334fef46454SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1335fef46454SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1336fef46454SAlan Somers 		out.body.attr.attr.ino = ino;
1337fef46454SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1338fef46454SAlan Somers 	})));
1339fef46454SAlan Somers 
1340fef46454SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1341d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
1342fef46454SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1343fef46454SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
13448e765737SAlan Somers 
13458e765737SAlan Somers 	leak(fd);
1346fef46454SAlan Somers }
1347fef46454SAlan Somers 
1348fef46454SAlan Somers INSTANTIATE_TEST_CASE_P(RA, TimeGran, Range(0u, 10u));
1349fef46454SAlan Somers 
1350788af953SAlan Somers /*
13519821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
13529821f1d3SAlan Somers  */
1353f8ebf1cdSAlan Somers TEST_F(Write, writethrough)
13549821f1d3SAlan Somers {
13559821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
13569821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
13579821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
13589821f1d3SAlan Somers 	uint64_t ino = 42;
13599821f1d3SAlan Somers 	int fd;
13609821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
13618e765737SAlan Somers 	uint8_t readbuf[bufsize];
13629821f1d3SAlan Somers 
13639821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
13649821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1365bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
13669821f1d3SAlan Somers 
13679821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1368d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
13699821f1d3SAlan Somers 
13709821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
13719821f1d3SAlan Somers 	/*
13729821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
13739821f1d3SAlan Somers 	 * filesystem daemon
13749821f1d3SAlan Somers 	 */
1375b5aaf286SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
13769821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
13777fc0921dSAlan Somers 	leak(fd);
13789821f1d3SAlan Somers }
13799821f1d3SAlan Somers 
13800d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */
13810d3a88d7SAlan Somers TEST_F(Write, update_file_size)
13829821f1d3SAlan Somers {
13839821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
13849821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
13859821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
13869821f1d3SAlan Somers 	struct stat sb;
13879821f1d3SAlan Somers 	uint64_t ino = 42;
13889821f1d3SAlan Somers 	int fd;
13899821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
13909821f1d3SAlan Somers 
13919821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
13929821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1393bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
13949821f1d3SAlan Somers 
13959821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1396d2621689SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
13979821f1d3SAlan Somers 
13989821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
13999821f1d3SAlan Somers 	/* Get cached attributes */
14009821f1d3SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
14019821f1d3SAlan Somers 	ASSERT_EQ(bufsize, sb.st_size);
14027fc0921dSAlan Somers 	leak(fd);
14039821f1d3SAlan Somers }
1404