xref: /freebsd/tests/sys/fs/fusefs/write.cc (revision 6af6fdcea7147dd102ec36365dd774838650616c)
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" {
329821f1d3SAlan Somers #include <sys/types.h>
339821f1d3SAlan Somers #include <sys/mman.h>
349821f1d3SAlan Somers #include <sys/stat.h>
359821f1d3SAlan Somers #include <sys/sysctl.h>
369821f1d3SAlan Somers #include <sys/uio.h>
379821f1d3SAlan Somers 
389821f1d3SAlan Somers #include <aio.h>
399821f1d3SAlan Somers #include <fcntl.h>
409821f1d3SAlan Somers #include <unistd.h>
419821f1d3SAlan Somers }
429821f1d3SAlan Somers 
439821f1d3SAlan Somers #include "mockfs.hh"
449821f1d3SAlan Somers #include "utils.hh"
459821f1d3SAlan Somers 
469821f1d3SAlan Somers using namespace testing;
479821f1d3SAlan Somers 
489821f1d3SAlan Somers class Write: public FuseTest {
499821f1d3SAlan Somers 
509821f1d3SAlan Somers public:
519821f1d3SAlan Somers 
529821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
539821f1d3SAlan Somers {
549821f1d3SAlan Somers 	FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
559821f1d3SAlan Somers }
569821f1d3SAlan Somers 
579821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r)
589821f1d3SAlan Somers {
599821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
609821f1d3SAlan Somers 		ResultOf([=](auto in) {
619821f1d3SAlan Somers 			return (in->header.opcode == FUSE_RELEASE &&
629821f1d3SAlan Somers 				in->header.nodeid == ino);
639821f1d3SAlan Somers 		}, Eq(true)),
649821f1d3SAlan Somers 		_)
659821f1d3SAlan Somers 	).WillRepeatedly(Invoke(r));
669821f1d3SAlan Somers }
679821f1d3SAlan Somers 
689821f1d3SAlan Somers void require_sync_resize_0() {
699821f1d3SAlan Somers 	const char *sync_resize_node = "vfs.fusefs.sync_resize";
709821f1d3SAlan Somers 	int val = 0;
719821f1d3SAlan Somers 	size_t size = sizeof(val);
729821f1d3SAlan Somers 
739821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(sync_resize_node, &val, &size, NULL, 0))
749821f1d3SAlan Somers 		<< strerror(errno);
759821f1d3SAlan Somers 	if (val != 0)
769821f1d3SAlan Somers 		GTEST_SKIP() <<
779821f1d3SAlan Somers 			"vfs.fusefs.sync_resize must be set to 0 for this test."
789821f1d3SAlan Somers 			"  That sysctl will probably be removed soon.";
799821f1d3SAlan Somers }
809821f1d3SAlan Somers 
819821f1d3SAlan Somers };
829821f1d3SAlan Somers 
839821f1d3SAlan Somers class AioWrite: public Write {
849821f1d3SAlan Somers virtual void SetUp() {
859821f1d3SAlan Somers 	const char *node = "vfs.aio.enable_unsafe";
869821f1d3SAlan Somers 	int val = 0;
879821f1d3SAlan Somers 	size_t size = sizeof(val);
889821f1d3SAlan Somers 
899821f1d3SAlan Somers 	FuseTest::SetUp();
909821f1d3SAlan Somers 
919821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
929821f1d3SAlan Somers 		<< strerror(errno);
939821f1d3SAlan Somers 	if (!val)
949821f1d3SAlan Somers 		GTEST_SKIP() <<
959821f1d3SAlan Somers 			"vfs.aio.enable_unsafe must be set for this test";
969821f1d3SAlan Somers }
979821f1d3SAlan Somers };
989821f1d3SAlan Somers 
999821f1d3SAlan Somers /* Tests for the write-through cache mode */
1009821f1d3SAlan Somers class WriteThrough: public Write {
1019821f1d3SAlan Somers 
1029821f1d3SAlan Somers virtual void SetUp() {
1039821f1d3SAlan Somers 	const char *cache_mode_node = "vfs.fusefs.data_cache_mode";
1049821f1d3SAlan Somers 	int val = 0;
1059821f1d3SAlan Somers 	size_t size = sizeof(val);
1069821f1d3SAlan Somers 
1079821f1d3SAlan Somers 	FuseTest::SetUp();
1089821f1d3SAlan Somers 	if (IsSkipped())
1099821f1d3SAlan Somers 		return;
1109821f1d3SAlan Somers 
1119821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0))
1129821f1d3SAlan Somers 		<< strerror(errno);
1139821f1d3SAlan Somers 	if (val != 1)
1149821f1d3SAlan Somers 		GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 "
1159821f1d3SAlan Somers 			"(writethrough) for this test";
1169821f1d3SAlan Somers }
1179821f1d3SAlan Somers 
1189821f1d3SAlan Somers };
1199821f1d3SAlan Somers 
1209821f1d3SAlan Somers /* Tests for the writeback cache mode */
1219821f1d3SAlan Somers class WriteBack: public Write {
1229821f1d3SAlan Somers 
1239821f1d3SAlan Somers virtual void SetUp() {
1249821f1d3SAlan Somers 	const char *node = "vfs.fusefs.data_cache_mode";
1259821f1d3SAlan Somers 	int val = 0;
1269821f1d3SAlan Somers 	size_t size = sizeof(val);
1279821f1d3SAlan Somers 
1289821f1d3SAlan Somers 	FuseTest::SetUp();
1299821f1d3SAlan Somers 	if (IsSkipped())
1309821f1d3SAlan Somers 		return;
1319821f1d3SAlan Somers 
1329821f1d3SAlan Somers 	ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0))
1339821f1d3SAlan Somers 		<< strerror(errno);
1349821f1d3SAlan Somers 	if (val != 2)
1359821f1d3SAlan Somers 		GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 "
1369821f1d3SAlan Somers 			"(writeback) for this test";
1379821f1d3SAlan Somers }
1389821f1d3SAlan Somers 
1399821f1d3SAlan Somers };
1409821f1d3SAlan Somers 
1419821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */
1429821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
1439821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write)
1449821f1d3SAlan Somers {
1459821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1469821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
1479821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
1489821f1d3SAlan Somers 	uint64_t ino = 42;
1499821f1d3SAlan Somers 	uint64_t offset = 4096;
1509821f1d3SAlan Somers 	int fd;
1519821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
1529821f1d3SAlan Somers 	struct aiocb iocb, *piocb;
1539821f1d3SAlan Somers 
1549821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
1559821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1569821f1d3SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
1579821f1d3SAlan Somers 
1589821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
1599821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
1609821f1d3SAlan Somers 
1619821f1d3SAlan Somers 	iocb.aio_nbytes = bufsize;
1629821f1d3SAlan Somers 	iocb.aio_fildes = fd;
1639821f1d3SAlan Somers 	iocb.aio_buf = (void *)CONTENTS;
1649821f1d3SAlan Somers 	iocb.aio_offset = offset;
1659821f1d3SAlan Somers 	iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
1669821f1d3SAlan Somers 	ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
1679821f1d3SAlan Somers 	ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
1689821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
1699821f1d3SAlan Somers }
1709821f1d3SAlan Somers 
1719821f1d3SAlan Somers /*
1729821f1d3SAlan Somers  * When a file is opened with O_APPEND, we should forward that flag to
1739821f1d3SAlan Somers  * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
1749821f1d3SAlan Somers  * offset internally.  That way we'll work both with filesystems that
1759821f1d3SAlan Somers  * understand O_APPEND (and ignore the offset) and filesystems that don't (and
1769821f1d3SAlan Somers  * simply use the offset).
1779821f1d3SAlan Somers  *
1789821f1d3SAlan Somers  * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
1799821f1d3SAlan Somers  * Open.o_append test.
1809821f1d3SAlan Somers  */
1819821f1d3SAlan Somers TEST_F(Write, append)
1829821f1d3SAlan Somers {
1839821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
1849821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
1859821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
1869821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
1879821f1d3SAlan Somers 	uint64_t ino = 42;
1889821f1d3SAlan Somers 	/*
1899821f1d3SAlan Somers 	 * Set offset to a maxbcachebuf boundary so we don't need to RMW when
1909821f1d3SAlan Somers 	 * using writeback caching
1919821f1d3SAlan Somers 	 */
1929821f1d3SAlan Somers 	uint64_t initial_offset = m_maxbcachebuf;
1939821f1d3SAlan Somers 	int fd;
1949821f1d3SAlan Somers 
1959821f1d3SAlan Somers 	require_sync_resize_0();
1969821f1d3SAlan Somers 
1979821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
1989821f1d3SAlan Somers 	expect_open(ino, 0, 1);
1999821f1d3SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
2009821f1d3SAlan Somers 
2019821f1d3SAlan Somers 	/* Must open O_RDWR or fuse(4) implicitly sets direct_io */
2029821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_APPEND);
2039821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
2049821f1d3SAlan Somers 
2059821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
2069821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
2079821f1d3SAlan Somers }
2089821f1d3SAlan Somers 
2099821f1d3SAlan Somers TEST_F(Write, append_direct_io)
2109821f1d3SAlan Somers {
2119821f1d3SAlan Somers 	const ssize_t BUFSIZE = 9;
2129821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2139821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2149821f1d3SAlan Somers 	const char CONTENTS[BUFSIZE] = "abcdefgh";
2159821f1d3SAlan Somers 	uint64_t ino = 42;
2169821f1d3SAlan Somers 	uint64_t initial_offset = 4096;
2179821f1d3SAlan Somers 	int fd;
2189821f1d3SAlan Somers 
2199821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, initial_offset);
2209821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
2219821f1d3SAlan Somers 	expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, 0, CONTENTS);
2229821f1d3SAlan Somers 
2239821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY | O_APPEND);
2249821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
2259821f1d3SAlan Somers 
2269821f1d3SAlan Somers 	ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
2279821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
2289821f1d3SAlan Somers }
2299821f1d3SAlan Somers 
2309821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */
231*6af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache)
2329821f1d3SAlan Somers {
2339821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2349821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2359821f1d3SAlan Somers 	const char CONTENTS0[] = "abcdefgh";
2369821f1d3SAlan Somers 	const char CONTENTS1[] = "ijklmnop";
2379821f1d3SAlan Somers 	uint64_t ino = 42;
2389821f1d3SAlan Somers 	int fd;
2399821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
2409821f1d3SAlan Somers 	char readbuf[bufsize];
2419821f1d3SAlan Somers 
2429821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
2439821f1d3SAlan Somers 	expect_open(ino, 0, 1);
2449821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
2459821f1d3SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1);
2469821f1d3SAlan Somers 
2479821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
2489821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
2499821f1d3SAlan Somers 
2509821f1d3SAlan Somers 	// Prime cache
2519821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
2529821f1d3SAlan Somers 
2539821f1d3SAlan Somers 	// Write directly, evicting cache
2549821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
2559821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
2569821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
2579821f1d3SAlan Somers 
2589821f1d3SAlan Somers 	// Read again.  Cache should be bypassed
2599821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
2609821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
2619821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
2629821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
2639821f1d3SAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS1);
2649821f1d3SAlan Somers 
2659821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
2669821f1d3SAlan Somers }
2679821f1d3SAlan Somers 
2689821f1d3SAlan Somers /*
26912292a99SAlan Somers  * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
27012292a99SAlan Somers  * allowed to return a short write for that file handle.  However, if it does
27112292a99SAlan Somers  * then we should still do our darndest to handle it by resending the unwritten
27212292a99SAlan Somers  * portion.
2739821f1d3SAlan Somers  */
27412292a99SAlan Somers TEST_F(Write, indirect_io_short_write)
2759821f1d3SAlan Somers {
2769821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
2779821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
2789821f1d3SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
2799821f1d3SAlan Somers 	uint64_t ino = 42;
2809821f1d3SAlan Somers 	int fd;
2819821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
28212292a99SAlan Somers 	ssize_t bufsize0 = 11;
28312292a99SAlan Somers 	ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
28412292a99SAlan Somers 	const char *contents1 = CONTENTS + bufsize0;
2859821f1d3SAlan Somers 
2869821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
28712292a99SAlan Somers 	expect_open(ino, 0, 1);
28812292a99SAlan Somers 	expect_write(ino, 0, bufsize, bufsize0, 0, CONTENTS);
28912292a99SAlan Somers 	expect_write(ino, bufsize0, bufsize1, bufsize1, 0,
29012292a99SAlan Somers 		contents1);
2919821f1d3SAlan Somers 
2929821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
2939821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
2949821f1d3SAlan Somers 
2959821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
2969821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
2979821f1d3SAlan Somers }
2989821f1d3SAlan Somers 
2999821f1d3SAlan Somers /*
30012292a99SAlan Somers  * When the direct_io option is used, filesystems are allowed to write less
30112292a99SAlan Somers  * data than requested.  We should return the short write to userland.
30212292a99SAlan Somers  */
30312292a99SAlan Somers TEST_F(Write, direct_io_short_write)
30412292a99SAlan Somers {
30512292a99SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
30612292a99SAlan Somers 	const char RELPATH[] = "some_file.txt";
30712292a99SAlan Somers 	const char *CONTENTS = "abcdefghijklmnop";
30812292a99SAlan Somers 	uint64_t ino = 42;
30912292a99SAlan Somers 	int fd;
31012292a99SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
31112292a99SAlan Somers 	ssize_t halfbufsize = bufsize / 2;
31212292a99SAlan Somers 
31312292a99SAlan Somers 	expect_lookup(RELPATH, ino, 0);
31412292a99SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
31512292a99SAlan Somers 	expect_write(ino, 0, bufsize, halfbufsize, 0, CONTENTS);
31612292a99SAlan Somers 
31712292a99SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
31812292a99SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
31912292a99SAlan Somers 
32012292a99SAlan Somers 	ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
32112292a99SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
32212292a99SAlan Somers }
32312292a99SAlan Somers 
32412292a99SAlan Somers /*
3259821f1d3SAlan Somers  * An insidious edge case: the filesystem returns a short write, and the
3269821f1d3SAlan Somers  * difference between what we requested and what it actually wrote crosses an
3279821f1d3SAlan Somers  * iov element boundary
3289821f1d3SAlan Somers  */
32912292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov)
3309821f1d3SAlan Somers {
3319821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3329821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3339821f1d3SAlan Somers 	const char *CONTENTS0 = "abcdefgh";
3349821f1d3SAlan Somers 	const char *CONTENTS1 = "ijklmnop";
3359821f1d3SAlan Somers 	const char *EXPECTED0 = "abcdefghijklmnop";
3369821f1d3SAlan Somers 	uint64_t ino = 42;
3379821f1d3SAlan Somers 	int fd;
3389821f1d3SAlan Somers 	ssize_t size0 = strlen(CONTENTS0) - 1;
3399821f1d3SAlan Somers 	ssize_t size1 = strlen(CONTENTS1) + 1;
3409821f1d3SAlan Somers 	ssize_t totalsize = size0 + size1;
3419821f1d3SAlan Somers 	struct iovec iov[2];
3429821f1d3SAlan Somers 
3439821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
3449821f1d3SAlan Somers 	expect_open(ino, FOPEN_DIRECT_IO, 1);
3459821f1d3SAlan Somers 	expect_write(ino, 0, totalsize, size0, 0, EXPECTED0);
3469821f1d3SAlan Somers 
3479821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
3489821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3499821f1d3SAlan Somers 
3509821f1d3SAlan Somers 	iov[0].iov_base = (void*)CONTENTS0;
3519821f1d3SAlan Somers 	iov[0].iov_len = strlen(CONTENTS0);
3529821f1d3SAlan Somers 	iov[1].iov_base = (void*)CONTENTS1;
3539821f1d3SAlan Somers 	iov[1].iov_len = strlen(CONTENTS1);
35412292a99SAlan Somers 	ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
3559821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
3569821f1d3SAlan Somers }
3579821f1d3SAlan Somers 
3589821f1d3SAlan Somers /*
3599821f1d3SAlan Somers  * If the kernel cannot be sure which uid, gid, or pid was responsible for a
3609821f1d3SAlan Somers  * write, then it must set the FUSE_WRITE_CACHE bit
3619821f1d3SAlan Somers  */
3629821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
3639821f1d3SAlan Somers // TODO: check vfs.fusefs.mmap_enable
3649821f1d3SAlan Somers TEST_F(Write, DISABLED_mmap)
3659821f1d3SAlan Somers {
3669821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
3679821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
3689821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
3699821f1d3SAlan Somers 	uint64_t ino = 42;
3709821f1d3SAlan Somers 	int fd;
3719821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
3729821f1d3SAlan Somers 	void *p;
3739821f1d3SAlan Somers 	uint64_t offset = 10;
3749821f1d3SAlan Somers 	size_t len;
3759821f1d3SAlan Somers 	void *zeros, *expected;
3769821f1d3SAlan Somers 
3779821f1d3SAlan Somers 	len = getpagesize();
3789821f1d3SAlan Somers 
3799821f1d3SAlan Somers 	zeros = calloc(1, len);
3809821f1d3SAlan Somers 	ASSERT_NE(NULL, zeros);
3819821f1d3SAlan Somers 	expected = calloc(1, len);
3829821f1d3SAlan Somers 	ASSERT_NE(NULL, expected);
3839821f1d3SAlan Somers 	memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
3849821f1d3SAlan Somers 
3859821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, len);
3869821f1d3SAlan Somers 	expect_open(ino, 0, 1);
3879821f1d3SAlan Somers 	expect_read(ino, 0, len, len, zeros);
3889821f1d3SAlan Somers 	/*
3899821f1d3SAlan Somers 	 * Writes from the pager may or may not be associated with the correct
3909821f1d3SAlan Somers 	 * pid, so they must set FUSE_WRITE_CACHE
3919821f1d3SAlan Somers 	 */
3929821f1d3SAlan Somers 	expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, expected);
3939f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
3949821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
3959821f1d3SAlan Somers 
3969821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
3979821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
3989821f1d3SAlan Somers 
3999821f1d3SAlan Somers 	p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
4009821f1d3SAlan Somers 	ASSERT_NE(MAP_FAILED, p) << strerror(errno);
4019821f1d3SAlan Somers 
4029821f1d3SAlan Somers 	memmove((uint8_t*)p + offset, CONTENTS, bufsize);
4039821f1d3SAlan Somers 
4049821f1d3SAlan Somers 	ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
4059821f1d3SAlan Somers 	close(fd);	// Write mmap'd data on close
4069821f1d3SAlan Somers 
4079821f1d3SAlan Somers 	free(expected);
4089821f1d3SAlan Somers 	free(zeros);
4099821f1d3SAlan Somers }
4109821f1d3SAlan Somers 
411*6af6fdceSAlan Somers /* In WriteThrough mode, a write should evict overlapping cached data */
412*6af6fdceSAlan Somers TEST_F(WriteThrough, evicts_read_cache)
413*6af6fdceSAlan Somers {
414*6af6fdceSAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
415*6af6fdceSAlan Somers 	const char RELPATH[] = "some_file.txt";
416*6af6fdceSAlan Somers 	const char CONTENTS0[] = "abcdefgh";
417*6af6fdceSAlan Somers 	const char CONTENTS1[] = "ijklmnop";
418*6af6fdceSAlan Somers 	uint64_t ino = 42;
419*6af6fdceSAlan Somers 	int fd;
420*6af6fdceSAlan Somers 	ssize_t bufsize = strlen(CONTENTS0) + 1;
421*6af6fdceSAlan Somers 	char readbuf[bufsize];
422*6af6fdceSAlan Somers 
423*6af6fdceSAlan Somers 	expect_lookup(RELPATH, ino, bufsize);
424*6af6fdceSAlan Somers 	expect_open(ino, 0, 1);
425*6af6fdceSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
426*6af6fdceSAlan Somers 	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1);
427*6af6fdceSAlan Somers 
428*6af6fdceSAlan Somers 	fd = open(FULLPATH, O_RDWR);
429*6af6fdceSAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
430*6af6fdceSAlan Somers 
431*6af6fdceSAlan Somers 	// Prime cache
432*6af6fdceSAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
433*6af6fdceSAlan Somers 
434*6af6fdceSAlan Somers 	// Write directly, evicting cache
435*6af6fdceSAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
436*6af6fdceSAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
437*6af6fdceSAlan Somers 
438*6af6fdceSAlan Somers 	// Read again.  Cache should be bypassed
439*6af6fdceSAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
440*6af6fdceSAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
441*6af6fdceSAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
442*6af6fdceSAlan Somers 	ASSERT_STREQ(readbuf, CONTENTS1);
443*6af6fdceSAlan Somers 
444*6af6fdceSAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
445*6af6fdceSAlan Somers }
446*6af6fdceSAlan Somers 
4475fccbf31SAlan Somers TEST_F(WriteThrough, pwrite)
4489821f1d3SAlan Somers {
4499821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4509821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4519821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
4529821f1d3SAlan Somers 	uint64_t ino = 42;
4539821f1d3SAlan Somers 	uint64_t offset = 4096;
4549821f1d3SAlan Somers 	int fd;
4559821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
4569821f1d3SAlan Somers 
4579821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
4589821f1d3SAlan Somers 	expect_open(ino, 0, 1);
4599821f1d3SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
4609821f1d3SAlan Somers 
4619821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
4629821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
4639821f1d3SAlan Somers 
4649821f1d3SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
4659821f1d3SAlan Somers 		<< strerror(errno);
4669821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
4679821f1d3SAlan Somers }
4689821f1d3SAlan Somers 
4699821f1d3SAlan Somers TEST_F(Write, write)
4709821f1d3SAlan Somers {
4719821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4729821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4739821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
4749821f1d3SAlan Somers 	uint64_t ino = 42;
4759821f1d3SAlan Somers 	int fd;
4769821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
4779821f1d3SAlan Somers 
4789821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
4799821f1d3SAlan Somers 	expect_open(ino, 0, 1);
4809821f1d3SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
4819821f1d3SAlan Somers 
4829821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
4839821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
4849821f1d3SAlan Somers 
4859821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4869821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
4879821f1d3SAlan Somers }
4889821f1d3SAlan Somers 
4899821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */
4909821f1d3SAlan Somers TEST_F(Write, write_large)
4919821f1d3SAlan Somers {
4929821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
4939821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
4949821f1d3SAlan Somers 	int *contents;
4959821f1d3SAlan Somers 	uint64_t ino = 42;
4969821f1d3SAlan Somers 	int fd;
4979821f1d3SAlan Somers 	ssize_t halfbufsize, bufsize;
4989821f1d3SAlan Somers 
4999821f1d3SAlan Somers 	halfbufsize = m_mock->m_max_write;
5009821f1d3SAlan Somers 	bufsize = halfbufsize * 2;
5019821f1d3SAlan Somers 	contents = (int*)malloc(bufsize);
5029821f1d3SAlan Somers 	ASSERT_NE(NULL, contents);
5039821f1d3SAlan Somers 	for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
5049821f1d3SAlan Somers 		contents[i] = i;
5059821f1d3SAlan Somers 	}
5069821f1d3SAlan Somers 
5079821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
5089821f1d3SAlan Somers 	expect_open(ino, 0, 1);
5099821f1d3SAlan Somers 	expect_write(ino, 0, halfbufsize, halfbufsize, 0, contents);
5109821f1d3SAlan Somers 	expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 0,
5119821f1d3SAlan Somers 		&contents[halfbufsize / sizeof(int)]);
5129821f1d3SAlan Somers 
5139821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
5149821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
5159821f1d3SAlan Somers 
5169821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
5179821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
5189821f1d3SAlan Somers 
5199821f1d3SAlan Somers 	free(contents);
5209821f1d3SAlan Somers }
5219821f1d3SAlan Somers 
5229821f1d3SAlan Somers TEST_F(Write, write_nothing)
5239821f1d3SAlan Somers {
5249821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5259821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5269821f1d3SAlan Somers 	const char *CONTENTS = "";
5279821f1d3SAlan Somers 	uint64_t ino = 42;
5289821f1d3SAlan Somers 	int fd;
5299821f1d3SAlan Somers 	ssize_t bufsize = 0;
5309821f1d3SAlan Somers 
5319821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
5329821f1d3SAlan Somers 	expect_open(ino, 0, 1);
5339821f1d3SAlan Somers 
5349821f1d3SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
5359821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
5369821f1d3SAlan Somers 
5379821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
5389821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
5399821f1d3SAlan Somers }
5409821f1d3SAlan Somers 
5419821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */
5429821f1d3SAlan Somers TEST_F(WriteBack, close)
5439821f1d3SAlan Somers {
5449821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5459821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
5469821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
5479821f1d3SAlan Somers 	uint64_t ino = 42;
5489821f1d3SAlan Somers 	int fd;
5499821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5509821f1d3SAlan Somers 
5519821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
5529821f1d3SAlan Somers 	expect_open(ino, 0, 1);
5539821f1d3SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
5549821f1d3SAlan Somers 	EXPECT_CALL(*m_mock, process(
5559821f1d3SAlan Somers 		ResultOf([=](auto in) {
5569821f1d3SAlan Somers 			return (in->header.opcode == FUSE_SETATTR);
5579821f1d3SAlan Somers 		}, Eq(true)),
5589821f1d3SAlan Somers 		_)
5599821f1d3SAlan Somers 	).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) {
5609821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out, attr);
5619821f1d3SAlan Somers 		out->body.attr.attr.ino = ino;	// Must match nodeid
5629821f1d3SAlan Somers 	})));
5639f10f423SAlan Somers 	expect_flush(ino, 1, ReturnErrno(0));
5649821f1d3SAlan Somers 	expect_release(ino, ReturnErrno(0));
5659821f1d3SAlan Somers 
5669821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
5679821f1d3SAlan Somers 	ASSERT_LE(0, fd) << strerror(errno);
5689821f1d3SAlan Somers 
5699821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
5709821f1d3SAlan Somers 	close(fd);
5719821f1d3SAlan Somers }
5729821f1d3SAlan Somers 
5739821f1d3SAlan Somers /*
5745fccbf31SAlan Somers  * In writeback mode, writes to an O_WRONLY file could trigger reads from the
5755fccbf31SAlan Somers  * server.  The FUSE protocol explicitly allows that.
5765fccbf31SAlan Somers  */
5775fccbf31SAlan Somers TEST_F(WriteBack, rmw)
5785fccbf31SAlan Somers {
5795fccbf31SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
5805fccbf31SAlan Somers 	const char RELPATH[] = "some_file.txt";
5815fccbf31SAlan Somers 	const char *CONTENTS = "abcdefgh";
5825fccbf31SAlan Somers 	const char *INITIAL   = "XXXXXXXXXX";
5835fccbf31SAlan Somers 	uint64_t ino = 42;
5845fccbf31SAlan Somers 	uint64_t offset = 1;
5855fccbf31SAlan Somers 	off_t fsize = 10;
5865fccbf31SAlan Somers 	int fd;
5875fccbf31SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
5885fccbf31SAlan Somers 
589cad67791SAlan Somers 	FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
5905fccbf31SAlan Somers 	expect_open(ino, 0, 1);
5915fccbf31SAlan Somers 	expect_read(ino, 0, fsize, fsize, INITIAL);
5925fccbf31SAlan Somers 	expect_write(ino, offset, bufsize, bufsize, 0, CONTENTS);
5935fccbf31SAlan Somers 
5945fccbf31SAlan Somers 	fd = open(FULLPATH, O_WRONLY);
5955fccbf31SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
5965fccbf31SAlan Somers 
5975fccbf31SAlan Somers 	ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
5985fccbf31SAlan Somers 		<< strerror(errno);
5995fccbf31SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6005fccbf31SAlan Somers }
6015fccbf31SAlan Somers 
6025fccbf31SAlan Somers /*
6039821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
6049821f1d3SAlan Somers  */
6059821f1d3SAlan Somers TEST_F(WriteBack, writeback)
6069821f1d3SAlan Somers {
6079821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6089821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6099821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6109821f1d3SAlan Somers 	uint64_t ino = 42;
6119821f1d3SAlan Somers 	int fd;
6129821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6139821f1d3SAlan Somers 	char readbuf[bufsize];
6149821f1d3SAlan Somers 
6159821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6169821f1d3SAlan Somers 	expect_open(ino, 0, 1);
6179821f1d3SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
6189821f1d3SAlan Somers 
6199821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
6209821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6219821f1d3SAlan Somers 
6229821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6239821f1d3SAlan Somers 	/*
6249821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
6259821f1d3SAlan Somers 	 * filesystem daemon
6269821f1d3SAlan Somers 	 */
6279821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
6289821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
6299821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6309821f1d3SAlan Somers }
6319821f1d3SAlan Somers 
6329821f1d3SAlan Somers /*
6339821f1d3SAlan Somers  * With O_DIRECT, writes should be not committed to cache.  Admittedly this is
6349821f1d3SAlan Somers  * an odd test, because it would be unusual to use O_DIRECT for writes but not
6359821f1d3SAlan Somers  * reads.
6369821f1d3SAlan Somers  */
6379821f1d3SAlan Somers TEST_F(WriteBack, o_direct)
6389821f1d3SAlan Somers {
6399821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
6409821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
6419821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
6429821f1d3SAlan Somers 	uint64_t ino = 42;
6439821f1d3SAlan Somers 	int fd;
6449821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
6459821f1d3SAlan Somers 	char readbuf[bufsize];
6469821f1d3SAlan Somers 
6479821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6489821f1d3SAlan Somers 	expect_open(ino, 0, 1);
6499821f1d3SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
6509821f1d3SAlan Somers 	expect_read(ino, 0, bufsize, bufsize, CONTENTS);
6519821f1d3SAlan Somers 
6529821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR | O_DIRECT);
6539821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6549821f1d3SAlan Somers 
6559821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6569821f1d3SAlan Somers 	/* A subsequent read must query the daemon because cache is empty */
6579821f1d3SAlan Somers 	ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
6589821f1d3SAlan Somers 	ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
6599821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
6609821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6619821f1d3SAlan Somers }
6629821f1d3SAlan Somers 
6639821f1d3SAlan Somers /*
6649821f1d3SAlan Somers  * Without direct_io, writes should be committed to cache
6659821f1d3SAlan Somers  */
6669821f1d3SAlan Somers /*
6679821f1d3SAlan Somers  * Disabled because we don't yet implement write-through caching.  No bugzilla
6689821f1d3SAlan Somers  * entry, because that's a feature request, not a bug.
6699821f1d3SAlan Somers  */
6709821f1d3SAlan Somers TEST_F(WriteThrough, DISABLED_writethrough)
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 	char readbuf[bufsize];
6799821f1d3SAlan Somers 
6809821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
6819821f1d3SAlan Somers 	expect_open(ino, 0, 1);
6829821f1d3SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
6839821f1d3SAlan Somers 
6849821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
6859821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
6869821f1d3SAlan Somers 
6879821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
6889821f1d3SAlan Somers 	/*
6899821f1d3SAlan Somers 	 * A subsequent read should be serviced by cache, without querying the
6909821f1d3SAlan Somers 	 * filesystem daemon
6919821f1d3SAlan Somers 	 */
6929821f1d3SAlan Somers 	ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
6939821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
6949821f1d3SAlan Somers }
6959821f1d3SAlan Somers 
6969821f1d3SAlan Somers /* With writethrough caching, writes update the cached file size */
697cad67791SAlan Somers TEST_F(WriteThrough, update_file_size)
6989821f1d3SAlan Somers {
6999821f1d3SAlan Somers 	const char FULLPATH[] = "mountpoint/some_file.txt";
7009821f1d3SAlan Somers 	const char RELPATH[] = "some_file.txt";
7019821f1d3SAlan Somers 	const char *CONTENTS = "abcdefgh";
7029821f1d3SAlan Somers 	struct stat sb;
7039821f1d3SAlan Somers 	uint64_t ino = 42;
7049821f1d3SAlan Somers 	int fd;
7059821f1d3SAlan Somers 	ssize_t bufsize = strlen(CONTENTS);
7069821f1d3SAlan Somers 
7079821f1d3SAlan Somers 	expect_lookup(RELPATH, ino, 0);
7089821f1d3SAlan Somers 	expect_open(ino, 0, 1);
7099821f1d3SAlan Somers 	expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
7109821f1d3SAlan Somers 
7119821f1d3SAlan Somers 	fd = open(FULLPATH, O_RDWR);
7129821f1d3SAlan Somers 	EXPECT_LE(0, fd) << strerror(errno);
7139821f1d3SAlan Somers 
7149821f1d3SAlan Somers 	ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
7159821f1d3SAlan Somers 	/* Get cached attributes */
7169821f1d3SAlan Somers 	ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
7179821f1d3SAlan Somers 	ASSERT_EQ(bufsize, sb.st_size);
7189821f1d3SAlan Somers 	/* Deliberately leak fd.  close(2) will be tested in release.cc */
7199821f1d3SAlan Somers }
720