xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision c2265ae7a8060fa843bfeaf71afb551c588deb28)
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.
299821f1d3SAlan Somers  */
309821f1d3SAlan Somers 
319821f1d3SAlan Somers extern "C" {
328eecd9ceSAlan Somers #include <sys/param.h>
339821f1d3SAlan Somers #include <sys/mman.h>
34a639731bSAlan Somers #include <sys/resource.h>
359821f1d3SAlan Somers #include <sys/stat.h>
36a639731bSAlan Somers #include <sys/time.h>
379821f1d3SAlan Somers #include <sys/uio.h>
389821f1d3SAlan Somers 
399821f1d3SAlan Somers #include <aio.h>
409821f1d3SAlan Somers #include <fcntl.h>
41a639731bSAlan Somers #include <signal.h>
429821f1d3SAlan Somers #include <unistd.h>
439821f1d3SAlan Somers }
449821f1d3SAlan Somers 
459821f1d3SAlan Somers #include "mockfs.hh"
469821f1d3SAlan Somers #include "utils.hh"
479821f1d3SAlan Somers 
489821f1d3SAlan Somers using namespace testing;
499821f1d3SAlan Somers 
509821f1d3SAlan Somers class Write: public FuseTest {
519821f1d3SAlan Somers 
529821f1d3SAlan Somers public:
53a639731bSAlan Somers static sig_atomic_t s_sigxfsz;
54a639731bSAlan Somers 
55a639731bSAlan Somers void SetUp() {
56a639731bSAlan Somers 	s_sigxfsz = 0;
57a639731bSAlan Somers 	FuseTest::SetUp();
58a639731bSAlan Somers }
59a639731bSAlan Somers 
60a639731bSAlan Somers void TearDown() {
61a639731bSAlan Somers 	struct sigaction sa;
62a639731bSAlan Somers 
63a639731bSAlan Somers 	bzero(&sa, sizeof(sa));
64a639731bSAlan Somers 	sa.sa_handler = SIG_DFL;
65a639731bSAlan Somers 	sigaction(SIGXFSZ, &sa, NULL);
66a639731bSAlan Somers 
67a639731bSAlan Somers 	FuseTest::TearDown();
68a639731bSAlan Somers }
699821f1d3SAlan Somers 
709821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
719821f1d3SAlan Somers {
729821f1d3SAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
739821f1d3SAlan Somers }
749821f1d3SAlan Somers 
759821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r)
769821f1d3SAlan Somers {
779821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
789821f1d3SAlan Somers 		ResultOf([=](auto in) {
7929edc611SAlan Somers 			return (in.header.opcode == FUSE_RELEASE &&
8029edc611SAlan Somers 				in.header.nodeid == ino);
819821f1d3SAlan Somers 		}, Eq(true)),
829821f1d3SAlan Somers 		_)
839821f1d3SAlan Somers 	).WillRepeatedly(Invoke(r));
849821f1d3SAlan Somers }
859821f1d3SAlan Somers 
86bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
87bda39894SAlan Somers 	uint64_t osize, const void *contents)
88bda39894SAlan Somers {
89bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
90bda39894SAlan Somers }
91bda39894SAlan Somers 
9284879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */
9384879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
9484879e46SAlan Somers 	const void *contents)
9584879e46SAlan Somers {
9684879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
9784879e46SAlan Somers 		ResultOf([=](auto in) {
9884879e46SAlan Somers 			const char *buf = (const char*)in.body.bytes +
9984879e46SAlan Somers 				sizeof(struct fuse_write_in);
10084879e46SAlan Somers 
10184879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE &&
10284879e46SAlan Somers 				in.header.nodeid == ino &&
10384879e46SAlan Somers 				in.body.write.offset == offset  &&
10484879e46SAlan Somers 				in.body.write.size == size &&
10584879e46SAlan Somers 				0 == bcmp(buf, contents, size));
10684879e46SAlan Somers 		}, Eq(true)),
10784879e46SAlan Somers 		_)
10884879e46SAlan Somers 	).Times(AtMost(1))
10984879e46SAlan Somers 	.WillRepeatedly(Invoke(
11084879e46SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
11184879e46SAlan Somers 			SET_OUT_HEADER_LEN(out, write);
11284879e46SAlan Somers 			out.body.write.size = size;
11384879e46SAlan Somers 		})
11484879e46SAlan Somers 	));
11584879e46SAlan Somers }
11684879e46SAlan Somers 
1179821f1d3SAlan Somers };
1189821f1d3SAlan Somers 
119a639731bSAlan Somers sig_atomic_t Write::s_sigxfsz = 0;
120a639731bSAlan Somers 
12116bd2d47SAlan Somers class Write_7_8: public FuseTest {
12216bd2d47SAlan Somers 
12316bd2d47SAlan Somers public:
12416bd2d47SAlan Somers virtual void SetUp() {
12516bd2d47SAlan Somers 	m_kernel_minor_version = 8;
12616bd2d47SAlan Somers 	FuseTest::SetUp();
12716bd2d47SAlan Somers }
12816bd2d47SAlan Somers 
12916bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
13016bd2d47SAlan Somers {
13116bd2d47SAlan Somers 	FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
13216bd2d47SAlan Somers }
13316bd2d47SAlan Somers 
13416bd2d47SAlan Somers };
13516bd2d47SAlan Somers 
1369821f1d3SAlan Somers class AioWrite: public Write {
1379821f1d3SAlan Somers virtual void SetUp() {
138*c2265ae7SAlan Somers 	if (!is_unsafe_aio_enabled())
1399821f1d3SAlan Somers 		GTEST_SKIP() <<
1409821f1d3SAlan Somers 			"vfs.aio.enable_unsafe must be set for this test";
141*c2265ae7SAlan Somers 	FuseTest::SetUp();
1429821f1d3SAlan Somers }
1439821f1d3SAlan Somers };
1449821f1d3SAlan Somers 
1459821f1d3SAlan Somers /* Tests for the writeback cache mode */
1469821f1d3SAlan Somers class WriteBack: public Write {
147bda39894SAlan Somers public:
1489821f1d3SAlan Somers virtual void SetUp() {
149f8ebf1cdSAlan Somers 	m_init_flags |= FUSE_WRITEBACK_CACHE;
1509821f1d3SAlan Somers 	FuseTest::SetUp();
1519821f1d3SAlan Somers 	if (IsSkipped())
1529821f1d3SAlan Somers 		return;
1539821f1d3SAlan Somers }
1549821f1d3SAlan Somers 
155bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
156bda39894SAlan Somers 	uint64_t osize, const void *contents)
157bda39894SAlan Somers {
158bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
159bda39894SAlan Somers 		contents);
160bda39894SAlan Somers }
1619821f1d3SAlan Somers };
1629821f1d3SAlan Somers 
16384879e46SAlan Somers class WriteBackAsync: public WriteBack {
16484879e46SAlan Somers public:
16584879e46SAlan Somers virtual void SetUp() {
16684879e46SAlan Somers 	m_async = true;
16784879e46SAlan Somers 	WriteBack::SetUp();
16884879e46SAlan Somers }
16984879e46SAlan Somers };
17084879e46SAlan Somers 
171fef46454SAlan Somers class TimeGran: public WriteBackAsync, public WithParamInterface<unsigned> {
172fef46454SAlan Somers public:
173fef46454SAlan Somers virtual void SetUp() {
174fef46454SAlan Somers 	m_time_gran = 1 << GetParam();
175fef46454SAlan Somers 	WriteBackAsync::SetUp();
176fef46454SAlan Somers }
177fef46454SAlan Somers };
178fef46454SAlan Somers 
1798eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */
1808eecd9ceSAlan Somers class WriteCluster: public WriteBack {
1818eecd9ceSAlan Somers public:
1828eecd9ceSAlan Somers virtual void SetUp() {
1838eecd9ceSAlan Somers 	m_async = true;
184f2704f05SAlan Somers 	m_maxwrite = m_maxphys;
1858eecd9ceSAlan Somers 	WriteBack::SetUp();
186f8ebf1cdSAlan Somers 	if (m_maxphys < 2 * DFLTPHYS)
187f8ebf1cdSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
188f8ebf1cdSAlan Somers 			<< " for this test";
189f2704f05SAlan Somers 	if (m_maxphys < 2 * m_maxbcachebuf)
1906ca3b02bSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf"
1916ca3b02bSAlan Somers 			<< " for this test";
1928eecd9ceSAlan Somers }
1938eecd9ceSAlan Somers };
1948eecd9ceSAlan Somers 
195a639731bSAlan Somers void sigxfsz_handler(int __unused sig) {
196a639731bSAlan Somers 	Write::s_sigxfsz = 1;
197a639731bSAlan Somers }
198a639731bSAlan Somers 
1999821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */
2009821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
2019821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write)
2029821f1d3SAlan Somers {
2039821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2049821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2059821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
2069821f1d3SAlan Somers 	uint64_t ino = 42;
2079821f1d3SAlan Somers 	uint64_t offset = 4096;
2089821f1d3SAlan Somers 	int fd;
2099821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
2109821f1d3SAlan Somers 	struct aiocb iocb, *piocb;
2119821f1d3SAlan Somers 
2129821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
2139821f1d3SAlan Somers 	expect_open(ino, 0, 1);
214bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
2159821f1d3SAlan Somers 
2169821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
2179821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
2189821f1d3SAlan Somers 
2199821f1d3SAlan Somers 	iocb.aio_nbytes = bufsize;
2209821f1d3SAlan Somers 	iocb.aio_fildes = fd;
2215a0b9a27SAlan Somers 	iocb.aio_buf = __DECONST(void *, CONTENTS);
2229821f1d3SAlan Somers 	iocb.aio_offset = offset;
2239821f1d3SAlan Somers 	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
2249821f1d3SAlan Somers 	ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
2259821f1d3SAlan Somers 	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
2267fc0921dSAlan Somers 	leak(fd);
2279821f1d3SAlan Somers }
2289821f1d3SAlan Somers 
2299821f1d3SAlan Somers /*
2309821f1d3SAlan Somers  * When a file is opened with O_APPEND, we should forward that flag to
2319821f1d3SAlan Somers  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
2329821f1d3SAlan Somers  * offset internally.  That way we'll work both with filesystems that
2339821f1d3SAlan Somers  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
2349821f1d3SAlan Somers  * simply use the offset).
2359821f1d3SAlan Somers  *
2369821f1d3SAlan Somers  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
2379821f1d3SAlan Somers  * Open.o_append test.
2389821f1d3SAlan Somers  */
2399821f1d3SAlan Somers TEST_F(Write, append)
2409821f1d3SAlan Somers {
2419821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
2429821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2439821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2449821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
2459821f1d3SAlan Somers 	uint64_t ino = 42;
2469821f1d3SAlan Somers 	/*
2479821f1d3SAlan Somers 	 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
2489821f1d3SAlan Somers 	 * using writeback caching
2499821f1d3SAlan Somers 	 */
2509821f1d3SAlan Somers 	uint64_t initial_offset = m_maxbcachebuf;
2519821f1d3SAlan Somers 	int fd;
2529821f1d3SAlan Somers 
2539821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
2549821f1d3SAlan Somers 	expect_open(ino, 0, 1);
255bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
2569821f1d3SAlan Somers 
2579821f1d3SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
2589821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
2599821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
2609821f1d3SAlan Somers 
2619821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
2627fc0921dSAlan Somers 	leak(fd);
2639821f1d3SAlan Somers }
2649821f1d3SAlan Somers 
265a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */
266a87e0831SAlan Somers TEST_F(Write, append_to_cached)
267a87e0831SAlan Somers {
268a87e0831SAlan Somers 	const ssize_t BUFSIZE = 9;
269a87e0831SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
270a87e0831SAlan Somers 	const char RELPATH[] = "some_file.txt";
271a87e0831SAlan Somers 	char *oldcontents, *oldbuf;
272a87e0831SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
273a87e0831SAlan Somers 	uint64_t ino = 42;
274a87e0831SAlan Somers 	/*
275a87e0831SAlan Somers 	 * Set offset in between maxbcachebuf boundary to test buffer handling
276a87e0831SAlan Somers 	 */
277a87e0831SAlan Somers 	uint64_t oldsize = m_maxbcachebuf / 2;
278a87e0831SAlan Somers 	int fd;
279a87e0831SAlan Somers 
280a87e0831SAlan Somers 	oldcontents = (char*)calloc(1, oldsize);
2815a0b9a27SAlan Somers 	ASSERT_NE(nullptr, oldcontents) << strerror(errno);
282a87e0831SAlan Somers 	oldbuf = (char*)malloc(oldsize);
2835a0b9a27SAlan Somers 	ASSERT_NE(nullptr, oldbuf) << strerror(errno);
284a87e0831SAlan Somers 
285a87e0831SAlan Somers 	expect_lookup(RELPATH, ino, oldsize);
286a87e0831SAlan Somers 	expect_open(ino, 0, 1);
287a87e0831SAlan Somers 	expect_read(ino, 0, oldsize, oldsize, oldcontents);
28884879e46SAlan Somers 	maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
289a87e0831SAlan Somers 
290a87e0831SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
291a87e0831SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
292a87e0831SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
293a87e0831SAlan Somers 
294a87e0831SAlan Somers 	/* Read the old data into the cache */
295a87e0831SAlan Somers 	ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
296a87e0831SAlan Somers 		<< strerror(errno);
297a87e0831SAlan Somers 
298a87e0831SAlan Somers 	/* Write the new data.  There should be no more read operations */
299a87e0831SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3007fc0921dSAlan Somers 	leak(fd);
301a87e0831SAlan Somers }
302a87e0831SAlan Somers 
3039821f1d3SAlan Somers TEST_F(Write, append_direct_io)
3049821f1d3SAlan Somers {
3059821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
3069821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3079821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3089821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
3099821f1d3SAlan Somers 	uint64_t ino = 42;
3109821f1d3SAlan Somers 	uint64_t initial_offset = 4096;
3119821f1d3SAlan Somers 	int fd;
3129821f1d3SAlan Somers 
3139821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
3149821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
315bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
3169821f1d3SAlan Somers 
3179821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY | O_APPEND);
3189821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3199821f1d3SAlan Somers 
3209821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3217fc0921dSAlan Somers 	leak(fd);
3229821f1d3SAlan Somers }
3239821f1d3SAlan Somers 
3249821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */
3256af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache)
3269821f1d3SAlan Somers {
3279821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3289821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3299821f1d3SAlan Somers 	const char CONTENTS0[] = "abcdefgh";
3309821f1d3SAlan Somers 	const char CONTENTS1[] = "ijklmnop";
3319821f1d3SAlan Somers 	uint64_t ino = 42;
3329821f1d3SAlan Somers 	int fd;
3339821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
3349821f1d3SAlan Somers 	char readbuf[bufsize];
3359821f1d3SAlan Somers 
3369821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
3379821f1d3SAlan Somers 	expect_open(ino, 0, 1);
3389821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
339bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
3409821f1d3SAlan Somers 
3419821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
3429821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3439821f1d3SAlan Somers 
3449821f1d3SAlan Somers 	// Prime cache
3459821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3469821f1d3SAlan Somers 
3479821f1d3SAlan Somers 	// Write directly, evicting cache
3489821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
3499821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3509821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
3519821f1d3SAlan Somers 
3529821f1d3SAlan Somers 	// Read again.  Cache should be bypassed
3539821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
3549821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
3559821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3569821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3579821f1d3SAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS1);
3589821f1d3SAlan Somers 
3597fc0921dSAlan Somers 	leak(fd);
3609821f1d3SAlan Somers }
3619821f1d3SAlan Somers 
3629821f1d3SAlan Somers /*
36312292a99SAlan Somers  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
36412292a99SAlan Somers  * allowed to return a short write for that file handle.  However, if it does
36512292a99SAlan Somers  * then we should still do our darndest to handle it by resending the unwritten
36612292a99SAlan Somers  * portion.
3679821f1d3SAlan Somers  */
36812292a99SAlan Somers TEST_F(Write, indirect_io_short_write)
3699821f1d3SAlan Somers {
3709821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3719821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3729821f1d3SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
3739821f1d3SAlan Somers 	uint64_t ino = 42;
3749821f1d3SAlan Somers 	int fd;
3759821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
37612292a99SAlan Somers 	ssize_t bufsize0 = 11;
37712292a99SAlan Somers 	ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
37812292a99SAlan Somers 	const char *contents1 = CONTENTS + bufsize0;
3799821f1d3SAlan Somers 
3809821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
38112292a99SAlan Somers 	expect_open(ino, 0, 1);
382bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
383bda39894SAlan Somers 	expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
3849821f1d3SAlan Somers 
3859821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
3869821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3879821f1d3SAlan Somers 
3889821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
3897fc0921dSAlan Somers 	leak(fd);
3909821f1d3SAlan Somers }
3919821f1d3SAlan Somers 
3929821f1d3SAlan Somers /*
39312292a99SAlan Somers  * When the direct_io option is used, filesystems are allowed to write less
39412292a99SAlan Somers  * data than requested.  We should return the short write to userland.
39512292a99SAlan Somers  */
39612292a99SAlan Somers TEST_F(Write, direct_io_short_write)
39712292a99SAlan Somers {
39812292a99SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
39912292a99SAlan Somers 	const char RELPATH[] = "some_file.txt";
40012292a99SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
40112292a99SAlan Somers 	uint64_t ino = 42;
40212292a99SAlan Somers 	int fd;
40312292a99SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
40412292a99SAlan Somers 	ssize_t halfbufsize = bufsize / 2;
40512292a99SAlan Somers 
40612292a99SAlan Somers 	expect_lookup(RELPATH, ino, 0);
40712292a99SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
408bda39894SAlan Somers 	expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
40912292a99SAlan Somers 
41012292a99SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
41112292a99SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
41212292a99SAlan Somers 
41312292a99SAlan Somers 	ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4147fc0921dSAlan Somers 	leak(fd);
41512292a99SAlan Somers }
41612292a99SAlan Somers 
41712292a99SAlan Somers /*
4189821f1d3SAlan Somers  * An insidious edge case: the filesystem returns a short write, and the
4199821f1d3SAlan Somers  * difference between what we requested and what it actually wrote crosses an
4209821f1d3SAlan Somers  * iov element boundary
4219821f1d3SAlan Somers  */
42212292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov)
4239821f1d3SAlan Somers {
4249821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4259821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4269821f1d3SAlan Somers 	const char *CONTENTS0 = "abcdefgh";
4279821f1d3SAlan Somers 	const char *CONTENTS1 = "ijklmnop";
4289821f1d3SAlan Somers 	const char *EXPECTED0 = "abcdefghijklmnop";
4299821f1d3SAlan Somers 	uint64_t ino = 42;
4309821f1d3SAlan Somers 	int fd;
4319821f1d3SAlan Somers 	ssize_t size0 = strlen(CONTENTS0) - 1;
4329821f1d3SAlan Somers 	ssize_t size1 = strlen(CONTENTS1) + 1;
4339821f1d3SAlan Somers 	ssize_t totalsize = size0 + size1;
4349821f1d3SAlan Somers 	struct iovec iov[2];
4359821f1d3SAlan Somers 
4369821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
4379821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
438bda39894SAlan Somers 	expect_write(ino, 0, totalsize, size0, EXPECTED0);
4399821f1d3SAlan Somers 
4409821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
4419821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
4429821f1d3SAlan Somers 
4435a0b9a27SAlan Somers 	iov[0].iov_base = __DECONST(void*, CONTENTS0);
4449821f1d3SAlan Somers 	iov[0].iov_len = strlen(CONTENTS0);
4455a0b9a27SAlan Somers 	iov[1].iov_base = __DECONST(void*, CONTENTS1);
4469821f1d3SAlan Somers 	iov[1].iov_len = strlen(CONTENTS1);
44712292a99SAlan Somers 	ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
4487fc0921dSAlan Somers 	leak(fd);
4499821f1d3SAlan Somers }
4509821f1d3SAlan Somers 
451a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */
452a639731bSAlan Somers TEST_F(Write, rlimit_fsize)
453a639731bSAlan Somers {
454a639731bSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
455a639731bSAlan Somers 	const char RELPATH[] = "some_file.txt";
456a639731bSAlan Somers 	const char *CONTENTS = "abcdefgh";
457a639731bSAlan Somers 	struct rlimit rl;
458a639731bSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
459a639731bSAlan Somers 	off_t offset = 1'000'000'000;
460a639731bSAlan Somers 	uint64_t ino = 42;
461a639731bSAlan Somers 	int fd;
462a639731bSAlan Somers 
463a639731bSAlan Somers 	expect_lookup(RELPATH, ino, 0);
464a639731bSAlan Somers 	expect_open(ino, 0, 1);
465a639731bSAlan Somers 
466a639731bSAlan Somers 	rl.rlim_cur = offset;
467a639731bSAlan Somers 	rl.rlim_max = 10 * offset;
468a639731bSAlan Somers 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
469a639731bSAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
470a639731bSAlan Somers 
471a639731bSAlan Somers 	fd = open(FULLPATH, O_WRONLY);
472a639731bSAlan Somers 
473a639731bSAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
474a639731bSAlan Somers 
475a639731bSAlan Somers 	ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
476a639731bSAlan Somers 	EXPECT_EQ(EFBIG, errno);
477a639731bSAlan Somers 	EXPECT_EQ(1, s_sigxfsz);
4787fc0921dSAlan Somers 	leak(fd);
479a639731bSAlan Somers }
480a639731bSAlan Somers 
4819821f1d3SAlan Somers /*
482b9e20197SAlan Somers  * A short read indicates EOF.  Test that nothing bad happens if we get EOF
483b9e20197SAlan Somers  * during the R of a RMW operation.
484b9e20197SAlan Somers  */
485f8ebf1cdSAlan Somers TEST_F(Write, eof_during_rmw)
486b9e20197SAlan Somers {
487b9e20197SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
488b9e20197SAlan Somers 	const char RELPATH[] = "some_file.txt";
489b9e20197SAlan Somers 	const char *CONTENTS = "abcdefgh";
490b9e20197SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
491b9e20197SAlan Somers 	uint64_t ino = 42;
492b9e20197SAlan Somers 	uint64_t offset = 1;
493b9e20197SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
494b9e20197SAlan Somers 	off_t orig_fsize = 10;
495b9e20197SAlan Somers 	off_t truncated_fsize = 5;
496b9e20197SAlan Somers 	off_t final_fsize = bufsize;
497b9e20197SAlan Somers 	int fd;
498b9e20197SAlan Somers 
499b9e20197SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
500b9e20197SAlan Somers 	expect_open(ino, 0, 1);
501b9e20197SAlan Somers 	expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
502b9e20197SAlan Somers 	expect_getattr(ino, truncated_fsize);
503b9e20197SAlan Somers 	expect_read(ino, 0, final_fsize, final_fsize, INITIAL, O_RDWR);
504b9e20197SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
505b9e20197SAlan Somers 
506b9e20197SAlan Somers 	fd = open(FULLPATH, O_RDWR);
507b9e20197SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
508b9e20197SAlan Somers 
509b9e20197SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
510b9e20197SAlan Somers 		<< strerror(errno);
5117fc0921dSAlan Somers 	leak(fd);
512b9e20197SAlan Somers }
513b9e20197SAlan Somers 
514b9e20197SAlan Somers /*
5159821f1d3SAlan Somers  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
5169821f1d3SAlan Somers  * write, then it must set the FUSE_WRITE_CACHE bit
5179821f1d3SAlan Somers  */
5189821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
519f8ebf1cdSAlan Somers TEST_F(Write, mmap)
5209821f1d3SAlan Somers {
5219821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5229821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5239821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
5249821f1d3SAlan Somers 	uint64_t ino = 42;
5259821f1d3SAlan Somers 	int fd;
5269821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5279821f1d3SAlan Somers 	void *p;
5289821f1d3SAlan Somers 	uint64_t offset = 10;
5299821f1d3SAlan Somers 	size_t len;
5309821f1d3SAlan Somers 	void *zeros, *expected;
5319821f1d3SAlan Somers 
5329821f1d3SAlan Somers 	len = getpagesize();
5339821f1d3SAlan Somers 
5349821f1d3SAlan Somers 	zeros = calloc(1, len);
5355a0b9a27SAlan Somers 	ASSERT_NE(nullptr, zeros);
5369821f1d3SAlan Somers 	expected = calloc(1, len);
5375a0b9a27SAlan Somers 	ASSERT_NE(nullptr, expected);
5389821f1d3SAlan Somers 	memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
5399821f1d3SAlan Somers 
5409821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, len);
5419821f1d3SAlan Somers 	expect_open(ino, 0, 1);
5429821f1d3SAlan Somers 	expect_read(ino, 0, len, len, zeros);
5439821f1d3SAlan Somers 	/*
5449821f1d3SAlan Somers 	 * Writes from the pager may or may not be associated with the correct
545cf437e2aSAlan Somers 	 * pid, so they must set FUSE_WRITE_CACHE.
5469821f1d3SAlan Somers 	 */
547bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
5489f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
5499821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
5509821f1d3SAlan Somers 
5519821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
5529821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
5539821f1d3SAlan Somers 
5549821f1d3SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
5559821f1d3SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
5569821f1d3SAlan Somers 
5579821f1d3SAlan Somers 	memmove((uint8_t*)p + offset, CONTENTS, bufsize);
5589821f1d3SAlan Somers 
5599821f1d3SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
5609821f1d3SAlan Somers 	close(fd);	// Write mmap'd data on close
5619821f1d3SAlan Somers 
5629821f1d3SAlan Somers 	free(expected);
5639821f1d3SAlan Somers 	free(zeros);
5649821f1d3SAlan Somers }
5659821f1d3SAlan Somers 
566f8ebf1cdSAlan Somers TEST_F(Write, pwrite)
5679821f1d3SAlan Somers {
5689821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5699821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5709821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
5719821f1d3SAlan Somers 	uint64_t ino = 42;
5726ca3b02bSAlan Somers 	uint64_t offset = m_maxbcachebuf;
5739821f1d3SAlan Somers 	int fd;
5749821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5759821f1d3SAlan Somers 
5769821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
5779821f1d3SAlan Somers 	expect_open(ino, 0, 1);
578bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
5799821f1d3SAlan Somers 
5809821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
5819821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
5829821f1d3SAlan Somers 
5839821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
5849821f1d3SAlan Somers 		<< strerror(errno);
5857fc0921dSAlan Somers 	leak(fd);
5869821f1d3SAlan Somers }
5879821f1d3SAlan Somers 
588788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */
589788af953SAlan Somers TEST_F(Write, timestamps)
590788af953SAlan Somers {
591788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
592788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
593788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
594788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
595788af953SAlan Somers 	uint64_t ino = 42;
596788af953SAlan Somers 	struct stat sb0, sb1;
597788af953SAlan Somers 	int fd;
598788af953SAlan Somers 
599788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
600788af953SAlan Somers 	expect_open(ino, 0, 1);
601788af953SAlan Somers 	maybe_expect_write(ino, 0, bufsize, CONTENTS);
602788af953SAlan Somers 
603788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
604788af953SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
605788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
606788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
607788af953SAlan Somers 
608788af953SAlan Somers 	nap();
609788af953SAlan Somers 
610788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno);
611788af953SAlan Somers 
612788af953SAlan Somers 	EXPECT_EQ(sb0.st_atime, sb1.st_atime);
613788af953SAlan Somers 	EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
614788af953SAlan Somers 	EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
615788af953SAlan Somers }
616788af953SAlan Somers 
6179821f1d3SAlan Somers TEST_F(Write, write)
6189821f1d3SAlan Somers {
6199821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6209821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6219821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6229821f1d3SAlan Somers 	uint64_t ino = 42;
6239821f1d3SAlan Somers 	int fd;
6249821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6259821f1d3SAlan Somers 
6269821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6279821f1d3SAlan Somers 	expect_open(ino, 0, 1);
628bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
6299821f1d3SAlan Somers 
6309821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
6319821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6329821f1d3SAlan Somers 
6339821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6347fc0921dSAlan Somers 	leak(fd);
6359821f1d3SAlan Somers }
6369821f1d3SAlan Somers 
6379821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */
6389821f1d3SAlan Somers TEST_F(Write, write_large)
6399821f1d3SAlan Somers {
6409821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6419821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6429821f1d3SAlan Somers 	int *contents;
6439821f1d3SAlan Somers 	uint64_t ino = 42;
6449821f1d3SAlan Somers 	int fd;
6459821f1d3SAlan Somers 	ssize_t halfbufsize, bufsize;
6469821f1d3SAlan Somers 
6478eecd9ceSAlan Somers 	halfbufsize = m_mock->m_maxwrite;
6489821f1d3SAlan Somers 	bufsize = halfbufsize * 2;
6499821f1d3SAlan Somers 	contents = (int*)malloc(bufsize);
6505a0b9a27SAlan Somers 	ASSERT_NE(nullptr, contents);
6519821f1d3SAlan Somers 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
6529821f1d3SAlan Somers 		contents[i] = i;
6539821f1d3SAlan Somers 	}
6549821f1d3SAlan Somers 
6559821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6569821f1d3SAlan Somers 	expect_open(ino, 0, 1);
657f2704f05SAlan Somers 	maybe_expect_write(ino, 0, halfbufsize, contents);
65884879e46SAlan Somers 	maybe_expect_write(ino, halfbufsize, halfbufsize,
6599821f1d3SAlan Somers 		&contents[halfbufsize / sizeof(int)]);
6609821f1d3SAlan Somers 
6619821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
6629821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6639821f1d3SAlan Somers 
6649821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
6657fc0921dSAlan Somers 	leak(fd);
6669821f1d3SAlan Somers 
6679821f1d3SAlan Somers 	free(contents);
6689821f1d3SAlan Somers }
6699821f1d3SAlan Somers 
6709821f1d3SAlan Somers TEST_F(Write, write_nothing)
6719821f1d3SAlan Somers {
6729821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6739821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6749821f1d3SAlan Somers 	const char *CONTENTS = "";
6759821f1d3SAlan Somers 	uint64_t ino = 42;
6769821f1d3SAlan Somers 	int fd;
6779821f1d3SAlan Somers 	ssize_t bufsize = 0;
6789821f1d3SAlan Somers 
6799821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6809821f1d3SAlan Somers 	expect_open(ino, 0, 1);
6819821f1d3SAlan Somers 
6829821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
6839821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6849821f1d3SAlan Somers 
6859821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6867fc0921dSAlan Somers 	leak(fd);
6879821f1d3SAlan Somers }
6889821f1d3SAlan Somers 
68916bd2d47SAlan Somers TEST_F(Write_7_8, write)
69016bd2d47SAlan Somers {
69116bd2d47SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
69216bd2d47SAlan Somers 	const char RELPATH[] = "some_file.txt";
69316bd2d47SAlan Somers 	const char *CONTENTS = "abcdefgh";
69416bd2d47SAlan Somers 	uint64_t ino = 42;
69516bd2d47SAlan Somers 	int fd;
69616bd2d47SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
69716bd2d47SAlan Somers 
69816bd2d47SAlan Somers 	expect_lookup(RELPATH, ino, 0);
69916bd2d47SAlan Somers 	expect_open(ino, 0, 1);
700bda39894SAlan Somers 	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
70116bd2d47SAlan Somers 
70216bd2d47SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
70316bd2d47SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
70416bd2d47SAlan Somers 
70516bd2d47SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7067fc0921dSAlan Somers 	leak(fd);
70716bd2d47SAlan Somers }
70816bd2d47SAlan Somers 
7099821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */
71084879e46SAlan Somers TEST_F(WriteBackAsync, close)
7119821f1d3SAlan Somers {
7129821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7139821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7149821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7159821f1d3SAlan Somers 	uint64_t ino = 42;
7169821f1d3SAlan Somers 	int fd;
7179821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7189821f1d3SAlan Somers 
7199821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7209821f1d3SAlan Somers 	expect_open(ino, 0, 1);
721bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
7229821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
7239821f1d3SAlan Somers 		ResultOf([=](auto in) {
72429edc611SAlan Somers 			return (in.header.opcode == FUSE_SETATTR);
7259821f1d3SAlan Somers 		}, Eq(true)),
7269821f1d3SAlan Somers 		_)
72729edc611SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
7289821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
72929edc611SAlan Somers 		out.body.attr.attr.ino = ino;	// Must match nodeid
7309821f1d3SAlan Somers 	})));
7319f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
7329821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
7339821f1d3SAlan Somers 
7349821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
7359821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7369821f1d3SAlan Somers 
7379821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7389821f1d3SAlan Somers 	close(fd);
7399821f1d3SAlan Somers }
7409821f1d3SAlan Somers 
7418eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */
7428eecd9ceSAlan Somers TEST_F(WriteCluster, clustering)
7438eecd9ceSAlan Somers {
7448eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7458eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
7468eecd9ceSAlan Somers 	uint64_t ino = 42;
7478eecd9ceSAlan Somers 	int i, fd;
7488eecd9ceSAlan Somers 	void *wbuf, *wbuf2x;
7496ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
7506ca3b02bSAlan Somers 	off_t filesize = 5 * bufsize;
7518eecd9ceSAlan Somers 
7528eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
7535a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf) << strerror(errno);
7548eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
7558eecd9ceSAlan Somers 	wbuf2x = malloc(2 * bufsize);
7565a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf2x) << strerror(errno);
7578eecd9ceSAlan Somers 	memset(wbuf2x, 'X', 2 * bufsize);
7588eecd9ceSAlan Somers 
7598eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
7608eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
7618eecd9ceSAlan Somers 	/*
7628eecd9ceSAlan Somers 	 * Writes of bufsize-bytes each should be clustered into greater sizes.
7638eecd9ceSAlan Somers 	 * The amount of clustering is adaptive, so the first write actually
7648eecd9ceSAlan Somers 	 * issued will be 2x bufsize and subsequent writes may be larger
7658eecd9ceSAlan Somers 	 */
7668eecd9ceSAlan Somers 	expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
7678eecd9ceSAlan Somers 	expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
7688eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
7698eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
7708eecd9ceSAlan Somers 
7718eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
7728eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7738eecd9ceSAlan Somers 
7748eecd9ceSAlan Somers 	for (i = 0; i < 4; i++) {
7758eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
7768eecd9ceSAlan Somers 			<< strerror(errno);
7778eecd9ceSAlan Somers 	}
7788eecd9ceSAlan Somers 	close(fd);
7798eecd9ceSAlan Somers }
7808eecd9ceSAlan Somers 
7818eecd9ceSAlan Somers /*
7828eecd9ceSAlan Somers  * When clustering writes, an I/O error to any of the cluster's children should
7838eecd9ceSAlan Somers  * not panic the system on unmount
7848eecd9ceSAlan Somers  */
7858eecd9ceSAlan Somers /*
7868eecd9ceSAlan Somers  * Disabled because it panics.
7878eecd9ceSAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
7888eecd9ceSAlan Somers  */
7898eecd9ceSAlan Somers TEST_F(WriteCluster, DISABLED_cluster_write_err)
7908eecd9ceSAlan Somers {
7918eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7928eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
7938eecd9ceSAlan Somers 	uint64_t ino = 42;
7948eecd9ceSAlan Somers 	int i, fd;
7958eecd9ceSAlan Somers 	void *wbuf;
7966ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
7976ca3b02bSAlan Somers 	off_t filesize = 4 * bufsize;
7988eecd9ceSAlan Somers 
7998eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
8005a0b9a27SAlan Somers 	ASSERT_NE(nullptr, wbuf) << strerror(errno);
8018eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
8028eecd9ceSAlan Somers 
8038eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
8048eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
8058eecd9ceSAlan Somers 	EXPECT_CALL(*m_mock, process(
8068eecd9ceSAlan Somers 		ResultOf([=](auto in) {
8078eecd9ceSAlan Somers 			return (in.header.opcode == FUSE_WRITE);
8088eecd9ceSAlan Somers 		}, Eq(true)),
8098eecd9ceSAlan Somers 		_)
8108eecd9ceSAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
8118eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
8128eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
8138eecd9ceSAlan Somers 
8148eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
8158eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8168eecd9ceSAlan Somers 
8178eecd9ceSAlan Somers 	for (i = 0; i < 3; i++) {
8188eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
8198eecd9ceSAlan Somers 			<< strerror(errno);
8208eecd9ceSAlan Somers 	}
8218eecd9ceSAlan Somers 	close(fd);
8228eecd9ceSAlan Somers }
8238eecd9ceSAlan Somers 
8249821f1d3SAlan Somers /*
8255fccbf31SAlan Somers  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
8265fccbf31SAlan Somers  * server.  The FUSE protocol explicitly allows that.
8275fccbf31SAlan Somers  */
8285fccbf31SAlan Somers TEST_F(WriteBack, rmw)
8295fccbf31SAlan Somers {
8305fccbf31SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8315fccbf31SAlan Somers 	const char RELPATH[] = "some_file.txt";
8325fccbf31SAlan Somers 	const char *CONTENTS = "abcdefgh";
8335fccbf31SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
8345fccbf31SAlan Somers 	uint64_t ino = 42;
8355fccbf31SAlan Somers 	uint64_t offset = 1;
8365fccbf31SAlan Somers 	off_t fsize = 10;
8375fccbf31SAlan Somers 	int fd;
8385fccbf31SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8395fccbf31SAlan Somers 
840cad67791SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
8415fccbf31SAlan Somers 	expect_open(ino, 0, 1);
842d4fd0c81SAlan Somers 	expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
84384879e46SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
8445fccbf31SAlan Somers 
8455fccbf31SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
8465fccbf31SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
8475fccbf31SAlan Somers 
8485fccbf31SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
8495fccbf31SAlan Somers 		<< strerror(errno);
8507fc0921dSAlan Somers 	leak(fd);
8515fccbf31SAlan Somers }
8525fccbf31SAlan Somers 
8535fccbf31SAlan Somers /*
8549821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
8559821f1d3SAlan Somers  */
85684879e46SAlan Somers TEST_F(WriteBack, cache)
8579821f1d3SAlan Somers {
8589821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8599821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8609821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
8619821f1d3SAlan Somers 	uint64_t ino = 42;
8629821f1d3SAlan Somers 	int fd;
8639821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8649821f1d3SAlan Somers 	char readbuf[bufsize];
8659821f1d3SAlan Somers 
8669821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
8679821f1d3SAlan Somers 	expect_open(ino, 0, 1);
868bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
8699821f1d3SAlan Somers 
8709821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
8719821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
8729821f1d3SAlan Somers 
8739821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
8749821f1d3SAlan Somers 	/*
8759821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
8769821f1d3SAlan Somers 	 * filesystem daemon
8779821f1d3SAlan Somers 	 */
8789821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
8799821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
8807fc0921dSAlan Somers 	leak(fd);
8819821f1d3SAlan Somers }
8829821f1d3SAlan Somers 
8839821f1d3SAlan Somers /*
8849821f1d3SAlan Somers  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
8859821f1d3SAlan Somers  * an odd test, because it would be unusual to use O_DIRECT for writes but not
8869821f1d3SAlan Somers  * reads.
8879821f1d3SAlan Somers  */
8889821f1d3SAlan Somers TEST_F(WriteBack, o_direct)
8899821f1d3SAlan Somers {
8909821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8919821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8929821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
8939821f1d3SAlan Somers 	uint64_t ino = 42;
8949821f1d3SAlan Somers 	int fd;
8959821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8969821f1d3SAlan Somers 	char readbuf[bufsize];
8979821f1d3SAlan Somers 
8989821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
8999821f1d3SAlan Somers 	expect_open(ino, 0, 1);
900bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
901bda39894SAlan Somers 		CONTENTS);
9029821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
9039821f1d3SAlan Somers 
9049821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
9059821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
9069821f1d3SAlan Somers 
9079821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9089821f1d3SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
9099821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
9109821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
9119821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
9127fc0921dSAlan Somers 	leak(fd);
9139821f1d3SAlan Somers }
9149821f1d3SAlan Somers 
9159821f1d3SAlan Somers /*
91684879e46SAlan Somers  * When mounted with -o async, the writeback cache mode should delay writes
91784879e46SAlan Somers  */
91884879e46SAlan Somers TEST_F(WriteBackAsync, delay)
91984879e46SAlan Somers {
92084879e46SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
92184879e46SAlan Somers 	const char RELPATH[] = "some_file.txt";
92284879e46SAlan Somers 	const char *CONTENTS = "abcdefgh";
92384879e46SAlan Somers 	uint64_t ino = 42;
92484879e46SAlan Somers 	int fd;
92584879e46SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
92684879e46SAlan Somers 
92784879e46SAlan Somers 	expect_lookup(RELPATH, ino, 0);
92884879e46SAlan Somers 	expect_open(ino, 0, 1);
92984879e46SAlan Somers 	/* Write should be cached, but FUSE_WRITE shouldn't be sent */
93084879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
93184879e46SAlan Somers 		ResultOf([=](auto in) {
93284879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE);
93384879e46SAlan Somers 		}, Eq(true)),
93484879e46SAlan Somers 		_)
93584879e46SAlan Somers 	).Times(0);
93684879e46SAlan Somers 
93784879e46SAlan Somers 	fd = open(FULLPATH, O_RDWR);
93884879e46SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
93984879e46SAlan Somers 
94084879e46SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
94184879e46SAlan Somers 
94284879e46SAlan Somers 	/* Don't close the file because that would flush the cache */
94384879e46SAlan Somers }
94484879e46SAlan Somers 
94584879e46SAlan Somers /*
946669a092aSAlan Somers  * A direct write should not evict dirty cached data from outside of its own
947669a092aSAlan Somers  * byte range.
948669a092aSAlan Somers  */
949669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached)
950669a092aSAlan Somers {
951669a092aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
952669a092aSAlan Somers 	const char RELPATH[] = "some_file.txt";
953669a092aSAlan Somers 	const char CONTENTS0[] = "abcdefgh";
954669a092aSAlan Somers 	const char CONTENTS1[] = "ijklmnop";
955669a092aSAlan Somers 	uint64_t ino = 42;
956669a092aSAlan Somers 	int fd;
957669a092aSAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
958669a092aSAlan Somers 	ssize_t fsize = 2 * m_maxbcachebuf;
959669a092aSAlan Somers 	char readbuf[bufsize];
960669a092aSAlan Somers 	void *zeros;
961669a092aSAlan Somers 
962669a092aSAlan Somers 	zeros = calloc(1, m_maxbcachebuf);
963669a092aSAlan Somers 	ASSERT_NE(nullptr, zeros);
964669a092aSAlan Somers 
965669a092aSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
966669a092aSAlan Somers 	expect_open(ino, 0, 1);
967669a092aSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros);
968669a092aSAlan Somers 	FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0,
969669a092aSAlan Somers 		CONTENTS1);
970669a092aSAlan Somers 
971669a092aSAlan Somers 	fd = open(FULLPATH, O_RDWR);
972669a092aSAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
973669a092aSAlan Somers 
974669a092aSAlan Somers 	// Cache first block with dirty data.  This will entail first reading
975669a092aSAlan Somers 	// the existing data.
976669a092aSAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0))
977669a092aSAlan Somers 		<< strerror(errno);
978669a092aSAlan Somers 
979669a092aSAlan Somers 	// Write directly to second block
980669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
981669a092aSAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf))
982669a092aSAlan Somers 		<< strerror(errno);
983669a092aSAlan Somers 
984669a092aSAlan Somers 	// Read from the first block again.  Should be serviced by cache.
985669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
986669a092aSAlan Somers 	ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno);
987669a092aSAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS0);
988669a092aSAlan Somers 
989669a092aSAlan Somers 	leak(fd);
990669a092aSAlan Somers 	free(zeros);
991669a092aSAlan Somers }
992669a092aSAlan Somers 
993669a092aSAlan Somers /*
994669a092aSAlan Somers  * If a direct io write partially overlaps one or two blocks of dirty cached
995669a092aSAlan Somers  * data, No dirty data should be lost.  Admittedly this is a weird test,
996669a092aSAlan Somers  * because it would be unusual to use O_DIRECT and the writeback cache.
997669a092aSAlan Somers  */
998669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block)
999669a092aSAlan Somers {
1000669a092aSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1001669a092aSAlan Somers 	const char RELPATH[] = "some_file.txt";
1002669a092aSAlan Somers 	uint64_t ino = 42;
1003669a092aSAlan Somers 	int fd;
1004669a092aSAlan Somers 	off_t bs = m_maxbcachebuf;
1005669a092aSAlan Somers 	ssize_t fsize = 3 * bs;
1006669a092aSAlan Somers 	void *readbuf, *zeros, *ones, *zeroones, *onezeros;
1007669a092aSAlan Somers 
1008669a092aSAlan Somers 	readbuf = malloc(bs);
1009669a092aSAlan Somers 	ASSERT_NE(nullptr, readbuf) << strerror(errno);
1010669a092aSAlan Somers 	zeros = calloc(1, 3 * bs);
1011669a092aSAlan Somers 	ASSERT_NE(nullptr, zeros);
1012669a092aSAlan Somers 	ones = calloc(1, 2 * bs);
1013669a092aSAlan Somers 	ASSERT_NE(nullptr, ones);
1014669a092aSAlan Somers 	memset(ones, 1, 2 * bs);
1015669a092aSAlan Somers 	zeroones = calloc(1, bs);
1016669a092aSAlan Somers 	ASSERT_NE(nullptr, zeroones);
1017669a092aSAlan Somers 	memset((uint8_t*)zeroones + bs / 2, 1, bs / 2);
1018669a092aSAlan Somers 	onezeros = calloc(1, bs);
1019669a092aSAlan Somers 	ASSERT_NE(nullptr, onezeros);
1020669a092aSAlan Somers 	memset(onezeros, 1, bs / 2);
1021669a092aSAlan Somers 
1022669a092aSAlan Somers 	expect_lookup(RELPATH, ino, fsize);
1023669a092aSAlan Somers 	expect_open(ino, 0, 1);
1024669a092aSAlan Somers 
1025669a092aSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1026669a092aSAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1027669a092aSAlan Somers 
1028669a092aSAlan Somers 	/* Cache first and third blocks with dirty data.  */
1029669a092aSAlan Somers 	ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno);
1030669a092aSAlan Somers 
1031669a092aSAlan Somers 	/*
1032669a092aSAlan Somers 	 * Write directly to all three blocks.  The partially written blocks
1033669a092aSAlan Somers 	 * will be flushed because they're dirty.
1034669a092aSAlan Somers 	 */
1035669a092aSAlan Somers 	FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros);
1036669a092aSAlan Somers 	FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros);
1037669a092aSAlan Somers 	/* The direct write is split in two because of the m_maxwrite value */
1038669a092aSAlan Somers 	FuseTest::expect_write(ino,     bs / 2, bs, bs, 0, 0, ones);
1039669a092aSAlan Somers 	FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones);
1040669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1041669a092aSAlan Somers 	ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno);
1042669a092aSAlan Somers 
1043669a092aSAlan Somers 	/*
1044669a092aSAlan Somers 	 * Read from both the valid and invalid portions of the first and third
1045669a092aSAlan Somers 	 * blocks again.  This will entail FUSE_READ operations because these
1046669a092aSAlan Somers 	 * blocks were invalidated by the direct write.
1047669a092aSAlan Somers 	 */
1048669a092aSAlan Somers 	expect_read(ino, 0, bs, bs, zeroones);
1049669a092aSAlan Somers 	expect_read(ino, 2 * bs, bs, bs, onezeros);
1050669a092aSAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1051669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno);
1052669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1053669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2))
1054669a092aSAlan Somers 		<< strerror(errno);
1055669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1056669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2))
1057669a092aSAlan Somers 		<< strerror(errno);
1058669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1059669a092aSAlan Somers 	ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs))
1060669a092aSAlan Somers 		<< strerror(errno);
1061669a092aSAlan Somers 	EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1062669a092aSAlan Somers 
1063669a092aSAlan Somers 	leak(fd);
1064669a092aSAlan Somers 	free(zeroones);
1065669a092aSAlan Somers 	free(onezeros);
1066669a092aSAlan Somers 	free(ones);
1067669a092aSAlan Somers 	free(zeros);
1068669a092aSAlan Somers 	free(readbuf);
1069669a092aSAlan Somers }
1070669a092aSAlan Somers 
1071669a092aSAlan Somers /*
1072aef22f2dSAlan Somers  * In WriteBack mode, writes may be cached beyond what the server thinks is the
1073aef22f2dSAlan Somers  * EOF.  In this case, a short read at EOF should _not_ cause fusefs to update
1074aef22f2dSAlan Somers  * the file's size.
1075aef22f2dSAlan Somers  */
1076aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof)
1077aef22f2dSAlan Somers {
1078aef22f2dSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1079aef22f2dSAlan Somers 	const char RELPATH[] = "some_file.txt";
1080aef22f2dSAlan Somers 	const char *CONTENTS0 = "abcdefgh";
1081aef22f2dSAlan Somers 	const char *CONTENTS1 = "ijklmnop";
1082aef22f2dSAlan Somers 	uint64_t ino = 42;
1083aef22f2dSAlan Somers 	int fd;
1084aef22f2dSAlan Somers 	off_t offset = m_maxbcachebuf;
1085aef22f2dSAlan Somers 	ssize_t wbufsize = strlen(CONTENTS1);
1086aef22f2dSAlan Somers 	off_t old_filesize = (off_t)strlen(CONTENTS0);
1087aef22f2dSAlan Somers 	ssize_t rbufsize = 2 * old_filesize;
1088aef22f2dSAlan Somers 	char readbuf[rbufsize];
1089aef22f2dSAlan Somers 	size_t holesize = rbufsize - old_filesize;
1090aef22f2dSAlan Somers 	char hole[holesize];
1091aef22f2dSAlan Somers 	struct stat sb;
1092aef22f2dSAlan Somers 	ssize_t r;
1093aef22f2dSAlan Somers 
1094aef22f2dSAlan Somers 	expect_lookup(RELPATH, ino, 0);
1095aef22f2dSAlan Somers 	expect_open(ino, 0, 1);
1096aef22f2dSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
1097aef22f2dSAlan Somers 
1098aef22f2dSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1099aef22f2dSAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1100aef22f2dSAlan Somers 
1101aef22f2dSAlan Somers 	/* Write and cache data beyond EOF */
1102aef22f2dSAlan Somers 	ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
1103aef22f2dSAlan Somers 		<< strerror(errno);
1104aef22f2dSAlan Somers 
1105aef22f2dSAlan Somers 	/* Read from the old EOF */
1106aef22f2dSAlan Somers 	r = pread(fd, readbuf, rbufsize, 0);
1107aef22f2dSAlan Somers 	ASSERT_LE(0, r) << strerror(errno);
1108aef22f2dSAlan Somers 	EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
1109aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
1110aef22f2dSAlan Somers 	bzero(hole, holesize);
1111aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
1112aef22f2dSAlan Somers 
1113aef22f2dSAlan Somers 	/* The file's size should still be what was established by pwrite */
1114aef22f2dSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1115aef22f2dSAlan Somers 	EXPECT_EQ(offset + wbufsize, sb.st_size);
11167fc0921dSAlan Somers 	leak(fd);
1117aef22f2dSAlan Somers }
1118aef22f2dSAlan Somers 
1119aef22f2dSAlan Somers /*
1120788af953SAlan Somers  * When a file has dirty writes that haven't been flushed, the server's notion
1121788af953SAlan Somers  * of its mtime and ctime will be wrong.  The kernel should ignore those if it
1122788af953SAlan Somers  * gets them from a FUSE_GETATTR before flushing.
1123788af953SAlan Somers  */
1124788af953SAlan Somers TEST_F(WriteBackAsync, timestamps)
1125788af953SAlan Somers {
1126788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1127788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1128788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1129788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1130788af953SAlan Somers 	uint64_t ino = 42;
1131788af953SAlan Somers 	uint64_t attr_valid = 0;
1132788af953SAlan Somers 	uint64_t attr_valid_nsec = 0;
1133788af953SAlan Somers 	uint64_t server_time = 12345;
1134788af953SAlan Somers 	mode_t mode = S_IFREG | 0644;
1135788af953SAlan Somers 	int fd;
1136788af953SAlan Somers 
1137788af953SAlan Somers 	struct stat sb;
1138788af953SAlan Somers 
1139788af953SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
1140788af953SAlan Somers 	.WillRepeatedly(Invoke(
1141788af953SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
1142788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
1143788af953SAlan Somers 		out.body.entry.attr.mode = mode;
1144788af953SAlan Somers 		out.body.entry.nodeid = ino;
1145788af953SAlan Somers 		out.body.entry.attr.nlink = 1;
1146788af953SAlan Somers 		out.body.entry.attr_valid = attr_valid;
1147788af953SAlan Somers 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
1148788af953SAlan Somers 	})));
1149788af953SAlan Somers 	expect_open(ino, 0, 1);
1150788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1151788af953SAlan Somers 		ResultOf([=](auto in) {
1152788af953SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
1153788af953SAlan Somers 				in.header.nodeid == ino);
1154788af953SAlan Somers 		}, Eq(true)),
1155788af953SAlan Somers 		_)
1156788af953SAlan Somers 	).WillRepeatedly(Invoke(
1157788af953SAlan Somers 	ReturnImmediate([=](auto i __unused, auto& out) {
1158788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1159788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1160788af953SAlan Somers 		out.body.attr.attr.mode = mode;
1161788af953SAlan Somers 		out.body.attr.attr_valid = attr_valid;
1162788af953SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
1163788af953SAlan Somers 		out.body.attr.attr.atime = server_time;
1164788af953SAlan Somers 		out.body.attr.attr.mtime = server_time;
1165788af953SAlan Somers 		out.body.attr.attr.ctime = server_time;
1166788af953SAlan Somers 	})));
1167788af953SAlan Somers 
1168788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1169788af953SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1170788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1171788af953SAlan Somers 
1172788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1173788af953SAlan Somers 	EXPECT_EQ((time_t)server_time, sb.st_atime);
1174788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_mtime);
1175788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_ctime);
1176788af953SAlan Somers }
1177788af953SAlan Somers 
1178788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */
1179788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr)
1180788af953SAlan Somers {
1181788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1182788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1183788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1184788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1185788af953SAlan Somers 	uint64_t ino = 42;
1186788af953SAlan Somers 	const mode_t newmode = 0755;
1187788af953SAlan Somers 	int fd;
1188788af953SAlan Somers 
1189788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1190788af953SAlan Somers 	expect_open(ino, 0, 1);
1191788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1192788af953SAlan Somers 		ResultOf([=](auto in) {
11930a8fe2d3SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1194788af953SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1195788af953SAlan Somers 				in.header.nodeid == ino &&
1196788af953SAlan Somers 				in.body.setattr.valid == valid);
1197788af953SAlan Somers 		}, Eq(true)),
1198788af953SAlan Somers 		_)
1199788af953SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1200788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1201788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1202788af953SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1203788af953SAlan Somers 	})));
1204788af953SAlan Somers 
1205788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1206788af953SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1207788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1208788af953SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1209788af953SAlan Somers }
1210788af953SAlan Somers 
1211fef46454SAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */
1212fef46454SAlan Somers TEST_P(TimeGran, timestamps_during_setattr)
1213fef46454SAlan Somers {
1214fef46454SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1215fef46454SAlan Somers 	const char RELPATH[] = "some_file.txt";
1216fef46454SAlan Somers 	const char *CONTENTS = "abcdefgh";
1217fef46454SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1218fef46454SAlan Somers 	uint64_t ino = 42;
1219fef46454SAlan Somers 	const mode_t newmode = 0755;
1220fef46454SAlan Somers 	int fd;
1221fef46454SAlan Somers 
1222fef46454SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1223fef46454SAlan Somers 	expect_open(ino, 0, 1);
1224fef46454SAlan Somers 	EXPECT_CALL(*m_mock, process(
1225fef46454SAlan Somers 		ResultOf([=](auto in) {
1226fef46454SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1227fef46454SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1228fef46454SAlan Somers 				in.header.nodeid == ino &&
1229fef46454SAlan Somers 				in.body.setattr.valid == valid &&
1230fef46454SAlan Somers 				in.body.setattr.mtimensec % m_time_gran == 0 &&
1231fef46454SAlan Somers 				in.body.setattr.ctimensec % m_time_gran == 0);
1232fef46454SAlan Somers 		}, Eq(true)),
1233fef46454SAlan Somers 		_)
1234fef46454SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1235fef46454SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1236fef46454SAlan Somers 		out.body.attr.attr.ino = ino;
1237fef46454SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1238fef46454SAlan Somers 	})));
1239fef46454SAlan Somers 
1240fef46454SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1241fef46454SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1242fef46454SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1243fef46454SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1244fef46454SAlan Somers }
1245fef46454SAlan Somers 
1246fef46454SAlan Somers INSTANTIATE_TEST_CASE_P(RA, TimeGran, Range(0u, 10u));
1247fef46454SAlan Somers 
1248788af953SAlan Somers /*
12499821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
12509821f1d3SAlan Somers  */
1251f8ebf1cdSAlan Somers TEST_F(Write, writethrough)
12529821f1d3SAlan Somers {
12539821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
12549821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
12559821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
12569821f1d3SAlan Somers 	uint64_t ino = 42;
12579821f1d3SAlan Somers 	int fd;
12589821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
12599821f1d3SAlan Somers 	char readbuf[bufsize];
12609821f1d3SAlan Somers 
12619821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
12629821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1263bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
12649821f1d3SAlan Somers 
12659821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
12669821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
12679821f1d3SAlan Somers 
12689821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
12699821f1d3SAlan Somers 	/*
12709821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
12719821f1d3SAlan Somers 	 * filesystem daemon
12729821f1d3SAlan Somers 	 */
1273b5aaf286SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
12749821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
12757fc0921dSAlan Somers 	leak(fd);
12769821f1d3SAlan Somers }
12779821f1d3SAlan Somers 
12780d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */
12790d3a88d7SAlan Somers TEST_F(Write, update_file_size)
12809821f1d3SAlan Somers {
12819821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
12829821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
12839821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
12849821f1d3SAlan Somers 	struct stat sb;
12859821f1d3SAlan Somers 	uint64_t ino = 42;
12869821f1d3SAlan Somers 	int fd;
12879821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
12889821f1d3SAlan Somers 
12899821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
12909821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1291bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
12929821f1d3SAlan Somers 
12939821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
12949821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
12959821f1d3SAlan Somers 
12969821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
12979821f1d3SAlan Somers 	/* Get cached attributes */
12989821f1d3SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
12999821f1d3SAlan Somers 	ASSERT_EQ(bufsize, sb.st_size);
13007fc0921dSAlan Somers 	leak(fd);
13019821f1d3SAlan Somers }
1302