xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision 8eecd9ce05ee9744ebf9ecd0c2d1d2431b9fe06c)
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" {
32*8eecd9ceSAlan 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 
939821f1d3SAlan Somers };
949821f1d3SAlan Somers 
95a639731bSAlan Somers sig_atomic_t Write::s_sigxfsz = 0;
96a639731bSAlan Somers 
9716bd2d47SAlan Somers class Write_7_8: public FuseTest {
9816bd2d47SAlan Somers 
9916bd2d47SAlan Somers public:
10016bd2d47SAlan Somers virtual void SetUp() {
10116bd2d47SAlan Somers 	m_kernel_minor_version = 8;
10216bd2d47SAlan Somers 	FuseTest::SetUp();
10316bd2d47SAlan Somers }
10416bd2d47SAlan Somers 
10516bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
10616bd2d47SAlan Somers {
10716bd2d47SAlan Somers 	FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
10816bd2d47SAlan Somers }
10916bd2d47SAlan Somers 
11016bd2d47SAlan Somers };
11116bd2d47SAlan Somers 
1129821f1d3SAlan Somers class AioWrite: public Write {
1139821f1d3SAlan Somers virtual void SetUp() {
1149821f1d3SAlan Somers 	const char *node = "vfs.aio.enable_unsafe";
1159821f1d3SAlan Somers 	int val = 0;
1169821f1d3SAlan Somers 	size_t size = sizeof(val);
1179821f1d3SAlan Somers 
1189821f1d3SAlan Somers 	FuseTest::SetUp();
1199821f1d3SAlan Somers 
1209821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
1219821f1d3SAlan Somers 		<< strerror(errno);
1229821f1d3SAlan Somers 	if (!val)
1239821f1d3SAlan Somers 		GTEST_SKIP() <<
1249821f1d3SAlan Somers 			"vfs.aio.enable_unsafe must be set for this test";
1259821f1d3SAlan Somers }
1269821f1d3SAlan Somers };
1279821f1d3SAlan Somers 
1289821f1d3SAlan Somers /* Tests for the write-through cache mode */
1299821f1d3SAlan Somers class WriteThrough: public Write {
130bda39894SAlan Somers public:
1319821f1d3SAlan Somers virtual void SetUp() {
1329821f1d3SAlan Somers 	const char *cache_mode_node = "vfs.fusefs.data_cache_mode";
1339821f1d3SAlan Somers 	int val = 0;
1349821f1d3SAlan Somers 	size_t size = sizeof(val);
1359821f1d3SAlan Somers 
1369821f1d3SAlan Somers 	FuseTest::SetUp();
1379821f1d3SAlan Somers 	if (IsSkipped())
1389821f1d3SAlan Somers 		return;
1399821f1d3SAlan Somers 
1409821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0))
1419821f1d3SAlan Somers 		<< strerror(errno);
1429821f1d3SAlan Somers 	if (val != 1)
1439821f1d3SAlan Somers 		GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 "
1449821f1d3SAlan Somers 			"(writethrough) for this test";
1459821f1d3SAlan Somers }
1469821f1d3SAlan Somers 
147bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
148bda39894SAlan Somers 	uint64_t osize, const void *contents)
149bda39894SAlan Somers {
150bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, 0, FUSE_WRITE_CACHE,
151bda39894SAlan Somers 		contents);
152bda39894SAlan Somers }
1539821f1d3SAlan Somers };
1549821f1d3SAlan Somers 
1559821f1d3SAlan Somers /* Tests for the writeback cache mode */
1569821f1d3SAlan Somers class WriteBack: public Write {
157bda39894SAlan Somers public:
1589821f1d3SAlan Somers virtual void SetUp() {
1599821f1d3SAlan Somers 	const char *node = "vfs.fusefs.data_cache_mode";
1609821f1d3SAlan Somers 	int val = 0;
1619821f1d3SAlan Somers 	size_t size = sizeof(val);
1629821f1d3SAlan Somers 
1639821f1d3SAlan Somers 	FuseTest::SetUp();
1649821f1d3SAlan Somers 	if (IsSkipped())
1659821f1d3SAlan Somers 		return;
1669821f1d3SAlan Somers 
1679821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
1689821f1d3SAlan Somers 		<< strerror(errno);
1699821f1d3SAlan Somers 	if (val != 2)
1709821f1d3SAlan Somers 		GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 "
1719821f1d3SAlan Somers 			"(writeback) for this test";
1729821f1d3SAlan Somers }
1739821f1d3SAlan Somers 
174bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
175bda39894SAlan Somers 	uint64_t osize, const void *contents)
176bda39894SAlan Somers {
177bda39894SAlan Somers 	FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
178bda39894SAlan Somers 		contents);
179bda39894SAlan Somers }
1809821f1d3SAlan Somers };
1819821f1d3SAlan Somers 
182*8eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */
183*8eecd9ceSAlan Somers class WriteCluster: public WriteBack {
184*8eecd9ceSAlan Somers public:
185*8eecd9ceSAlan Somers virtual void SetUp() {
186*8eecd9ceSAlan Somers 	if (MAXPHYS < 2 * DFLTPHYS)
187*8eecd9ceSAlan Somers 		GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
188*8eecd9ceSAlan Somers 			<< "for this test";
189*8eecd9ceSAlan Somers 	m_async = true;
190*8eecd9ceSAlan Somers 	m_maxwrite = MAXPHYS;
191*8eecd9ceSAlan Somers 	WriteBack::SetUp();
192*8eecd9ceSAlan Somers }
193*8eecd9ceSAlan Somers };
194*8eecd9ceSAlan 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;
2219821f1d3SAlan Somers 	iocb.aio_buf = (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);
2269821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
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);
2629821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
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);
281a87e0831SAlan Somers 	ASSERT_NE(NULL, oldcontents) << strerror(errno);
282a87e0831SAlan Somers 	oldbuf = (char*)malloc(oldsize);
283a87e0831SAlan Somers 	ASSERT_NE(NULL, 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);
288a87e0831SAlan Somers 	expect_write(ino, oldsize, BUFSIZE, 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);
300a87e0831SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
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);
3219821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
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 
3599821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
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);
3899821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
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);
41412292a99SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
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 
4439821f1d3SAlan Somers 	iov[0].iov_base = (void*)CONTENTS0;
4449821f1d3SAlan Somers 	iov[0].iov_len = strlen(CONTENTS0);
4459821f1d3SAlan Somers 	iov[1].iov_base = (void*)CONTENTS1;
4469821f1d3SAlan Somers 	iov[1].iov_len = strlen(CONTENTS1);
44712292a99SAlan Somers 	ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
4489821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
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);
478a639731bSAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
479a639731bSAlan Somers }
480a639731bSAlan Somers 
4819821f1d3SAlan Somers /*
4829821f1d3SAlan Somers  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
4839821f1d3SAlan Somers  * write, then it must set the FUSE_WRITE_CACHE bit
4849821f1d3SAlan Somers  */
4859821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
4869821f1d3SAlan Somers // TODO: check vfs.fusefs.mmap_enable
487cf437e2aSAlan Somers TEST_F(Write, mmap)
4889821f1d3SAlan Somers {
4899821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4909821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4919821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
4929821f1d3SAlan Somers 	uint64_t ino = 42;
4939821f1d3SAlan Somers 	int fd;
4949821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
4959821f1d3SAlan Somers 	void *p;
4969821f1d3SAlan Somers 	uint64_t offset = 10;
4979821f1d3SAlan Somers 	size_t len;
4989821f1d3SAlan Somers 	void *zeros, *expected;
4999821f1d3SAlan Somers 
5009821f1d3SAlan Somers 	len = getpagesize();
5019821f1d3SAlan Somers 
5029821f1d3SAlan Somers 	zeros = calloc(1, len);
5039821f1d3SAlan Somers 	ASSERT_NE(NULL, zeros);
5049821f1d3SAlan Somers 	expected = calloc(1, len);
5059821f1d3SAlan Somers 	ASSERT_NE(NULL, expected);
5069821f1d3SAlan Somers 	memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
5079821f1d3SAlan Somers 
5089821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, len);
5099821f1d3SAlan Somers 	expect_open(ino, 0, 1);
5109821f1d3SAlan Somers 	expect_read(ino, 0, len, len, zeros);
5119821f1d3SAlan Somers 	/*
5129821f1d3SAlan Somers 	 * Writes from the pager may or may not be associated with the correct
513cf437e2aSAlan Somers 	 * pid, so they must set FUSE_WRITE_CACHE.
5149821f1d3SAlan Somers 	 */
515bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
5169f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
5179821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
5189821f1d3SAlan Somers 
5199821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
5209821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
5219821f1d3SAlan Somers 
5229821f1d3SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
5239821f1d3SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
5249821f1d3SAlan Somers 
5259821f1d3SAlan Somers 	memmove((uint8_t*)p + offset, CONTENTS, bufsize);
5269821f1d3SAlan Somers 
5279821f1d3SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
5289821f1d3SAlan Somers 	close(fd);	// Write mmap'd data on close
5299821f1d3SAlan Somers 
5309821f1d3SAlan Somers 	free(expected);
5319821f1d3SAlan Somers 	free(zeros);
5329821f1d3SAlan Somers }
5339821f1d3SAlan Somers 
5346af6fdceSAlan Somers /* In WriteThrough mode, a write should evict overlapping cached data */
5356af6fdceSAlan Somers TEST_F(WriteThrough, evicts_read_cache)
5366af6fdceSAlan Somers {
5376af6fdceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5386af6fdceSAlan Somers 	const char RELPATH[] = "some_file.txt";
53975d5cb29SAlan Somers 	ssize_t bufsize = 65536;
54075d5cb29SAlan Somers 	/* End the write in the middle of a page */
54175d5cb29SAlan Somers 	ssize_t wrsize = bufsize - 1000;
54275d5cb29SAlan Somers 	char *contents0, *contents1, *readbuf, *expected;
5436af6fdceSAlan Somers 	uint64_t ino = 42;
5446af6fdceSAlan Somers 	int fd;
54575d5cb29SAlan Somers 
54675d5cb29SAlan Somers 	contents0 = (char*)malloc(bufsize);
54775d5cb29SAlan Somers 	memset(contents0, 'X', bufsize);
54875d5cb29SAlan Somers 	contents0[bufsize - 1] = '\0';	// Null-terminate
54975d5cb29SAlan Somers 	contents1 = (char*)malloc(wrsize);
55075d5cb29SAlan Somers 	memset(contents1, 'Y', wrsize);
55175d5cb29SAlan Somers 	readbuf = (char*)calloc(bufsize, 1);
55275d5cb29SAlan Somers 	expected = (char*)malloc(bufsize);
55375d5cb29SAlan Somers 	memset(expected, 'Y', wrsize);
55475d5cb29SAlan Somers 	memset(expected + wrsize, 'X', bufsize - wrsize);
55575d5cb29SAlan Somers 	expected[bufsize - 1] = '\0';	// Null-terminate
5566af6fdceSAlan Somers 
5576af6fdceSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
5586af6fdceSAlan Somers 	expect_open(ino, 0, 1);
55975d5cb29SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, contents0);
560bda39894SAlan Somers 	expect_write(ino, 0, wrsize, wrsize, contents1);
5616af6fdceSAlan Somers 
5626af6fdceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
5636af6fdceSAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
5646af6fdceSAlan Somers 
5656af6fdceSAlan Somers 	// Prime cache
5666af6fdceSAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
5676af6fdceSAlan Somers 
5686af6fdceSAlan Somers 	// Write directly, evicting cache
5696af6fdceSAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
57075d5cb29SAlan Somers 	ASSERT_EQ(wrsize, write(fd, contents1, wrsize)) << strerror(errno);
5716af6fdceSAlan Somers 
5726af6fdceSAlan Somers 	// Read again.  Cache should be bypassed
57375d5cb29SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, expected);
5746af6fdceSAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
5756af6fdceSAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
57675d5cb29SAlan Somers 	ASSERT_STREQ(readbuf, expected);
5776af6fdceSAlan Somers 
5786af6fdceSAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
5796af6fdceSAlan Somers }
5806af6fdceSAlan Somers 
5815fccbf31SAlan Somers TEST_F(WriteThrough, pwrite)
5829821f1d3SAlan Somers {
5839821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5849821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5859821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
5869821f1d3SAlan Somers 	uint64_t ino = 42;
5879821f1d3SAlan Somers 	uint64_t offset = 4096;
5889821f1d3SAlan Somers 	int fd;
5899821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5909821f1d3SAlan Somers 
5919821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
5929821f1d3SAlan Somers 	expect_open(ino, 0, 1);
593bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
5949821f1d3SAlan Somers 
5959821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
5969821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
5979821f1d3SAlan Somers 
5989821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
5999821f1d3SAlan Somers 		<< strerror(errno);
6009821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6019821f1d3SAlan Somers }
6029821f1d3SAlan Somers 
6039821f1d3SAlan Somers TEST_F(Write, write)
6049821f1d3SAlan Somers {
6059821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6069821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6079821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6089821f1d3SAlan Somers 	uint64_t ino = 42;
6099821f1d3SAlan Somers 	int fd;
6109821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6119821f1d3SAlan Somers 
6129821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6139821f1d3SAlan Somers 	expect_open(ino, 0, 1);
614bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
6159821f1d3SAlan Somers 
6169821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
6179821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6189821f1d3SAlan Somers 
6199821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6209821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6219821f1d3SAlan Somers }
6229821f1d3SAlan Somers 
6239821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */
6249821f1d3SAlan Somers TEST_F(Write, write_large)
6259821f1d3SAlan Somers {
6269821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6279821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6289821f1d3SAlan Somers 	int *contents;
6299821f1d3SAlan Somers 	uint64_t ino = 42;
6309821f1d3SAlan Somers 	int fd;
6319821f1d3SAlan Somers 	ssize_t halfbufsize, bufsize;
6329821f1d3SAlan Somers 
633*8eecd9ceSAlan Somers 	halfbufsize = m_mock->m_maxwrite;
6349821f1d3SAlan Somers 	bufsize = halfbufsize * 2;
6359821f1d3SAlan Somers 	contents = (int*)malloc(bufsize);
6369821f1d3SAlan Somers 	ASSERT_NE(NULL, contents);
6379821f1d3SAlan Somers 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
6389821f1d3SAlan Somers 		contents[i] = i;
6399821f1d3SAlan Somers 	}
6409821f1d3SAlan Somers 
6419821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6429821f1d3SAlan Somers 	expect_open(ino, 0, 1);
643bda39894SAlan Somers 	expect_write(ino, 0, halfbufsize, halfbufsize, contents);
644bda39894SAlan Somers 	expect_write(ino, halfbufsize, halfbufsize, halfbufsize,
6459821f1d3SAlan Somers 		&contents[halfbufsize / sizeof(int)]);
6469821f1d3SAlan Somers 
6479821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
6489821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6499821f1d3SAlan Somers 
6509821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
6519821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6529821f1d3SAlan Somers 
6539821f1d3SAlan Somers 	free(contents);
6549821f1d3SAlan Somers }
6559821f1d3SAlan Somers 
6569821f1d3SAlan Somers TEST_F(Write, write_nothing)
6579821f1d3SAlan Somers {
6589821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6599821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6609821f1d3SAlan Somers 	const char *CONTENTS = "";
6619821f1d3SAlan Somers 	uint64_t ino = 42;
6629821f1d3SAlan Somers 	int fd;
6639821f1d3SAlan Somers 	ssize_t bufsize = 0;
6649821f1d3SAlan Somers 
6659821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6669821f1d3SAlan Somers 	expect_open(ino, 0, 1);
6679821f1d3SAlan Somers 
6689821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
6699821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6709821f1d3SAlan Somers 
6719821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6729821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6739821f1d3SAlan Somers }
6749821f1d3SAlan Somers 
67516bd2d47SAlan Somers TEST_F(Write_7_8, write)
67616bd2d47SAlan Somers {
67716bd2d47SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
67816bd2d47SAlan Somers 	const char RELPATH[] = "some_file.txt";
67916bd2d47SAlan Somers 	const char *CONTENTS = "abcdefgh";
68016bd2d47SAlan Somers 	uint64_t ino = 42;
68116bd2d47SAlan Somers 	int fd;
68216bd2d47SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
68316bd2d47SAlan Somers 
68416bd2d47SAlan Somers 	expect_lookup(RELPATH, ino, 0);
68516bd2d47SAlan Somers 	expect_open(ino, 0, 1);
686bda39894SAlan Somers 	expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
68716bd2d47SAlan Somers 
68816bd2d47SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
68916bd2d47SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
69016bd2d47SAlan Somers 
69116bd2d47SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
69216bd2d47SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
69316bd2d47SAlan Somers }
69416bd2d47SAlan Somers 
6959821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */
6969821f1d3SAlan Somers TEST_F(WriteBack, close)
6979821f1d3SAlan Somers {
6989821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6999821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7009821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7019821f1d3SAlan Somers 	uint64_t ino = 42;
7029821f1d3SAlan Somers 	int fd;
7039821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7049821f1d3SAlan Somers 
7059821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7069821f1d3SAlan Somers 	expect_open(ino, 0, 1);
707bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
7089821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
7099821f1d3SAlan Somers 		ResultOf([=](auto in) {
71029edc611SAlan Somers 			return (in.header.opcode == FUSE_SETATTR);
7119821f1d3SAlan Somers 		}, Eq(true)),
7129821f1d3SAlan Somers 		_)
71329edc611SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
7149821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
71529edc611SAlan Somers 		out.body.attr.attr.ino = ino;	// Must match nodeid
7169821f1d3SAlan Somers 	})));
7179f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
7189821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
7199821f1d3SAlan Somers 
7209821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
7219821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
7229821f1d3SAlan Somers 
7239821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7249821f1d3SAlan Somers 	close(fd);
7259821f1d3SAlan Somers }
7269821f1d3SAlan Somers 
727*8eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */
728*8eecd9ceSAlan Somers TEST_F(WriteCluster, clustering)
729*8eecd9ceSAlan Somers {
730*8eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
731*8eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
732*8eecd9ceSAlan Somers 	uint64_t ino = 42;
733*8eecd9ceSAlan Somers 	int i, fd;
734*8eecd9ceSAlan Somers 	void *wbuf, *wbuf2x;
735*8eecd9ceSAlan Somers 	ssize_t bufsize = 65536;
736*8eecd9ceSAlan Somers 	off_t filesize = 327680;
737*8eecd9ceSAlan Somers 
738*8eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
739*8eecd9ceSAlan Somers 	ASSERT_NE(NULL, wbuf) << strerror(errno);
740*8eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
741*8eecd9ceSAlan Somers 	wbuf2x = malloc(2 * bufsize);
742*8eecd9ceSAlan Somers 	ASSERT_NE(NULL, wbuf2x) << strerror(errno);
743*8eecd9ceSAlan Somers 	memset(wbuf2x, 'X', 2 * bufsize);
744*8eecd9ceSAlan Somers 
745*8eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
746*8eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
747*8eecd9ceSAlan Somers 	/*
748*8eecd9ceSAlan Somers 	 * Writes of bufsize-bytes each should be clustered into greater sizes.
749*8eecd9ceSAlan Somers 	 * The amount of clustering is adaptive, so the first write actually
750*8eecd9ceSAlan Somers 	 * issued will be 2x bufsize and subsequent writes may be larger
751*8eecd9ceSAlan Somers 	 */
752*8eecd9ceSAlan Somers 	expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
753*8eecd9ceSAlan Somers 	expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
754*8eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
755*8eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
756*8eecd9ceSAlan Somers 
757*8eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
758*8eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
759*8eecd9ceSAlan Somers 
760*8eecd9ceSAlan Somers 	for (i = 0; i < 4; i++) {
761*8eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
762*8eecd9ceSAlan Somers 			<< strerror(errno);
763*8eecd9ceSAlan Somers 	}
764*8eecd9ceSAlan Somers 	close(fd);
765*8eecd9ceSAlan Somers }
766*8eecd9ceSAlan Somers 
767*8eecd9ceSAlan Somers /*
768*8eecd9ceSAlan Somers  * When clustering writes, an I/O error to any of the cluster's children should
769*8eecd9ceSAlan Somers  * not panic the system on unmount
770*8eecd9ceSAlan Somers  */
771*8eecd9ceSAlan Somers /*
772*8eecd9ceSAlan Somers  * Disabled because it panics.
773*8eecd9ceSAlan Somers  * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
774*8eecd9ceSAlan Somers  */
775*8eecd9ceSAlan Somers TEST_F(WriteCluster, DISABLED_cluster_write_err)
776*8eecd9ceSAlan Somers {
777*8eecd9ceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
778*8eecd9ceSAlan Somers 	const char RELPATH[] = "some_file.txt";
779*8eecd9ceSAlan Somers 	uint64_t ino = 42;
780*8eecd9ceSAlan Somers 	int i, fd;
781*8eecd9ceSAlan Somers 	void *wbuf;
782*8eecd9ceSAlan Somers 	ssize_t bufsize = 65536;
783*8eecd9ceSAlan Somers 	off_t filesize = 262144;
784*8eecd9ceSAlan Somers 
785*8eecd9ceSAlan Somers 	wbuf = malloc(bufsize);
786*8eecd9ceSAlan Somers 	ASSERT_NE(NULL, wbuf) << strerror(errno);
787*8eecd9ceSAlan Somers 	memset(wbuf, 'X', bufsize);
788*8eecd9ceSAlan Somers 
789*8eecd9ceSAlan Somers 	expect_lookup(RELPATH, ino, filesize);
790*8eecd9ceSAlan Somers 	expect_open(ino, 0, 1);
791*8eecd9ceSAlan Somers 	EXPECT_CALL(*m_mock, process(
792*8eecd9ceSAlan Somers 		ResultOf([=](auto in) {
793*8eecd9ceSAlan Somers 			return (in.header.opcode == FUSE_WRITE);
794*8eecd9ceSAlan Somers 		}, Eq(true)),
795*8eecd9ceSAlan Somers 		_)
796*8eecd9ceSAlan Somers 	).WillRepeatedly(Invoke(ReturnErrno(EIO)));
797*8eecd9ceSAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
798*8eecd9ceSAlan Somers 	expect_release(ino, ReturnErrno(0));
799*8eecd9ceSAlan Somers 
800*8eecd9ceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
801*8eecd9ceSAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
802*8eecd9ceSAlan Somers 
803*8eecd9ceSAlan Somers 	for (i = 0; i < 3; i++) {
804*8eecd9ceSAlan Somers 		ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
805*8eecd9ceSAlan Somers 			<< strerror(errno);
806*8eecd9ceSAlan Somers 	}
807*8eecd9ceSAlan Somers 	close(fd);
808*8eecd9ceSAlan Somers }
809*8eecd9ceSAlan Somers 
8109821f1d3SAlan Somers /*
8115fccbf31SAlan Somers  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
8125fccbf31SAlan Somers  * server.  The FUSE protocol explicitly allows that.
8135fccbf31SAlan Somers  */
8145fccbf31SAlan Somers TEST_F(WriteBack, rmw)
8155fccbf31SAlan Somers {
8165fccbf31SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8175fccbf31SAlan Somers 	const char RELPATH[] = "some_file.txt";
8185fccbf31SAlan Somers 	const char *CONTENTS = "abcdefgh";
8195fccbf31SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
8205fccbf31SAlan Somers 	uint64_t ino = 42;
8215fccbf31SAlan Somers 	uint64_t offset = 1;
8225fccbf31SAlan Somers 	off_t fsize = 10;
8235fccbf31SAlan Somers 	int fd;
8245fccbf31SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8255fccbf31SAlan Somers 
826cad67791SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
8275fccbf31SAlan Somers 	expect_open(ino, 0, 1);
828d4fd0c81SAlan Somers 	expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
829bda39894SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, CONTENTS);
8305fccbf31SAlan Somers 
8315fccbf31SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
8325fccbf31SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
8335fccbf31SAlan Somers 
8345fccbf31SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
8355fccbf31SAlan Somers 		<< strerror(errno);
8365fccbf31SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
8375fccbf31SAlan Somers }
8385fccbf31SAlan Somers 
8395fccbf31SAlan Somers /*
8409821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
8419821f1d3SAlan Somers  */
8429821f1d3SAlan Somers TEST_F(WriteBack, writeback)
8439821f1d3SAlan Somers {
8449821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8459821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8469821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
8479821f1d3SAlan Somers 	uint64_t ino = 42;
8489821f1d3SAlan Somers 	int fd;
8499821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8509821f1d3SAlan Somers 	char readbuf[bufsize];
8519821f1d3SAlan Somers 
8529821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
8539821f1d3SAlan Somers 	expect_open(ino, 0, 1);
854bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
8559821f1d3SAlan Somers 
8569821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
8579821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
8589821f1d3SAlan Somers 
8599821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
8609821f1d3SAlan Somers 	/*
8619821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
8629821f1d3SAlan Somers 	 * filesystem daemon
8639821f1d3SAlan Somers 	 */
8649821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
8659821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
8669821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
8679821f1d3SAlan Somers }
8689821f1d3SAlan Somers 
8699821f1d3SAlan Somers /*
8709821f1d3SAlan Somers  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
8719821f1d3SAlan Somers  * an odd test, because it would be unusual to use O_DIRECT for writes but not
8729821f1d3SAlan Somers  * reads.
8739821f1d3SAlan Somers  */
8749821f1d3SAlan Somers TEST_F(WriteBack, o_direct)
8759821f1d3SAlan Somers {
8769821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
8779821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
8789821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
8799821f1d3SAlan Somers 	uint64_t ino = 42;
8809821f1d3SAlan Somers 	int fd;
8819821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
8829821f1d3SAlan Somers 	char readbuf[bufsize];
8839821f1d3SAlan Somers 
8849821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
8859821f1d3SAlan Somers 	expect_open(ino, 0, 1);
886bda39894SAlan Somers 	FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
887bda39894SAlan Somers 		CONTENTS);
8889821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
8899821f1d3SAlan Somers 
8909821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
8919821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
8929821f1d3SAlan Somers 
8939821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
8949821f1d3SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
8959821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
8969821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
8979821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
8989821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
8999821f1d3SAlan Somers }
9009821f1d3SAlan Somers 
9019821f1d3SAlan Somers /*
9029821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
9039821f1d3SAlan Somers  */
9049821f1d3SAlan Somers /*
9059821f1d3SAlan Somers  * Disabled because we don't yet implement write-through caching.  No bugzilla
9069821f1d3SAlan Somers  * entry, because that's a feature request, not a bug.
9079821f1d3SAlan Somers  */
9089821f1d3SAlan Somers TEST_F(WriteThrough, DISABLED_writethrough)
9099821f1d3SAlan Somers {
9109821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9119821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
9129821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
9139821f1d3SAlan Somers 	uint64_t ino = 42;
9149821f1d3SAlan Somers 	int fd;
9159821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
9169821f1d3SAlan Somers 	char readbuf[bufsize];
9179821f1d3SAlan Somers 
9189821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
9199821f1d3SAlan Somers 	expect_open(ino, 0, 1);
920bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
9219821f1d3SAlan Somers 
9229821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
9239821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
9249821f1d3SAlan Somers 
9259821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9269821f1d3SAlan Somers 	/*
9279821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
9289821f1d3SAlan Somers 	 * filesystem daemon
9299821f1d3SAlan Somers 	 */
9309821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
9319821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
9329821f1d3SAlan Somers }
9339821f1d3SAlan Somers 
9349821f1d3SAlan Somers /* With writethrough caching, writes update the cached file size */
935cad67791SAlan Somers TEST_F(WriteThrough, update_file_size)
9369821f1d3SAlan Somers {
9379821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
9389821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
9399821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
9409821f1d3SAlan Somers 	struct stat sb;
9419821f1d3SAlan Somers 	uint64_t ino = 42;
9429821f1d3SAlan Somers 	int fd;
9439821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
9449821f1d3SAlan Somers 
9459821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
9469821f1d3SAlan Somers 	expect_open(ino, 0, 1);
947bda39894SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, CONTENTS);
9489821f1d3SAlan Somers 
9499821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
9509821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
9519821f1d3SAlan Somers 
9529821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9539821f1d3SAlan Somers 	/* Get cached attributes */
9549821f1d3SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
9559821f1d3SAlan Somers 	ASSERT_EQ(bufsize, sb.st_size);
9569821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
9579821f1d3SAlan Somers }
958