xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision 0a8fe2d3697ef841ccfd03f0a1f66e6d5abe77ad)
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>
369821f1d3SAlan Somers #include <sys/sysctl.h>
37a639731bSAlan Somers #include <sys/time.h>
389821f1d3SAlan Somers #include <sys/uio.h>
399821f1d3SAlan Somers 
409821f1d3SAlan Somers #include <aio.h>
419821f1d3SAlan Somers #include <fcntl.h>
42a639731bSAlan Somers #include <signal.h>
439821f1d3SAlan Somers #include <unistd.h>
449821f1d3SAlan Somers }
459821f1d3SAlan Somers 
469821f1d3SAlan Somers #include "mockfs.hh"
479821f1d3SAlan Somers #include "utils.hh"
489821f1d3SAlan Somers 
499821f1d3SAlan Somers using namespace testing;
509821f1d3SAlan Somers 
519821f1d3SAlan Somers class Write: public FuseTest {
529821f1d3SAlan Somers 
539821f1d3SAlan Somers public:
54a639731bSAlan Somers static sig_atomic_t s_sigxfsz;
55a639731bSAlan Somers 
56a639731bSAlan Somers void SetUp() {
57a639731bSAlan Somers 	s_sigxfsz = 0;
58a639731bSAlan Somers 	FuseTest::SetUp();
59a639731bSAlan Somers }
60a639731bSAlan Somers 
61a639731bSAlan Somers void TearDown() {
62a639731bSAlan Somers 	struct sigaction sa;
63a639731bSAlan Somers 
64a639731bSAlan Somers 	bzero(&sa, sizeof(sa));
65a639731bSAlan Somers 	sa.sa_handler = SIG_DFL;
66a639731bSAlan Somers 	sigaction(SIGXFSZ, &sa, NULL);
67a639731bSAlan Somers 
68a639731bSAlan Somers 	FuseTest::TearDown();
69a639731bSAlan Somers }
709821f1d3SAlan Somers 
719821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
729821f1d3SAlan Somers {
739821f1d3SAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
749821f1d3SAlan Somers }
759821f1d3SAlan Somers 
769821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r)
779821f1d3SAlan Somers {
789821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
799821f1d3SAlan Somers 		ResultOf([=](auto in) {
8029edc611SAlan Somers 			return (in.header.opcode == FUSE_RELEASE &&
8129edc611SAlan Somers 				in.header.nodeid == ino);
829821f1d3SAlan Somers 		}, Eq(true)),
839821f1d3SAlan Somers 		_)
849821f1d3SAlan Somers 	).WillRepeatedly(Invoke(r));
859821f1d3SAlan Somers }
869821f1d3SAlan Somers 
87bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
88bda39894SAlan Somers 	uint64_t osize, const void *contents)
89bda39894SAlan Somers {
90bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
91bda39894SAlan Somers }
92bda39894SAlan Somers 
9384879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */
9484879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
9584879e46SAlan Somers 	const void *contents)
9684879e46SAlan Somers {
9784879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
9884879e46SAlan Somers 		ResultOf([=](auto in) {
9984879e46SAlan Somers 			const char *buf = (const char*)in.body.bytes +
10084879e46SAlan Somers 				sizeof(struct fuse_write_in);
10184879e46SAlan Somers 
10284879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE &&
10384879e46SAlan Somers 				in.header.nodeid == ino &&
10484879e46SAlan Somers 				in.body.write.offset == offset  &&
10584879e46SAlan Somers 				in.body.write.size == size &&
10684879e46SAlan Somers 				0 == bcmp(buf, contents, size));
10784879e46SAlan Somers 		}, Eq(true)),
10884879e46SAlan Somers 		_)
10984879e46SAlan Somers 	).Times(AtMost(1))
11084879e46SAlan Somers 	.WillRepeatedly(Invoke(
11184879e46SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
11284879e46SAlan Somers 			SET_OUT_HEADER_LEN(out, write);
11384879e46SAlan Somers 			out.body.write.size = size;
11484879e46SAlan Somers 		})
11584879e46SAlan Somers 	));
11684879e46SAlan Somers }
11784879e46SAlan Somers 
1189821f1d3SAlan Somers };
1199821f1d3SAlan Somers 
1206fa772a8SAlan Somers class WriteCacheable: public Write {
1216fa772a8SAlan Somers public:
1226fa772a8SAlan Somers virtual void SetUp() {
1236fa772a8SAlan Somers 	const char *node = "vfs.fusefs.data_cache_mode";
1246fa772a8SAlan Somers 	int val = 0;
1256fa772a8SAlan Somers 	size_t size = sizeof(val);
1266fa772a8SAlan Somers 
1276fa772a8SAlan Somers 	FuseTest::SetUp();
1286fa772a8SAlan Somers 
1296fa772a8SAlan Somers 	ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
1306fa772a8SAlan Somers 		<< strerror(errno);
1316fa772a8SAlan Somers 	if (val == 0)
1326fa772a8SAlan Somers 		GTEST_SKIP() <<
1336fa772a8SAlan Somers 			"fusefs data caching must be enabled for this test";
1346fa772a8SAlan Somers }
1356fa772a8SAlan Somers };
1366fa772a8SAlan Somers 
137a639731bSAlan Somers sig_atomic_t Write::s_sigxfsz = 0;
138a639731bSAlan Somers 
13916bd2d47SAlan Somers class Write_7_8: public FuseTest {
14016bd2d47SAlan Somers 
14116bd2d47SAlan Somers public:
14216bd2d47SAlan Somers virtual void SetUp() {
14316bd2d47SAlan Somers 	m_kernel_minor_version = 8;
14416bd2d47SAlan Somers 	FuseTest::SetUp();
14516bd2d47SAlan Somers }
14616bd2d47SAlan Somers 
14716bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
14816bd2d47SAlan Somers {
14916bd2d47SAlan Somers 	FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
15016bd2d47SAlan Somers }
15116bd2d47SAlan Somers 
15216bd2d47SAlan Somers };
15316bd2d47SAlan Somers 
1549821f1d3SAlan Somers class AioWrite: public Write {
1559821f1d3SAlan Somers virtual void SetUp() {
1569821f1d3SAlan Somers 	const char *node = "vfs.aio.enable_unsafe";
1579821f1d3SAlan Somers 	int val = 0;
1589821f1d3SAlan Somers 	size_t size = sizeof(val);
1599821f1d3SAlan Somers 
1609821f1d3SAlan Somers 	FuseTest::SetUp();
1619821f1d3SAlan Somers 
1629821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
1639821f1d3SAlan Somers 		<< strerror(errno);
1649821f1d3SAlan Somers 	if (!val)
1659821f1d3SAlan Somers 		GTEST_SKIP() <<
1669821f1d3SAlan Somers 			"vfs.aio.enable_unsafe must be set for this test";
1679821f1d3SAlan Somers }
1689821f1d3SAlan Somers };
1699821f1d3SAlan Somers 
1709821f1d3SAlan Somers /* Tests for the write-through cache mode */
1719821f1d3SAlan Somers class WriteThrough: public Write {
172bda39894SAlan Somers public:
1739821f1d3SAlan Somers virtual void SetUp() {
1749821f1d3SAlan Somers 	const char *cache_mode_node = "vfs.fusefs.data_cache_mode";
1759821f1d3SAlan Somers 	int val = 0;
1769821f1d3SAlan Somers 	size_t size = sizeof(val);
1779821f1d3SAlan Somers 
1789821f1d3SAlan Somers 	FuseTest::SetUp();
1799821f1d3SAlan Somers 	if (IsSkipped())
1809821f1d3SAlan Somers 		return;
1819821f1d3SAlan Somers 
1829821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0))
1839821f1d3SAlan Somers 		<< strerror(errno);
1849821f1d3SAlan Somers 	if (val != 1)
1859821f1d3SAlan Somers 		GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 "
1869821f1d3SAlan Somers 			"(writethrough) for this test";
1879821f1d3SAlan Somers }
1889821f1d3SAlan Somers 
189bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
190bda39894SAlan Somers 	uint64_t osize, const void *contents)
191bda39894SAlan Somers {
192bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, 0, FUSE_WRITE_CACHE,
193bda39894SAlan Somers 		contents);
194bda39894SAlan Somers }
1959821f1d3SAlan Somers };
1969821f1d3SAlan Somers 
1979821f1d3SAlan Somers /* Tests for the writeback cache mode */
1989821f1d3SAlan Somers class WriteBack: public Write {
199bda39894SAlan Somers public:
2009821f1d3SAlan Somers virtual void SetUp() {
2019821f1d3SAlan Somers 	const char *node = "vfs.fusefs.data_cache_mode";
2029821f1d3SAlan Somers 	int val = 0;
2039821f1d3SAlan Somers 	size_t size = sizeof(val);
2049821f1d3SAlan Somers 
2059821f1d3SAlan Somers 	FuseTest::SetUp();
2069821f1d3SAlan Somers 	if (IsSkipped())
2079821f1d3SAlan Somers 		return;
2089821f1d3SAlan Somers 
2099821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
2109821f1d3SAlan Somers 		<< strerror(errno);
2119821f1d3SAlan Somers 	if (val != 2)
2129821f1d3SAlan Somers 		GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 "
2139821f1d3SAlan Somers 			"(writeback) for this test";
2149821f1d3SAlan Somers }
2159821f1d3SAlan Somers 
216bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
217bda39894SAlan Somers 	uint64_t osize, const void *contents)
218bda39894SAlan Somers {
219bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
220bda39894SAlan Somers 		contents);
221bda39894SAlan Somers }
2229821f1d3SAlan Somers };
2239821f1d3SAlan Somers 
22484879e46SAlan Somers class WriteBackAsync: public WriteBack {
22584879e46SAlan Somers public:
22684879e46SAlan Somers virtual void SetUp() {
22784879e46SAlan Somers 	m_async = true;
22884879e46SAlan Somers 	WriteBack::SetUp();
22984879e46SAlan Somers }
23084879e46SAlan Somers };
23184879e46SAlan Somers 
2328eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */
2338eecd9ceSAlan Somers class WriteCluster: public WriteBack {
2348eecd9ceSAlan Somers public:
2358eecd9ceSAlan Somers virtual void SetUp() {
236f2704f05SAlan Somers 	if (m_maxphys < 2 * DFLTPHYS)
2378eecd9ceSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
2388eecd9ceSAlan Somers 			<< " for this test";
2398eecd9ceSAlan Somers 	m_async = true;
240f2704f05SAlan Somers 	m_maxwrite = m_maxphys;
2418eecd9ceSAlan Somers 	WriteBack::SetUp();
242f2704f05SAlan Somers 	if (m_maxphys < 2 * m_maxbcachebuf)
2436ca3b02bSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf"
2446ca3b02bSAlan Somers 			<< " for this test";
2458eecd9ceSAlan Somers }
2468eecd9ceSAlan Somers };
2478eecd9ceSAlan Somers 
248a639731bSAlan Somers void sigxfsz_handler(int __unused sig) {
249a639731bSAlan Somers 	Write::s_sigxfsz = 1;
250a639731bSAlan Somers }
251a639731bSAlan Somers 
2529821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */
2539821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
2549821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write)
2559821f1d3SAlan Somers {
2569821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2579821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2589821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
2599821f1d3SAlan Somers 	uint64_t ino = 42;
2609821f1d3SAlan Somers 	uint64_t offset = 4096;
2619821f1d3SAlan Somers 	int fd;
2629821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
2639821f1d3SAlan Somers 	struct aiocb iocb, *piocb;
2649821f1d3SAlan Somers 
2659821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
2669821f1d3SAlan Somers 	expect_open(ino, 0, 1);
267bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
2689821f1d3SAlan Somers 
2699821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
2709821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
2719821f1d3SAlan Somers 
2729821f1d3SAlan Somers 	iocb.aio_nbytes = bufsize;
2739821f1d3SAlan Somers 	iocb.aio_fildes = fd;
2749821f1d3SAlan Somers 	iocb.aio_buf = (void *)CONTENTS;
2759821f1d3SAlan Somers 	iocb.aio_offset = offset;
2769821f1d3SAlan Somers 	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
2779821f1d3SAlan Somers 	ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
2789821f1d3SAlan Somers 	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
2799821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
2809821f1d3SAlan Somers }
2819821f1d3SAlan Somers 
2829821f1d3SAlan Somers /*
2839821f1d3SAlan Somers  * When a file is opened with O_APPEND, we should forward that flag to
2849821f1d3SAlan Somers  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
2859821f1d3SAlan Somers  * offset internally.  That way we'll work both with filesystems that
2869821f1d3SAlan Somers  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
2879821f1d3SAlan Somers  * simply use the offset).
2889821f1d3SAlan Somers  *
2899821f1d3SAlan Somers  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
2909821f1d3SAlan Somers  * Open.o_append test.
2919821f1d3SAlan Somers  */
2929821f1d3SAlan Somers TEST_F(Write, append)
2939821f1d3SAlan Somers {
2949821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
2959821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2969821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2979821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
2989821f1d3SAlan Somers 	uint64_t ino = 42;
2999821f1d3SAlan Somers 	/*
3009821f1d3SAlan Somers 	 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
3019821f1d3SAlan Somers 	 * using writeback caching
3029821f1d3SAlan Somers 	 */
3039821f1d3SAlan Somers 	uint64_t initial_offset = m_maxbcachebuf;
3049821f1d3SAlan Somers 	int fd;
3059821f1d3SAlan Somers 
3069821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
3079821f1d3SAlan Somers 	expect_open(ino, 0, 1);
308bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
3099821f1d3SAlan Somers 
3109821f1d3SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
3119821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
3129821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3139821f1d3SAlan Somers 
3149821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3159821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
3169821f1d3SAlan Somers }
3179821f1d3SAlan Somers 
318a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */
319a87e0831SAlan Somers TEST_F(Write, append_to_cached)
320a87e0831SAlan Somers {
321a87e0831SAlan Somers 	const ssize_t BUFSIZE = 9;
322a87e0831SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
323a87e0831SAlan Somers 	const char RELPATH[] = "some_file.txt";
324a87e0831SAlan Somers 	char *oldcontents, *oldbuf;
325a87e0831SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
326a87e0831SAlan Somers 	uint64_t ino = 42;
327a87e0831SAlan Somers 	/*
328a87e0831SAlan Somers 	 * Set offset in between maxbcachebuf boundary to test buffer handling
329a87e0831SAlan Somers 	 */
330a87e0831SAlan Somers 	uint64_t oldsize = m_maxbcachebuf / 2;
331a87e0831SAlan Somers 	int fd;
332a87e0831SAlan Somers 
333a87e0831SAlan Somers 	oldcontents = (char*)calloc(1, oldsize);
334a87e0831SAlan Somers 	ASSERT_NE(NULL, oldcontents) << strerror(errno);
335a87e0831SAlan Somers 	oldbuf = (char*)malloc(oldsize);
336a87e0831SAlan Somers 	ASSERT_NE(NULL, oldbuf) << strerror(errno);
337a87e0831SAlan Somers 
338a87e0831SAlan Somers 	expect_lookup(RELPATH, ino, oldsize);
339a87e0831SAlan Somers 	expect_open(ino, 0, 1);
340a87e0831SAlan Somers 	expect_read(ino, 0, oldsize, oldsize, oldcontents);
34184879e46SAlan Somers 	maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
342a87e0831SAlan Somers 
343a87e0831SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
344a87e0831SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
345a87e0831SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
346a87e0831SAlan Somers 
347a87e0831SAlan Somers 	/* Read the old data into the cache */
348a87e0831SAlan Somers 	ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
349a87e0831SAlan Somers 		<< strerror(errno);
350a87e0831SAlan Somers 
351a87e0831SAlan Somers 	/* Write the new data.  There should be no more read operations */
352a87e0831SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
353a87e0831SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
354a87e0831SAlan Somers }
355a87e0831SAlan Somers 
3569821f1d3SAlan Somers TEST_F(Write, append_direct_io)
3579821f1d3SAlan Somers {
3589821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
3599821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3609821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3619821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
3629821f1d3SAlan Somers 	uint64_t ino = 42;
3639821f1d3SAlan Somers 	uint64_t initial_offset = 4096;
3649821f1d3SAlan Somers 	int fd;
3659821f1d3SAlan Somers 
3669821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
3679821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
368bda39894SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
3699821f1d3SAlan Somers 
3709821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY | O_APPEND);
3719821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3729821f1d3SAlan Somers 
3739821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3749821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
3759821f1d3SAlan Somers }
3769821f1d3SAlan Somers 
3779821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */
3786af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache)
3799821f1d3SAlan Somers {
3809821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3819821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3829821f1d3SAlan Somers 	const char CONTENTS0[] = "abcdefgh";
3839821f1d3SAlan Somers 	const char CONTENTS1[] = "ijklmnop";
3849821f1d3SAlan Somers 	uint64_t ino = 42;
3859821f1d3SAlan Somers 	int fd;
3869821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
3879821f1d3SAlan Somers 	char readbuf[bufsize];
3889821f1d3SAlan Somers 
3899821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
3909821f1d3SAlan Somers 	expect_open(ino, 0, 1);
3919821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
392bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
3939821f1d3SAlan Somers 
3949821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
3959821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3969821f1d3SAlan Somers 
3979821f1d3SAlan Somers 	// Prime cache
3989821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3999821f1d3SAlan Somers 
4009821f1d3SAlan Somers 	// Write directly, evicting cache
4019821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
4029821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
4039821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
4049821f1d3SAlan Somers 
4059821f1d3SAlan Somers 	// Read again.  Cache should be bypassed
4069821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
4079821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
4089821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
4099821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
4109821f1d3SAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS1);
4119821f1d3SAlan Somers 
4129821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
4139821f1d3SAlan Somers }
4149821f1d3SAlan Somers 
4159821f1d3SAlan Somers /*
41612292a99SAlan Somers  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
41712292a99SAlan Somers  * allowed to return a short write for that file handle.  However, if it does
41812292a99SAlan Somers  * then we should still do our darndest to handle it by resending the unwritten
41912292a99SAlan Somers  * portion.
4209821f1d3SAlan Somers  */
42112292a99SAlan Somers TEST_F(Write, indirect_io_short_write)
4229821f1d3SAlan Somers {
4239821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4249821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4259821f1d3SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
4269821f1d3SAlan Somers 	uint64_t ino = 42;
4279821f1d3SAlan Somers 	int fd;
4289821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
42912292a99SAlan Somers 	ssize_t bufsize0 = 11;
43012292a99SAlan Somers 	ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
43112292a99SAlan Somers 	const char *contents1 = CONTENTS + bufsize0;
4329821f1d3SAlan Somers 
4339821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
43412292a99SAlan Somers 	expect_open(ino, 0, 1);
435bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
436bda39894SAlan Somers 	expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
4379821f1d3SAlan Somers 
4389821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
4399821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
4409821f1d3SAlan Somers 
4419821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4429821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
4439821f1d3SAlan Somers }
4449821f1d3SAlan Somers 
4459821f1d3SAlan Somers /*
44612292a99SAlan Somers  * When the direct_io option is used, filesystems are allowed to write less
44712292a99SAlan Somers  * data than requested.  We should return the short write to userland.
44812292a99SAlan Somers  */
44912292a99SAlan Somers TEST_F(Write, direct_io_short_write)
45012292a99SAlan Somers {
45112292a99SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
45212292a99SAlan Somers 	const char RELPATH[] = "some_file.txt";
45312292a99SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
45412292a99SAlan Somers 	uint64_t ino = 42;
45512292a99SAlan Somers 	int fd;
45612292a99SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
45712292a99SAlan Somers 	ssize_t halfbufsize = bufsize / 2;
45812292a99SAlan Somers 
45912292a99SAlan Somers 	expect_lookup(RELPATH, ino, 0);
46012292a99SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
461bda39894SAlan Somers 	expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
46212292a99SAlan Somers 
46312292a99SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
46412292a99SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
46512292a99SAlan Somers 
46612292a99SAlan Somers 	ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
46712292a99SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
46812292a99SAlan Somers }
46912292a99SAlan Somers 
47012292a99SAlan Somers /*
4719821f1d3SAlan Somers  * An insidious edge case: the filesystem returns a short write, and the
4729821f1d3SAlan Somers  * difference between what we requested and what it actually wrote crosses an
4739821f1d3SAlan Somers  * iov element boundary
4749821f1d3SAlan Somers  */
47512292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov)
4769821f1d3SAlan Somers {
4779821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4789821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4799821f1d3SAlan Somers 	const char *CONTENTS0 = "abcdefgh";
4809821f1d3SAlan Somers 	const char *CONTENTS1 = "ijklmnop";
4819821f1d3SAlan Somers 	const char *EXPECTED0 = "abcdefghijklmnop";
4829821f1d3SAlan Somers 	uint64_t ino = 42;
4839821f1d3SAlan Somers 	int fd;
4849821f1d3SAlan Somers 	ssize_t size0 = strlen(CONTENTS0) - 1;
4859821f1d3SAlan Somers 	ssize_t size1 = strlen(CONTENTS1) + 1;
4869821f1d3SAlan Somers 	ssize_t totalsize = size0 + size1;
4879821f1d3SAlan Somers 	struct iovec iov[2];
4889821f1d3SAlan Somers 
4899821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
4909821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
491bda39894SAlan Somers 	expect_write(ino, 0, totalsize, size0, EXPECTED0);
4929821f1d3SAlan Somers 
4939821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
4949821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
4959821f1d3SAlan Somers 
4969821f1d3SAlan Somers 	iov[0].iov_base = (void*)CONTENTS0;
4979821f1d3SAlan Somers 	iov[0].iov_len = strlen(CONTENTS0);
4989821f1d3SAlan Somers 	iov[1].iov_base = (void*)CONTENTS1;
4999821f1d3SAlan Somers 	iov[1].iov_len = strlen(CONTENTS1);
50012292a99SAlan Somers 	ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
5019821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
5029821f1d3SAlan Somers }
5039821f1d3SAlan Somers 
504a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */
505a639731bSAlan Somers TEST_F(Write, rlimit_fsize)
506a639731bSAlan Somers {
507a639731bSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
508a639731bSAlan Somers 	const char RELPATH[] = "some_file.txt";
509a639731bSAlan Somers 	const char *CONTENTS = "abcdefgh";
510a639731bSAlan Somers 	struct rlimit rl;
511a639731bSAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
512a639731bSAlan Somers 	off_t offset = 1'000'000'000;
513a639731bSAlan Somers 	uint64_t ino = 42;
514a639731bSAlan Somers 	int fd;
515a639731bSAlan Somers 
516a639731bSAlan Somers 	expect_lookup(RELPATH, ino, 0);
517a639731bSAlan Somers 	expect_open(ino, 0, 1);
518a639731bSAlan Somers 
519a639731bSAlan Somers 	rl.rlim_cur = offset;
520a639731bSAlan Somers 	rl.rlim_max = 10 * offset;
521a639731bSAlan Somers 	ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
522a639731bSAlan Somers 	ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
523a639731bSAlan Somers 
524a639731bSAlan Somers 	fd = open(FULLPATH, O_WRONLY);
525a639731bSAlan Somers 
526a639731bSAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
527a639731bSAlan Somers 
528a639731bSAlan Somers 	ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
529a639731bSAlan Somers 	EXPECT_EQ(EFBIG, errno);
530a639731bSAlan Somers 	EXPECT_EQ(1, s_sigxfsz);
531a639731bSAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
532a639731bSAlan Somers }
533a639731bSAlan Somers 
5349821f1d3SAlan Somers /*
535b9e20197SAlan Somers  * A short read indicates EOF.  Test that nothing bad happens if we get EOF
536b9e20197SAlan Somers  * during the R of a RMW operation.
537b9e20197SAlan Somers  */
538b9e20197SAlan Somers TEST_F(WriteCacheable, eof_during_rmw)
539b9e20197SAlan Somers {
540b9e20197SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
541b9e20197SAlan Somers 	const char RELPATH[] = "some_file.txt";
542b9e20197SAlan Somers 	const char *CONTENTS = "abcdefgh";
543b9e20197SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
544b9e20197SAlan Somers 	uint64_t ino = 42;
545b9e20197SAlan Somers 	uint64_t offset = 1;
546b9e20197SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
547b9e20197SAlan Somers 	off_t orig_fsize = 10;
548b9e20197SAlan Somers 	off_t truncated_fsize = 5;
549b9e20197SAlan Somers 	off_t final_fsize = bufsize;
550b9e20197SAlan Somers 	int fd;
551b9e20197SAlan Somers 
552b9e20197SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
553b9e20197SAlan Somers 	expect_open(ino, 0, 1);
554b9e20197SAlan Somers 	expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
555b9e20197SAlan Somers 	expect_getattr(ino, truncated_fsize);
556b9e20197SAlan Somers 	expect_read(ino, 0, final_fsize, final_fsize, INITIAL, O_RDWR);
557b9e20197SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
558b9e20197SAlan Somers 
559b9e20197SAlan Somers 	fd = open(FULLPATH, O_RDWR);
560b9e20197SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
561b9e20197SAlan Somers 
562b9e20197SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
563b9e20197SAlan Somers 		<< strerror(errno);
564b9e20197SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
565b9e20197SAlan Somers }
566b9e20197SAlan Somers 
567b9e20197SAlan Somers /*
5689821f1d3SAlan Somers  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
5699821f1d3SAlan Somers  * write, then it must set the FUSE_WRITE_CACHE bit
5709821f1d3SAlan Somers  */
5719821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
5726fa772a8SAlan Somers TEST_F(WriteCacheable, mmap)
5739821f1d3SAlan Somers {
5749821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5759821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5769821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
5779821f1d3SAlan Somers 	uint64_t ino = 42;
5789821f1d3SAlan Somers 	int fd;
5799821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5809821f1d3SAlan Somers 	void *p;
5819821f1d3SAlan Somers 	uint64_t offset = 10;
5829821f1d3SAlan Somers 	size_t len;
5839821f1d3SAlan Somers 	void *zeros, *expected;
5849821f1d3SAlan Somers 
5859821f1d3SAlan Somers 	len = getpagesize();
5869821f1d3SAlan Somers 
5879821f1d3SAlan Somers 	zeros = calloc(1, len);
5889821f1d3SAlan Somers 	ASSERT_NE(NULL, zeros);
5899821f1d3SAlan Somers 	expected = calloc(1, len);
5909821f1d3SAlan Somers 	ASSERT_NE(NULL, expected);
5919821f1d3SAlan Somers 	memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
5929821f1d3SAlan Somers 
5939821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, len);
5949821f1d3SAlan Somers 	expect_open(ino, 0, 1);
5959821f1d3SAlan Somers 	expect_read(ino, 0, len, len, zeros);
5969821f1d3SAlan Somers 	/*
5979821f1d3SAlan Somers 	 * Writes from the pager may or may not be associated with the correct
598cf437e2aSAlan Somers 	 * pid, so they must set FUSE_WRITE_CACHE.
5999821f1d3SAlan Somers 	 */
600bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
6019f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
6029821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
6039821f1d3SAlan Somers 
6049821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
6059821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6069821f1d3SAlan Somers 
6079821f1d3SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
6089821f1d3SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
6099821f1d3SAlan Somers 
6109821f1d3SAlan Somers 	memmove((uint8_t*)p + offset, CONTENTS, bufsize);
6119821f1d3SAlan Somers 
6129821f1d3SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
6139821f1d3SAlan Somers 	close(fd);	// Write mmap'd data on close
6149821f1d3SAlan Somers 
6159821f1d3SAlan Somers 	free(expected);
6169821f1d3SAlan Somers 	free(zeros);
6179821f1d3SAlan Somers }
6189821f1d3SAlan Somers 
6195fccbf31SAlan Somers TEST_F(WriteThrough, pwrite)
6209821f1d3SAlan Somers {
6219821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6229821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6239821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6249821f1d3SAlan Somers 	uint64_t ino = 42;
6256ca3b02bSAlan Somers 	uint64_t offset = m_maxbcachebuf;
6269821f1d3SAlan Somers 	int fd;
6279821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6289821f1d3SAlan Somers 
6299821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6309821f1d3SAlan Somers 	expect_open(ino, 0, 1);
631bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
6329821f1d3SAlan Somers 
6339821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
6349821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6359821f1d3SAlan Somers 
6369821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
6379821f1d3SAlan Somers 		<< strerror(errno);
6389821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6399821f1d3SAlan Somers }
6409821f1d3SAlan Somers 
641788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */
642788af953SAlan Somers TEST_F(Write, timestamps)
643788af953SAlan Somers {
644788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
645788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
646788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
647788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
648788af953SAlan Somers 	uint64_t ino = 42;
649788af953SAlan Somers 	struct stat sb0, sb1;
650788af953SAlan Somers 	int fd;
651788af953SAlan Somers 
652788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
653788af953SAlan Somers 	expect_open(ino, 0, 1);
654788af953SAlan Somers 	maybe_expect_write(ino, 0, bufsize, CONTENTS);
655788af953SAlan Somers 
656788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
657788af953SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
658788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
659788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
660788af953SAlan Somers 
661788af953SAlan Somers 	nap();
662788af953SAlan Somers 
663788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno);
664788af953SAlan Somers 
665788af953SAlan Somers 	EXPECT_EQ(sb0.st_atime, sb1.st_atime);
666788af953SAlan Somers 	EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
667788af953SAlan Somers 	EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
668788af953SAlan Somers }
669788af953SAlan Somers 
6709821f1d3SAlan Somers TEST_F(Write, write)
6719821f1d3SAlan Somers {
6729821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6739821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6749821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6759821f1d3SAlan Somers 	uint64_t ino = 42;
6769821f1d3SAlan Somers 	int fd;
6779821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6789821f1d3SAlan Somers 
6799821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6809821f1d3SAlan Somers 	expect_open(ino, 0, 1);
681bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
6829821f1d3SAlan Somers 
6839821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
6849821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6859821f1d3SAlan Somers 
6869821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6879821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6889821f1d3SAlan Somers }
6899821f1d3SAlan Somers 
6909821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */
6919821f1d3SAlan Somers TEST_F(Write, write_large)
6929821f1d3SAlan Somers {
6939821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6949821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6959821f1d3SAlan Somers 	int *contents;
6969821f1d3SAlan Somers 	uint64_t ino = 42;
6979821f1d3SAlan Somers 	int fd;
6989821f1d3SAlan Somers 	ssize_t halfbufsize, bufsize;
6999821f1d3SAlan Somers 
7008eecd9ceSAlan Somers 	halfbufsize = m_mock->m_maxwrite;
7019821f1d3SAlan Somers 	bufsize = halfbufsize * 2;
7029821f1d3SAlan Somers 	contents = (int*)malloc(bufsize);
7039821f1d3SAlan Somers 	ASSERT_NE(NULL, contents);
7049821f1d3SAlan Somers 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
7059821f1d3SAlan Somers 		contents[i] = i;
7069821f1d3SAlan Somers 	}
7079821f1d3SAlan Somers 
7089821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7099821f1d3SAlan Somers 	expect_open(ino, 0, 1);
710f2704f05SAlan Somers 	maybe_expect_write(ino, 0, halfbufsize, contents);
71184879e46SAlan Somers 	maybe_expect_write(ino, halfbufsize, halfbufsize,
7129821f1d3SAlan Somers 		&contents[halfbufsize / sizeof(int)]);
7139821f1d3SAlan Somers 
7149821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
7159821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
7169821f1d3SAlan Somers 
7179821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
7189821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
7199821f1d3SAlan Somers 
7209821f1d3SAlan Somers 	free(contents);
7219821f1d3SAlan Somers }
7229821f1d3SAlan Somers 
7239821f1d3SAlan Somers TEST_F(Write, write_nothing)
7249821f1d3SAlan Somers {
7259821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7269821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7279821f1d3SAlan Somers 	const char *CONTENTS = "";
7289821f1d3SAlan Somers 	uint64_t ino = 42;
7299821f1d3SAlan Somers 	int fd;
7309821f1d3SAlan Somers 	ssize_t bufsize = 0;
7319821f1d3SAlan Somers 
7329821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7339821f1d3SAlan Somers 	expect_open(ino, 0, 1);
7349821f1d3SAlan Somers 
7359821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
7369821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
7379821f1d3SAlan Somers 
7389821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7399821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
7409821f1d3SAlan Somers }
7419821f1d3SAlan Somers 
74216bd2d47SAlan Somers TEST_F(Write_7_8, write)
74316bd2d47SAlan Somers {
74416bd2d47SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
74516bd2d47SAlan Somers 	const char RELPATH[] = "some_file.txt";
74616bd2d47SAlan Somers 	const char *CONTENTS = "abcdefgh";
74716bd2d47SAlan Somers 	uint64_t ino = 42;
74816bd2d47SAlan Somers 	int fd;
74916bd2d47SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
75016bd2d47SAlan Somers 
75116bd2d47SAlan Somers 	expect_lookup(RELPATH, ino, 0);
75216bd2d47SAlan Somers 	expect_open(ino, 0, 1);
753bda39894SAlan Somers 	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
75416bd2d47SAlan Somers 
75516bd2d47SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
75616bd2d47SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
75716bd2d47SAlan Somers 
75816bd2d47SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
75916bd2d47SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
76016bd2d47SAlan Somers }
76116bd2d47SAlan Somers 
7629821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */
76384879e46SAlan Somers TEST_F(WriteBackAsync, close)
7649821f1d3SAlan Somers {
7659821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7669821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7679821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7689821f1d3SAlan Somers 	uint64_t ino = 42;
7699821f1d3SAlan Somers 	int fd;
7709821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7719821f1d3SAlan Somers 
7729821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7739821f1d3SAlan Somers 	expect_open(ino, 0, 1);
774bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
7759821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
7769821f1d3SAlan Somers 		ResultOf([=](auto in) {
77729edc611SAlan Somers 			return (in.header.opcode == FUSE_SETATTR);
7789821f1d3SAlan Somers 		}, Eq(true)),
7799821f1d3SAlan Somers 		_)
78029edc611SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
7819821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
78229edc611SAlan Somers 		out.body.attr.attr.ino = ino;	// Must match nodeid
7839821f1d3SAlan Somers 	})));
7849f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
7859821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
7869821f1d3SAlan Somers 
7879821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
7889821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7899821f1d3SAlan Somers 
7909821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7919821f1d3SAlan Somers 	close(fd);
7929821f1d3SAlan Somers }
7939821f1d3SAlan Somers 
7948eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */
7958eecd9ceSAlan Somers TEST_F(WriteCluster, clustering)
7968eecd9ceSAlan Somers {
7978eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7988eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
7998eecd9ceSAlan Somers 	uint64_t ino = 42;
8008eecd9ceSAlan Somers 	int i, fd;
8018eecd9ceSAlan Somers 	void *wbuf, *wbuf2x;
8026ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
8036ca3b02bSAlan Somers 	off_t filesize = 5 * bufsize;
8048eecd9ceSAlan Somers 
8058eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
8068eecd9ceSAlan Somers 	ASSERT_NE(NULL, wbuf) << strerror(errno);
8078eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
8088eecd9ceSAlan Somers 	wbuf2x = malloc(2 * bufsize);
8098eecd9ceSAlan Somers 	ASSERT_NE(NULL, wbuf2x) << strerror(errno);
8108eecd9ceSAlan Somers 	memset(wbuf2x, 'X', 2 * bufsize);
8118eecd9ceSAlan Somers 
8128eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
8138eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
8148eecd9ceSAlan Somers 	/*
8158eecd9ceSAlan Somers 	 * Writes of bufsize-bytes each should be clustered into greater sizes.
8168eecd9ceSAlan Somers 	 * The amount of clustering is adaptive, so the first write actually
8178eecd9ceSAlan Somers 	 * issued will be 2x bufsize and subsequent writes may be larger
8188eecd9ceSAlan Somers 	 */
8198eecd9ceSAlan Somers 	expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
8208eecd9ceSAlan Somers 	expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
8218eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
8228eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
8238eecd9ceSAlan Somers 
8248eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
8258eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8268eecd9ceSAlan Somers 
8278eecd9ceSAlan Somers 	for (i = 0; i < 4; i++) {
8288eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
8298eecd9ceSAlan Somers 			<< strerror(errno);
8308eecd9ceSAlan Somers 	}
8318eecd9ceSAlan Somers 	close(fd);
8328eecd9ceSAlan Somers }
8338eecd9ceSAlan Somers 
8348eecd9ceSAlan Somers /*
8358eecd9ceSAlan Somers  * When clustering writes, an I/O error to any of the cluster's children should
8368eecd9ceSAlan Somers  * not panic the system on unmount
8378eecd9ceSAlan Somers  */
8388eecd9ceSAlan Somers /*
8398eecd9ceSAlan Somers  * Disabled because it panics.
8408eecd9ceSAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
8418eecd9ceSAlan Somers  */
8428eecd9ceSAlan Somers TEST_F(WriteCluster, DISABLED_cluster_write_err)
8438eecd9ceSAlan Somers {
8448eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8458eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
8468eecd9ceSAlan Somers 	uint64_t ino = 42;
8478eecd9ceSAlan Somers 	int i, fd;
8488eecd9ceSAlan Somers 	void *wbuf;
8496ca3b02bSAlan Somers 	ssize_t bufsize = m_maxbcachebuf;
8506ca3b02bSAlan Somers 	off_t filesize = 4 * bufsize;
8518eecd9ceSAlan Somers 
8528eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
8538eecd9ceSAlan Somers 	ASSERT_NE(NULL, wbuf) << strerror(errno);
8548eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
8558eecd9ceSAlan Somers 
8568eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
8578eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
8588eecd9ceSAlan Somers 	EXPECT_CALL(*m_mock, process(
8598eecd9ceSAlan Somers 		ResultOf([=](auto in) {
8608eecd9ceSAlan Somers 			return (in.header.opcode == FUSE_WRITE);
8618eecd9ceSAlan Somers 		}, Eq(true)),
8628eecd9ceSAlan Somers 		_)
8638eecd9ceSAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
8648eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
8658eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
8668eecd9ceSAlan Somers 
8678eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
8688eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
8698eecd9ceSAlan Somers 
8708eecd9ceSAlan Somers 	for (i = 0; i < 3; i++) {
8718eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
8728eecd9ceSAlan Somers 			<< strerror(errno);
8738eecd9ceSAlan Somers 	}
8748eecd9ceSAlan Somers 	close(fd);
8758eecd9ceSAlan Somers }
8768eecd9ceSAlan Somers 
8779821f1d3SAlan Somers /*
8785fccbf31SAlan Somers  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
8795fccbf31SAlan Somers  * server.  The FUSE protocol explicitly allows that.
8805fccbf31SAlan Somers  */
8815fccbf31SAlan Somers TEST_F(WriteBack, rmw)
8825fccbf31SAlan Somers {
8835fccbf31SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8845fccbf31SAlan Somers 	const char RELPATH[] = "some_file.txt";
8855fccbf31SAlan Somers 	const char *CONTENTS = "abcdefgh";
8865fccbf31SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
8875fccbf31SAlan Somers 	uint64_t ino = 42;
8885fccbf31SAlan Somers 	uint64_t offset = 1;
8895fccbf31SAlan Somers 	off_t fsize = 10;
8905fccbf31SAlan Somers 	int fd;
8915fccbf31SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8925fccbf31SAlan Somers 
893cad67791SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
8945fccbf31SAlan Somers 	expect_open(ino, 0, 1);
895d4fd0c81SAlan Somers 	expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
89684879e46SAlan Somers 	maybe_expect_write(ino, offset, bufsize, CONTENTS);
8975fccbf31SAlan Somers 
8985fccbf31SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
8995fccbf31SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
9005fccbf31SAlan Somers 
9015fccbf31SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
9025fccbf31SAlan Somers 		<< strerror(errno);
9035fccbf31SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
9045fccbf31SAlan Somers }
9055fccbf31SAlan Somers 
9065fccbf31SAlan Somers /*
9079821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
9089821f1d3SAlan Somers  */
90984879e46SAlan Somers TEST_F(WriteBack, cache)
9109821f1d3SAlan Somers {
9119821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9129821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
9139821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
9149821f1d3SAlan Somers 	uint64_t ino = 42;
9159821f1d3SAlan Somers 	int fd;
9169821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
9179821f1d3SAlan Somers 	char readbuf[bufsize];
9189821f1d3SAlan Somers 
9199821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
9209821f1d3SAlan Somers 	expect_open(ino, 0, 1);
921bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
9229821f1d3SAlan Somers 
9239821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
9249821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
9259821f1d3SAlan Somers 
9269821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9279821f1d3SAlan Somers 	/*
9289821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
9299821f1d3SAlan Somers 	 * filesystem daemon
9309821f1d3SAlan Somers 	 */
9319821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
9329821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
9339821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
9349821f1d3SAlan Somers }
9359821f1d3SAlan Somers 
9369821f1d3SAlan Somers /*
9379821f1d3SAlan Somers  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
9389821f1d3SAlan Somers  * an odd test, because it would be unusual to use O_DIRECT for writes but not
9399821f1d3SAlan Somers  * reads.
9409821f1d3SAlan Somers  */
9419821f1d3SAlan Somers TEST_F(WriteBack, o_direct)
9429821f1d3SAlan Somers {
9439821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9449821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
9459821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
9469821f1d3SAlan Somers 	uint64_t ino = 42;
9479821f1d3SAlan Somers 	int fd;
9489821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
9499821f1d3SAlan Somers 	char readbuf[bufsize];
9509821f1d3SAlan Somers 
9519821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
9529821f1d3SAlan Somers 	expect_open(ino, 0, 1);
953bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
954bda39894SAlan Somers 		CONTENTS);
9559821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
9569821f1d3SAlan Somers 
9579821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
9589821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
9599821f1d3SAlan Somers 
9609821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9619821f1d3SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
9629821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
9639821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
9649821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
9659821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
9669821f1d3SAlan Somers }
9679821f1d3SAlan Somers 
9689821f1d3SAlan Somers /*
96984879e46SAlan Somers  * When mounted with -o async, the writeback cache mode should delay writes
97084879e46SAlan Somers  */
97184879e46SAlan Somers TEST_F(WriteBackAsync, delay)
97284879e46SAlan Somers {
97384879e46SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
97484879e46SAlan Somers 	const char RELPATH[] = "some_file.txt";
97584879e46SAlan Somers 	const char *CONTENTS = "abcdefgh";
97684879e46SAlan Somers 	uint64_t ino = 42;
97784879e46SAlan Somers 	int fd;
97884879e46SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
97984879e46SAlan Somers 
98084879e46SAlan Somers 	expect_lookup(RELPATH, ino, 0);
98184879e46SAlan Somers 	expect_open(ino, 0, 1);
98284879e46SAlan Somers 	/* Write should be cached, but FUSE_WRITE shouldn't be sent */
98384879e46SAlan Somers 	EXPECT_CALL(*m_mock, process(
98484879e46SAlan Somers 		ResultOf([=](auto in) {
98584879e46SAlan Somers 			return (in.header.opcode == FUSE_WRITE);
98684879e46SAlan Somers 		}, Eq(true)),
98784879e46SAlan Somers 		_)
98884879e46SAlan Somers 	).Times(0);
98984879e46SAlan Somers 
99084879e46SAlan Somers 	fd = open(FULLPATH, O_RDWR);
99184879e46SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
99284879e46SAlan Somers 
99384879e46SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
99484879e46SAlan Somers 
99584879e46SAlan Somers 	/* Don't close the file because that would flush the cache */
99684879e46SAlan Somers }
99784879e46SAlan Somers 
99884879e46SAlan Somers /*
999aef22f2dSAlan Somers  * In WriteBack mode, writes may be cached beyond what the server thinks is the
1000aef22f2dSAlan Somers  * EOF.  In this case, a short read at EOF should _not_ cause fusefs to update
1001aef22f2dSAlan Somers  * the file's size.
1002aef22f2dSAlan Somers  */
1003aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof)
1004aef22f2dSAlan Somers {
1005aef22f2dSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1006aef22f2dSAlan Somers 	const char RELPATH[] = "some_file.txt";
1007aef22f2dSAlan Somers 	const char *CONTENTS0 = "abcdefgh";
1008aef22f2dSAlan Somers 	const char *CONTENTS1 = "ijklmnop";
1009aef22f2dSAlan Somers 	uint64_t ino = 42;
1010aef22f2dSAlan Somers 	int fd;
1011aef22f2dSAlan Somers 	off_t offset = m_maxbcachebuf;
1012aef22f2dSAlan Somers 	ssize_t wbufsize = strlen(CONTENTS1);
1013aef22f2dSAlan Somers 	off_t old_filesize = (off_t)strlen(CONTENTS0);
1014aef22f2dSAlan Somers 	ssize_t rbufsize = 2 * old_filesize;
1015aef22f2dSAlan Somers 	char readbuf[rbufsize];
1016aef22f2dSAlan Somers 	size_t holesize = rbufsize - old_filesize;
1017aef22f2dSAlan Somers 	char hole[holesize];
1018aef22f2dSAlan Somers 	struct stat sb;
1019aef22f2dSAlan Somers 	ssize_t r;
1020aef22f2dSAlan Somers 
1021aef22f2dSAlan Somers 	expect_lookup(RELPATH, ino, 0);
1022aef22f2dSAlan Somers 	expect_open(ino, 0, 1);
1023aef22f2dSAlan Somers 	expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
1024aef22f2dSAlan Somers 
1025aef22f2dSAlan Somers 	fd = open(FULLPATH, O_RDWR);
1026aef22f2dSAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1027aef22f2dSAlan Somers 
1028aef22f2dSAlan Somers 	/* Write and cache data beyond EOF */
1029aef22f2dSAlan Somers 	ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
1030aef22f2dSAlan Somers 		<< strerror(errno);
1031aef22f2dSAlan Somers 
1032aef22f2dSAlan Somers 	/* Read from the old EOF */
1033aef22f2dSAlan Somers 	r = pread(fd, readbuf, rbufsize, 0);
1034aef22f2dSAlan Somers 	ASSERT_LE(0, r) << strerror(errno);
1035aef22f2dSAlan Somers 	EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
1036aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
1037aef22f2dSAlan Somers 	bzero(hole, holesize);
1038aef22f2dSAlan Somers 	EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
1039aef22f2dSAlan Somers 
1040aef22f2dSAlan Somers 	/* The file's size should still be what was established by pwrite */
1041aef22f2dSAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1042aef22f2dSAlan Somers 	EXPECT_EQ(offset + wbufsize, sb.st_size);
1043aef22f2dSAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
1044aef22f2dSAlan Somers }
1045aef22f2dSAlan Somers 
1046aef22f2dSAlan Somers /*
1047788af953SAlan Somers  * When a file has dirty writes that haven't been flushed, the server's notion
1048788af953SAlan Somers  * of its mtime and ctime will be wrong.  The kernel should ignore those if it
1049788af953SAlan Somers  * gets them from a FUSE_GETATTR before flushing.
1050788af953SAlan Somers  */
1051788af953SAlan Somers TEST_F(WriteBackAsync, timestamps)
1052788af953SAlan Somers {
1053788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1054788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1055788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1056788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1057788af953SAlan Somers 	uint64_t ino = 42;
1058788af953SAlan Somers 	uint64_t attr_valid = 0;
1059788af953SAlan Somers 	uint64_t attr_valid_nsec = 0;
1060788af953SAlan Somers 	uint64_t server_time = 12345;
1061788af953SAlan Somers 	mode_t mode = S_IFREG | 0644;
1062788af953SAlan Somers 	int fd;
1063788af953SAlan Somers 
1064788af953SAlan Somers 	struct stat sb;
1065788af953SAlan Somers 
1066788af953SAlan Somers 	EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
1067788af953SAlan Somers 	.WillRepeatedly(Invoke(
1068788af953SAlan Somers 		ReturnImmediate([=](auto in __unused, auto& out) {
1069788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, entry);
1070788af953SAlan Somers 		out.body.entry.attr.mode = mode;
1071788af953SAlan Somers 		out.body.entry.nodeid = ino;
1072788af953SAlan Somers 		out.body.entry.attr.nlink = 1;
1073788af953SAlan Somers 		out.body.entry.attr_valid = attr_valid;
1074788af953SAlan Somers 		out.body.entry.attr_valid_nsec = attr_valid_nsec;
1075788af953SAlan Somers 	})));
1076788af953SAlan Somers 	expect_open(ino, 0, 1);
1077788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1078788af953SAlan Somers 		ResultOf([=](auto in) {
1079788af953SAlan Somers 			return (in.header.opcode == FUSE_GETATTR &&
1080788af953SAlan Somers 				in.header.nodeid == ino);
1081788af953SAlan Somers 		}, Eq(true)),
1082788af953SAlan Somers 		_)
1083788af953SAlan Somers 	).WillRepeatedly(Invoke(
1084788af953SAlan Somers 	ReturnImmediate([=](auto i __unused, auto& out) {
1085788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1086788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1087788af953SAlan Somers 		out.body.attr.attr.mode = mode;
1088788af953SAlan Somers 		out.body.attr.attr_valid = attr_valid;
1089788af953SAlan Somers 		out.body.attr.attr_valid_nsec = attr_valid_nsec;
1090788af953SAlan Somers 		out.body.attr.attr.atime = server_time;
1091788af953SAlan Somers 		out.body.attr.attr.mtime = server_time;
1092788af953SAlan Somers 		out.body.attr.attr.ctime = server_time;
1093788af953SAlan Somers 	})));
1094788af953SAlan Somers 
1095788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1096788af953SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1097788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1098788af953SAlan Somers 
1099788af953SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1100788af953SAlan Somers 	EXPECT_EQ((time_t)server_time, sb.st_atime);
1101788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_mtime);
1102788af953SAlan Somers 	EXPECT_NE((time_t)server_time, sb.st_ctime);
1103788af953SAlan Somers }
1104788af953SAlan Somers 
1105788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */
1106788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr)
1107788af953SAlan Somers {
1108788af953SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1109788af953SAlan Somers 	const char RELPATH[] = "some_file.txt";
1110788af953SAlan Somers 	const char *CONTENTS = "abcdefgh";
1111788af953SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1112788af953SAlan Somers 	uint64_t ino = 42;
1113788af953SAlan Somers 	const mode_t newmode = 0755;
1114788af953SAlan Somers 	int fd;
1115788af953SAlan Somers 
1116788af953SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1117788af953SAlan Somers 	expect_open(ino, 0, 1);
1118788af953SAlan Somers 	EXPECT_CALL(*m_mock, process(
1119788af953SAlan Somers 		ResultOf([=](auto in) {
1120*0a8fe2d3SAlan Somers 			uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1121788af953SAlan Somers 			return (in.header.opcode == FUSE_SETATTR &&
1122788af953SAlan Somers 				in.header.nodeid == ino &&
1123788af953SAlan Somers 				in.body.setattr.valid == valid);
1124788af953SAlan Somers 		}, Eq(true)),
1125788af953SAlan Somers 		_)
1126788af953SAlan Somers 	).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1127788af953SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
1128788af953SAlan Somers 		out.body.attr.attr.ino = ino;
1129788af953SAlan Somers 		out.body.attr.attr.mode = S_IFREG | newmode;
1130788af953SAlan Somers 	})));
1131788af953SAlan Somers 
1132788af953SAlan Somers 	fd = open(FULLPATH, O_RDWR);
1133788af953SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1134788af953SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1135788af953SAlan Somers 	ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
1136788af953SAlan Somers }
1137788af953SAlan Somers 
1138788af953SAlan Somers /*
11399821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
11409821f1d3SAlan Somers  */
1141b5aaf286SAlan Somers TEST_F(WriteThrough, writethrough)
11429821f1d3SAlan Somers {
11439821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
11449821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
11459821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
11469821f1d3SAlan Somers 	uint64_t ino = 42;
11479821f1d3SAlan Somers 	int fd;
11489821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
11499821f1d3SAlan Somers 	char readbuf[bufsize];
11509821f1d3SAlan Somers 
11519821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
11529821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1153bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
11549821f1d3SAlan Somers 
11559821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
11569821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
11579821f1d3SAlan Somers 
11589821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
11599821f1d3SAlan Somers 	/*
11609821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
11619821f1d3SAlan Somers 	 * filesystem daemon
11629821f1d3SAlan Somers 	 */
1163b5aaf286SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
11649821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
11659821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
11669821f1d3SAlan Somers }
11679821f1d3SAlan Somers 
11680d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */
11690d3a88d7SAlan Somers TEST_F(Write, update_file_size)
11709821f1d3SAlan Somers {
11719821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
11729821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
11739821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
11749821f1d3SAlan Somers 	struct stat sb;
11759821f1d3SAlan Somers 	uint64_t ino = 42;
11769821f1d3SAlan Somers 	int fd;
11779821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
11789821f1d3SAlan Somers 
11799821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
11809821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1181bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
11829821f1d3SAlan Somers 
11839821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
11849821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
11859821f1d3SAlan Somers 
11869821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
11879821f1d3SAlan Somers 	/* Get cached attributes */
11889821f1d3SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
11899821f1d3SAlan Somers 	ASSERT_EQ(bufsize, sb.st_size);
11909821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
11919821f1d3SAlan Somers }
1192