19821f1d3SAlan Somers /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
39821f1d3SAlan Somers *
49821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation
59821f1d3SAlan Somers *
69821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship
79821f1d3SAlan Somers * from the FreeBSD Foundation.
89821f1d3SAlan Somers *
99821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without
109821f1d3SAlan Somers * modification, are permitted provided that the following conditions
119821f1d3SAlan Somers * are met:
129821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright
139821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer.
149821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright
159821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the
169821f1d3SAlan Somers * documentation and/or other materials provided with the distribution.
179821f1d3SAlan Somers *
189821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289821f1d3SAlan Somers * SUCH DAMAGE.
299821f1d3SAlan Somers */
309821f1d3SAlan Somers
319821f1d3SAlan Somers extern "C" {
328eecd9ceSAlan Somers #include <sys/param.h>
339821f1d3SAlan Somers #include <sys/mman.h>
34a639731bSAlan Somers #include <sys/resource.h>
359821f1d3SAlan Somers #include <sys/stat.h>
36a639731bSAlan Somers #include <sys/time.h>
379821f1d3SAlan Somers #include <sys/uio.h>
389821f1d3SAlan Somers
399821f1d3SAlan Somers #include <aio.h>
409821f1d3SAlan Somers #include <fcntl.h>
41a639731bSAlan Somers #include <signal.h>
429821f1d3SAlan Somers #include <unistd.h>
439821f1d3SAlan Somers }
449821f1d3SAlan Somers
459821f1d3SAlan Somers #include "mockfs.hh"
469821f1d3SAlan Somers #include "utils.hh"
479821f1d3SAlan Somers
489821f1d3SAlan Somers using namespace testing;
499821f1d3SAlan Somers
509821f1d3SAlan Somers class Write: public FuseTest {
519821f1d3SAlan Somers
529821f1d3SAlan Somers public:
SetUp()53a639731bSAlan Somers void SetUp() {
54a639731bSAlan Somers FuseTest::SetUp();
55a639731bSAlan Somers }
56a639731bSAlan Somers
TearDown()57a639731bSAlan Somers void TearDown() {
58a639731bSAlan Somers struct sigaction sa;
59a639731bSAlan Somers
60a639731bSAlan Somers bzero(&sa, sizeof(sa));
61a639731bSAlan Somers sa.sa_handler = SIG_DFL;
62a639731bSAlan Somers sigaction(SIGXFSZ, &sa, NULL);
63a639731bSAlan Somers
64a639731bSAlan Somers FuseTest::TearDown();
65a639731bSAlan Somers }
669821f1d3SAlan Somers
expect_lookup(const char * relpath,uint64_t ino,uint64_t size)679821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
689821f1d3SAlan Somers {
699821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1);
709821f1d3SAlan Somers }
719821f1d3SAlan Somers
expect_release(uint64_t ino,ProcessMockerT r)729821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r)
739821f1d3SAlan Somers {
749821f1d3SAlan Somers EXPECT_CALL(*m_mock, process(
759821f1d3SAlan Somers ResultOf([=](auto in) {
7629edc611SAlan Somers return (in.header.opcode == FUSE_RELEASE &&
7729edc611SAlan Somers in.header.nodeid == ino);
789821f1d3SAlan Somers }, Eq(true)),
799821f1d3SAlan Somers _)
809821f1d3SAlan Somers ).WillRepeatedly(Invoke(r));
819821f1d3SAlan Somers }
829821f1d3SAlan Somers
expect_write(uint64_t ino,uint64_t offset,uint64_t isize,uint64_t osize,const void * contents)83bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
84bda39894SAlan Somers uint64_t osize, const void *contents)
85bda39894SAlan Somers {
86bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents);
87bda39894SAlan Somers }
88bda39894SAlan Somers
8984879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */
maybe_expect_write(uint64_t ino,uint64_t offset,uint64_t size,const void * contents)9084879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size,
9184879e46SAlan Somers const void *contents)
9284879e46SAlan Somers {
9384879e46SAlan Somers EXPECT_CALL(*m_mock, process(
9484879e46SAlan Somers ResultOf([=](auto in) {
9584879e46SAlan Somers const char *buf = (const char*)in.body.bytes +
9684879e46SAlan Somers sizeof(struct fuse_write_in);
9784879e46SAlan Somers
980c9df4afSAlan Somers assert(size <= sizeof(in.body.bytes) -
990c9df4afSAlan Somers sizeof(struct fuse_write_in));
10084879e46SAlan Somers return (in.header.opcode == FUSE_WRITE &&
10184879e46SAlan Somers in.header.nodeid == ino &&
10284879e46SAlan Somers in.body.write.offset == offset &&
10384879e46SAlan Somers in.body.write.size == size &&
10484879e46SAlan Somers 0 == bcmp(buf, contents, size));
10584879e46SAlan Somers }, Eq(true)),
10684879e46SAlan Somers _)
10784879e46SAlan Somers ).Times(AtMost(1))
10884879e46SAlan Somers .WillRepeatedly(Invoke(
10984879e46SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) {
11084879e46SAlan Somers SET_OUT_HEADER_LEN(out, write);
11184879e46SAlan Somers out.body.write.size = size;
11284879e46SAlan Somers })
11384879e46SAlan Somers ));
11484879e46SAlan Somers }
11584879e46SAlan Somers
1169821f1d3SAlan Somers };
1179821f1d3SAlan Somers
11816bd2d47SAlan Somers class Write_7_8: public FuseTest {
11916bd2d47SAlan Somers
12016bd2d47SAlan Somers public:
SetUp()12116bd2d47SAlan Somers virtual void SetUp() {
12216bd2d47SAlan Somers m_kernel_minor_version = 8;
12316bd2d47SAlan Somers FuseTest::SetUp();
12416bd2d47SAlan Somers }
12516bd2d47SAlan Somers
expect_lookup(const char * relpath,uint64_t ino,uint64_t size)12616bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size)
12716bd2d47SAlan Somers {
12816bd2d47SAlan Somers FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1);
12916bd2d47SAlan Somers }
13016bd2d47SAlan Somers
13116bd2d47SAlan Somers };
13216bd2d47SAlan Somers
1339821f1d3SAlan Somers class AioWrite: public Write {
SetUp()1349821f1d3SAlan Somers virtual void SetUp() {
135c2265ae7SAlan Somers if (!is_unsafe_aio_enabled())
1369821f1d3SAlan Somers GTEST_SKIP() <<
1379821f1d3SAlan Somers "vfs.aio.enable_unsafe must be set for this test";
138c2265ae7SAlan Somers FuseTest::SetUp();
1399821f1d3SAlan Somers }
1409821f1d3SAlan Somers };
1419821f1d3SAlan Somers
1429821f1d3SAlan Somers /* Tests for the writeback cache mode */
1439821f1d3SAlan Somers class WriteBack: public Write {
144bda39894SAlan Somers public:
SetUp()1459821f1d3SAlan Somers virtual void SetUp() {
146f8ebf1cdSAlan Somers m_init_flags |= FUSE_WRITEBACK_CACHE;
1479821f1d3SAlan Somers FuseTest::SetUp();
1489821f1d3SAlan Somers if (IsSkipped())
1499821f1d3SAlan Somers return;
1509821f1d3SAlan Somers }
1519821f1d3SAlan Somers
expect_write(uint64_t ino,uint64_t offset,uint64_t isize,uint64_t osize,const void * contents)152bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
153bda39894SAlan Somers uint64_t osize, const void *contents)
154bda39894SAlan Somers {
155bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0,
156bda39894SAlan Somers contents);
157bda39894SAlan Somers }
1589821f1d3SAlan Somers };
1599821f1d3SAlan Somers
16084879e46SAlan Somers class WriteBackAsync: public WriteBack {
16184879e46SAlan Somers public:
SetUp()16284879e46SAlan Somers virtual void SetUp() {
16384879e46SAlan Somers m_async = true;
164f928dbcbSAlan Somers m_maxwrite = 65536;
16584879e46SAlan Somers WriteBack::SetUp();
16684879e46SAlan Somers }
16784879e46SAlan Somers };
16884879e46SAlan Somers
169fef46454SAlan Somers class TimeGran: public WriteBackAsync, public WithParamInterface<unsigned> {
170fef46454SAlan Somers public:
SetUp()171fef46454SAlan Somers virtual void SetUp() {
172fef46454SAlan Somers m_time_gran = 1 << GetParam();
173fef46454SAlan Somers WriteBackAsync::SetUp();
174fef46454SAlan Somers }
175fef46454SAlan Somers };
176fef46454SAlan Somers
1778eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */
1788eecd9ceSAlan Somers class WriteCluster: public WriteBack {
1798eecd9ceSAlan Somers public:
SetUp()1808eecd9ceSAlan Somers virtual void SetUp() {
1818eecd9ceSAlan Somers m_async = true;
182*b2792a30SAlan Somers m_maxwrite = UINT32_MAX; // Anything larger than MAXPHYS will suffice
1838eecd9ceSAlan Somers WriteBack::SetUp();
184f8ebf1cdSAlan Somers if (m_maxphys < 2 * DFLTPHYS)
185f8ebf1cdSAlan Somers GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS"
186f8ebf1cdSAlan Somers << " for this test";
187e9b411d2SGleb Smirnoff if (m_maxphys < 2 * (unsigned long )m_maxbcachebuf)
1886ca3b02bSAlan Somers GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf"
1896ca3b02bSAlan Somers << " for this test";
1908eecd9ceSAlan Somers }
1918eecd9ceSAlan Somers };
1928eecd9ceSAlan Somers
193f928dbcbSAlan Somers /* Tests relating to the server's max_write property */
194f928dbcbSAlan Somers class WriteMaxWrite: public Write {
195f928dbcbSAlan Somers public:
SetUp()196f928dbcbSAlan Somers virtual void SetUp() {
197f928dbcbSAlan Somers /*
198f928dbcbSAlan Somers * For this test, m_maxwrite must be less than either m_maxbcachebuf or
199f928dbcbSAlan Somers * maxphys.
200f928dbcbSAlan Somers */
201f928dbcbSAlan Somers m_maxwrite = 32768;
202f928dbcbSAlan Somers Write::SetUp();
203f928dbcbSAlan Somers }
204f928dbcbSAlan Somers };
205f928dbcbSAlan Somers
206032a5bd5SAlan Somers class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int>
207032a5bd5SAlan Somers {};
208032a5bd5SAlan Somers
209be280f60SAlan Somers class WriteRlimitFsize: public Write, public WithParamInterface<int> {
210be280f60SAlan Somers public:
211be280f60SAlan Somers static sig_atomic_t s_sigxfsz;
212be280f60SAlan Somers struct rlimit m_initial_limit;
213be280f60SAlan Somers
SetUp()214be280f60SAlan Somers void SetUp() {
215be280f60SAlan Somers s_sigxfsz = 0;
216be280f60SAlan Somers getrlimit(RLIMIT_FSIZE, &m_initial_limit);
217be280f60SAlan Somers FuseTest::SetUp();
218be280f60SAlan Somers }
219be280f60SAlan Somers
TearDown()220be280f60SAlan Somers void TearDown() {
221be280f60SAlan Somers setrlimit(RLIMIT_FSIZE, &m_initial_limit);
222be280f60SAlan Somers
223be280f60SAlan Somers FuseTest::TearDown();
224be280f60SAlan Somers }
225be280f60SAlan Somers };
226be280f60SAlan Somers
227be280f60SAlan Somers sig_atomic_t WriteRlimitFsize::s_sigxfsz = 0;
228be280f60SAlan Somers
sigxfsz_handler(int __unused sig)229a639731bSAlan Somers void sigxfsz_handler(int __unused sig) {
230be280f60SAlan Somers WriteRlimitFsize::s_sigxfsz = 1;
231a639731bSAlan Somers }
232a639731bSAlan Somers
2339821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */
2349821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */
TEST_F(AioWrite,DISABLED_aio_write)2359821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write)
2369821f1d3SAlan Somers {
2379821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
2389821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
2399821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
2409821f1d3SAlan Somers uint64_t ino = 42;
2419821f1d3SAlan Somers uint64_t offset = 4096;
2429821f1d3SAlan Somers int fd;
2439821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
2449821f1d3SAlan Somers struct aiocb iocb, *piocb;
2459821f1d3SAlan Somers
2469821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
2479821f1d3SAlan Somers expect_open(ino, 0, 1);
248bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS);
2499821f1d3SAlan Somers
2509821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY);
251d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
2529821f1d3SAlan Somers
2539821f1d3SAlan Somers iocb.aio_nbytes = bufsize;
2549821f1d3SAlan Somers iocb.aio_fildes = fd;
2555a0b9a27SAlan Somers iocb.aio_buf = __DECONST(void *, CONTENTS);
2569821f1d3SAlan Somers iocb.aio_offset = offset;
2579821f1d3SAlan Somers iocb.aio_sigevent.sigev_notify = SIGEV_NONE;
2589821f1d3SAlan Somers ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno);
2599821f1d3SAlan Somers ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno);
2607fc0921dSAlan Somers leak(fd);
2619821f1d3SAlan Somers }
2629821f1d3SAlan Somers
2639821f1d3SAlan Somers /*
2649821f1d3SAlan Somers * When a file is opened with O_APPEND, we should forward that flag to
2659821f1d3SAlan Somers * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the
2669821f1d3SAlan Somers * offset internally. That way we'll work both with filesystems that
2679821f1d3SAlan Somers * understand O_APPEND (and ignore the offset) and filesystems that don't (and
2689821f1d3SAlan Somers * simply use the offset).
2699821f1d3SAlan Somers *
2709821f1d3SAlan Somers * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the
2719821f1d3SAlan Somers * Open.o_append test.
2729821f1d3SAlan Somers */
TEST_F(Write,append)2739821f1d3SAlan Somers TEST_F(Write, append)
2749821f1d3SAlan Somers {
2759821f1d3SAlan Somers const ssize_t BUFSIZE = 9;
2769821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
2779821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
2789821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh";
2799821f1d3SAlan Somers uint64_t ino = 42;
2809821f1d3SAlan Somers /*
2819821f1d3SAlan Somers * Set offset to a maxbcachebuf boundary so we don't need to RMW when
2829821f1d3SAlan Somers * using writeback caching
2839821f1d3SAlan Somers */
2849821f1d3SAlan Somers uint64_t initial_offset = m_maxbcachebuf;
2859821f1d3SAlan Somers int fd;
2869821f1d3SAlan Somers
2879821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset);
2889821f1d3SAlan Somers expect_open(ino, 0, 1);
289bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
2909821f1d3SAlan Somers
2919821f1d3SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
2929821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND);
293d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
2949821f1d3SAlan Somers
2959821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
2967fc0921dSAlan Somers leak(fd);
2979821f1d3SAlan Somers }
2989821f1d3SAlan Somers
299a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */
TEST_F(Write,append_to_cached)300a87e0831SAlan Somers TEST_F(Write, append_to_cached)
301a87e0831SAlan Somers {
302a87e0831SAlan Somers const ssize_t BUFSIZE = 9;
303a87e0831SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
304a87e0831SAlan Somers const char RELPATH[] = "some_file.txt";
305a87e0831SAlan Somers char *oldcontents, *oldbuf;
306a87e0831SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh";
307a87e0831SAlan Somers uint64_t ino = 42;
308a87e0831SAlan Somers /*
309a87e0831SAlan Somers * Set offset in between maxbcachebuf boundary to test buffer handling
310a87e0831SAlan Somers */
311a87e0831SAlan Somers uint64_t oldsize = m_maxbcachebuf / 2;
312a87e0831SAlan Somers int fd;
313a87e0831SAlan Somers
3148bae22bbSAlan Somers oldcontents = new char[oldsize]();
3158bae22bbSAlan Somers oldbuf = new char[oldsize];
316a87e0831SAlan Somers
317a87e0831SAlan Somers expect_lookup(RELPATH, ino, oldsize);
318a87e0831SAlan Somers expect_open(ino, 0, 1);
319a87e0831SAlan Somers expect_read(ino, 0, oldsize, oldsize, oldcontents);
32084879e46SAlan Somers maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS);
321a87e0831SAlan Somers
322a87e0831SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */
323a87e0831SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND);
324d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
325a87e0831SAlan Somers
326a87e0831SAlan Somers /* Read the old data into the cache */
327a87e0831SAlan Somers ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize))
328a87e0831SAlan Somers << strerror(errno);
329a87e0831SAlan Somers
330a87e0831SAlan Somers /* Write the new data. There should be no more read operations */
331a87e0831SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3327fc0921dSAlan Somers leak(fd);
3338bae22bbSAlan Somers delete[] oldbuf;
3348bae22bbSAlan Somers delete[] oldcontents;
335a87e0831SAlan Somers }
336a87e0831SAlan Somers
TEST_F(Write,append_direct_io)3379821f1d3SAlan Somers TEST_F(Write, append_direct_io)
3389821f1d3SAlan Somers {
3399821f1d3SAlan Somers const ssize_t BUFSIZE = 9;
3409821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
3419821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
3429821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh";
3439821f1d3SAlan Somers uint64_t ino = 42;
3449821f1d3SAlan Somers uint64_t initial_offset = 4096;
3459821f1d3SAlan Somers int fd;
3469821f1d3SAlan Somers
3479821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset);
3489821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1);
349bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS);
3509821f1d3SAlan Somers
3519821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY | O_APPEND);
352d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
3539821f1d3SAlan Somers
3549821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno);
3557fc0921dSAlan Somers leak(fd);
3569821f1d3SAlan Somers }
3579821f1d3SAlan Somers
3589821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */
TEST_F(Write,direct_io_evicts_cache)3596af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache)
3609821f1d3SAlan Somers {
3619821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
3629821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
3639821f1d3SAlan Somers const char CONTENTS0[] = "abcdefgh";
3649821f1d3SAlan Somers const char CONTENTS1[] = "ijklmnop";
3659821f1d3SAlan Somers uint64_t ino = 42;
3669821f1d3SAlan Somers int fd;
3679821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1;
3689821f1d3SAlan Somers char readbuf[bufsize];
3699821f1d3SAlan Somers
3709821f1d3SAlan Somers expect_lookup(RELPATH, ino, bufsize);
3719821f1d3SAlan Somers expect_open(ino, 0, 1);
3729821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
373bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS1);
3749821f1d3SAlan Somers
3759821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
376d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
3779821f1d3SAlan Somers
3789821f1d3SAlan Somers // Prime cache
3799821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3809821f1d3SAlan Somers
3819821f1d3SAlan Somers // Write directly, evicting cache
3829821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
3839821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3849821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
3859821f1d3SAlan Somers
3869821f1d3SAlan Somers // Read again. Cache should be bypassed
3879821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
3889821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
3899821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
3909821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
3919821f1d3SAlan Somers ASSERT_STREQ(readbuf, CONTENTS1);
3929821f1d3SAlan Somers
3937fc0921dSAlan Somers leak(fd);
3949821f1d3SAlan Somers }
3959821f1d3SAlan Somers
3969821f1d3SAlan Somers /*
39712292a99SAlan Somers * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not
39812292a99SAlan Somers * allowed to return a short write for that file handle. However, if it does
39912292a99SAlan Somers * then we should still do our darndest to handle it by resending the unwritten
40012292a99SAlan Somers * portion.
4019821f1d3SAlan Somers */
TEST_F(Write,indirect_io_short_write)40212292a99SAlan Somers TEST_F(Write, indirect_io_short_write)
4039821f1d3SAlan Somers {
4049821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
4059821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
4069821f1d3SAlan Somers const char *CONTENTS = "abcdefghijklmnop";
4079821f1d3SAlan Somers uint64_t ino = 42;
4089821f1d3SAlan Somers int fd;
4099821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
41012292a99SAlan Somers ssize_t bufsize0 = 11;
41112292a99SAlan Somers ssize_t bufsize1 = strlen(CONTENTS) - bufsize0;
41212292a99SAlan Somers const char *contents1 = CONTENTS + bufsize0;
4139821f1d3SAlan Somers
4149821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
41512292a99SAlan Somers expect_open(ino, 0, 1);
416bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize0, CONTENTS);
417bda39894SAlan Somers expect_write(ino, bufsize0, bufsize1, bufsize1, contents1);
4189821f1d3SAlan Somers
4199821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY);
420d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
4219821f1d3SAlan Somers
4229821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
4237fc0921dSAlan Somers leak(fd);
4249821f1d3SAlan Somers }
4259821f1d3SAlan Somers
4263a1b3c6aSAlan Somers /* It is an error if the daemon claims to have written more data than we sent */
TEST_F(Write,indirect_io_long_write)4273a1b3c6aSAlan Somers TEST_F(Write, indirect_io_long_write)
4283a1b3c6aSAlan Somers {
4293a1b3c6aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
4303a1b3c6aSAlan Somers const char RELPATH[] = "some_file.txt";
4313a1b3c6aSAlan Somers const char *CONTENTS = "abcdefghijklmnop";
4323a1b3c6aSAlan Somers uint64_t ino = 42;
4333a1b3c6aSAlan Somers int fd;
4343a1b3c6aSAlan Somers ssize_t bufsize = strlen(CONTENTS);
4353a1b3c6aSAlan Somers ssize_t bufsize_out = 100;
4363a1b3c6aSAlan Somers off_t some_other_size = 25;
4373a1b3c6aSAlan Somers struct stat sb;
4383a1b3c6aSAlan Somers
4393a1b3c6aSAlan Somers expect_lookup(RELPATH, ino, 0);
4403a1b3c6aSAlan Somers expect_open(ino, 0, 1);
4413a1b3c6aSAlan Somers expect_write(ino, 0, bufsize, bufsize_out, CONTENTS);
4423a1b3c6aSAlan Somers expect_getattr(ino, some_other_size);
4433a1b3c6aSAlan Somers
4443a1b3c6aSAlan Somers fd = open(FULLPATH, O_WRONLY);
4453a1b3c6aSAlan Somers ASSERT_LE(0, fd) << strerror(errno);
4463a1b3c6aSAlan Somers
4473a1b3c6aSAlan Somers ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno);
4483a1b3c6aSAlan Somers ASSERT_EQ(EINVAL, errno);
4493a1b3c6aSAlan Somers
4503a1b3c6aSAlan Somers /*
4513a1b3c6aSAlan Somers * Following such an error, we should requery the server for the file's
4523a1b3c6aSAlan Somers * size.
4533a1b3c6aSAlan Somers */
4543a1b3c6aSAlan Somers fstat(fd, &sb);
4553a1b3c6aSAlan Somers ASSERT_EQ(sb.st_size, some_other_size);
4563a1b3c6aSAlan Somers
4573a1b3c6aSAlan Somers leak(fd);
4583a1b3c6aSAlan Somers }
4593a1b3c6aSAlan Somers
4603a1b3c6aSAlan Somers /*
4613a1b3c6aSAlan Somers * Don't crash if the server returns a write that can't be represented as a
4623a1b3c6aSAlan Somers * signed 32 bit number. Regression test for
4633a1b3c6aSAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263263
4643a1b3c6aSAlan Somers */
TEST_F(Write,indirect_io_very_long_write)4653a1b3c6aSAlan Somers TEST_F(Write, indirect_io_very_long_write)
4663a1b3c6aSAlan Somers {
4673a1b3c6aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
4683a1b3c6aSAlan Somers const char RELPATH[] = "some_file.txt";
4693a1b3c6aSAlan Somers const char *CONTENTS = "abcdefghijklmnop";
4703a1b3c6aSAlan Somers uint64_t ino = 42;
4713a1b3c6aSAlan Somers int fd;
4723a1b3c6aSAlan Somers ssize_t bufsize = strlen(CONTENTS);
4733a1b3c6aSAlan Somers ssize_t bufsize_out = 3 << 30;
4743a1b3c6aSAlan Somers
4753a1b3c6aSAlan Somers expect_lookup(RELPATH, ino, 0);
4763a1b3c6aSAlan Somers expect_open(ino, 0, 1);
4773a1b3c6aSAlan Somers expect_write(ino, 0, bufsize, bufsize_out, CONTENTS);
4783a1b3c6aSAlan Somers
4793a1b3c6aSAlan Somers fd = open(FULLPATH, O_WRONLY);
4803a1b3c6aSAlan Somers ASSERT_LE(0, fd) << strerror(errno);
4813a1b3c6aSAlan Somers
4823a1b3c6aSAlan Somers ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno);
4833a1b3c6aSAlan Somers ASSERT_EQ(EINVAL, errno);
4843a1b3c6aSAlan Somers leak(fd);
4853a1b3c6aSAlan Somers }
4863a1b3c6aSAlan Somers
4879821f1d3SAlan Somers /*
48812292a99SAlan Somers * When the direct_io option is used, filesystems are allowed to write less
48912292a99SAlan Somers * data than requested. We should return the short write to userland.
49012292a99SAlan Somers */
TEST_F(Write,direct_io_short_write)49112292a99SAlan Somers TEST_F(Write, direct_io_short_write)
49212292a99SAlan Somers {
49312292a99SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
49412292a99SAlan Somers const char RELPATH[] = "some_file.txt";
49512292a99SAlan Somers const char *CONTENTS = "abcdefghijklmnop";
49612292a99SAlan Somers uint64_t ino = 42;
49712292a99SAlan Somers int fd;
49812292a99SAlan Somers ssize_t bufsize = strlen(CONTENTS);
49912292a99SAlan Somers ssize_t halfbufsize = bufsize / 2;
50012292a99SAlan Somers
50112292a99SAlan Somers expect_lookup(RELPATH, ino, 0);
50212292a99SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1);
503bda39894SAlan Somers expect_write(ino, 0, bufsize, halfbufsize, CONTENTS);
50412292a99SAlan Somers
50512292a99SAlan Somers fd = open(FULLPATH, O_WRONLY);
506d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
50712292a99SAlan Somers
50812292a99SAlan Somers ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
5097fc0921dSAlan Somers leak(fd);
51012292a99SAlan Somers }
51112292a99SAlan Somers
51212292a99SAlan Somers /*
5139821f1d3SAlan Somers * An insidious edge case: the filesystem returns a short write, and the
5149821f1d3SAlan Somers * difference between what we requested and what it actually wrote crosses an
5159821f1d3SAlan Somers * iov element boundary
5169821f1d3SAlan Somers */
TEST_F(Write,direct_io_short_write_iov)51712292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov)
5189821f1d3SAlan Somers {
5199821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
5209821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
5219821f1d3SAlan Somers const char *CONTENTS0 = "abcdefgh";
5229821f1d3SAlan Somers const char *CONTENTS1 = "ijklmnop";
5239821f1d3SAlan Somers const char *EXPECTED0 = "abcdefghijklmnop";
5249821f1d3SAlan Somers uint64_t ino = 42;
5259821f1d3SAlan Somers int fd;
5269821f1d3SAlan Somers ssize_t size0 = strlen(CONTENTS0) - 1;
5279821f1d3SAlan Somers ssize_t size1 = strlen(CONTENTS1) + 1;
5289821f1d3SAlan Somers ssize_t totalsize = size0 + size1;
5299821f1d3SAlan Somers struct iovec iov[2];
5309821f1d3SAlan Somers
5319821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
5329821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1);
533bda39894SAlan Somers expect_write(ino, 0, totalsize, size0, EXPECTED0);
5349821f1d3SAlan Somers
5359821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY);
536d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
5379821f1d3SAlan Somers
5385a0b9a27SAlan Somers iov[0].iov_base = __DECONST(void*, CONTENTS0);
5399821f1d3SAlan Somers iov[0].iov_len = strlen(CONTENTS0);
5405a0b9a27SAlan Somers iov[1].iov_base = __DECONST(void*, CONTENTS1);
5419821f1d3SAlan Somers iov[1].iov_len = strlen(CONTENTS1);
54212292a99SAlan Somers ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno);
5437fc0921dSAlan Somers leak(fd);
5449821f1d3SAlan Somers }
5459821f1d3SAlan Somers
546a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */
TEST_P(WriteRlimitFsize,rlimit_fsize)547be280f60SAlan Somers TEST_P(WriteRlimitFsize, rlimit_fsize)
548a639731bSAlan Somers {
549a639731bSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
550a639731bSAlan Somers const char RELPATH[] = "some_file.txt";
551a639731bSAlan Somers const char *CONTENTS = "abcdefgh";
552a639731bSAlan Somers struct rlimit rl;
553a639731bSAlan Somers ssize_t bufsize = strlen(CONTENTS);
554a639731bSAlan Somers off_t offset = 1'000'000'000;
555a639731bSAlan Somers uint64_t ino = 42;
556be280f60SAlan Somers int fd, oflag;
557be280f60SAlan Somers
558be280f60SAlan Somers oflag = GetParam();
559a639731bSAlan Somers
560a639731bSAlan Somers expect_lookup(RELPATH, ino, 0);
561a639731bSAlan Somers expect_open(ino, 0, 1);
562a639731bSAlan Somers
563a639731bSAlan Somers rl.rlim_cur = offset;
564be280f60SAlan Somers rl.rlim_max = m_initial_limit.rlim_max;
565a639731bSAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
566a639731bSAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
567a639731bSAlan Somers
568be280f60SAlan Somers fd = open(FULLPATH, O_WRONLY | oflag);
569a639731bSAlan Somers
570d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
571a639731bSAlan Somers
572a639731bSAlan Somers ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset));
573a639731bSAlan Somers EXPECT_EQ(EFBIG, errno);
574a639731bSAlan Somers EXPECT_EQ(1, s_sigxfsz);
5757fc0921dSAlan Somers leak(fd);
576a639731bSAlan Somers }
577a639731bSAlan Somers
5789821f1d3SAlan Somers /*
579be280f60SAlan Somers * When crossing the RLIMIT_FSIZE boundary, writes should be truncated, not
580be280f60SAlan Somers * aborted.
581be280f60SAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=164793
582be280f60SAlan Somers */
TEST_P(WriteRlimitFsize,rlimit_fsize_truncate)583be280f60SAlan Somers TEST_P(WriteRlimitFsize, rlimit_fsize_truncate)
584be280f60SAlan Somers {
585be280f60SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
586be280f60SAlan Somers const char RELPATH[] = "some_file.txt";
587be280f60SAlan Somers const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz";
588be280f60SAlan Somers struct rlimit rl;
589be280f60SAlan Somers ssize_t bufsize = strlen(CONTENTS);
590be280f60SAlan Somers uint64_t ino = 42;
591be280f60SAlan Somers off_t offset = 1 << 30;
592be280f60SAlan Somers off_t limit = offset + strlen(CONTENTS) / 2;
593be280f60SAlan Somers int fd, oflag;
594be280f60SAlan Somers
595be280f60SAlan Somers oflag = GetParam();
596be280f60SAlan Somers
597be280f60SAlan Somers expect_lookup(RELPATH, ino, 0);
598be280f60SAlan Somers expect_open(ino, 0, 1);
599be280f60SAlan Somers expect_write(ino, offset, bufsize / 2, bufsize / 2, CONTENTS);
600be280f60SAlan Somers
601be280f60SAlan Somers rl.rlim_cur = limit;
602be280f60SAlan Somers rl.rlim_max = m_initial_limit.rlim_max;
603be280f60SAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno);
604be280f60SAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno);
605be280f60SAlan Somers
606be280f60SAlan Somers fd = open(FULLPATH, O_WRONLY | oflag);
607be280f60SAlan Somers
608be280f60SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
609be280f60SAlan Somers
610be280f60SAlan Somers ASSERT_EQ(bufsize / 2, pwrite(fd, CONTENTS, bufsize, offset))
611be280f60SAlan Somers << strerror(errno);
612be280f60SAlan Somers leak(fd);
613be280f60SAlan Somers }
614be280f60SAlan Somers
615811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(W, WriteRlimitFsize,
616be280f60SAlan Somers Values(0, O_DIRECT)
617be280f60SAlan Somers );
618be280f60SAlan Somers
619be280f60SAlan Somers /*
620b9e20197SAlan Somers * A short read indicates EOF. Test that nothing bad happens if we get EOF
621b9e20197SAlan Somers * during the R of a RMW operation.
622b9e20197SAlan Somers */
TEST_F(Write,eof_during_rmw)623f8ebf1cdSAlan Somers TEST_F(Write, eof_during_rmw)
624b9e20197SAlan Somers {
625b9e20197SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
626b9e20197SAlan Somers const char RELPATH[] = "some_file.txt";
627b9e20197SAlan Somers const char *CONTENTS = "abcdefgh";
628b9e20197SAlan Somers const char *INITIAL = "XXXXXXXXXX";
629b9e20197SAlan Somers uint64_t ino = 42;
630b9e20197SAlan Somers uint64_t offset = 1;
631ae39db74SAlan Somers ssize_t bufsize = strlen(CONTENTS) + 1;
632b9e20197SAlan Somers off_t orig_fsize = 10;
633b9e20197SAlan Somers off_t truncated_fsize = 5;
634b9e20197SAlan Somers int fd;
635b9e20197SAlan Somers
636b9e20197SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1);
637b9e20197SAlan Somers expect_open(ino, 0, 1);
638b9e20197SAlan Somers expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR);
639b9e20197SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS);
640b9e20197SAlan Somers
641b9e20197SAlan Somers fd = open(FULLPATH, O_RDWR);
642d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
643b9e20197SAlan Somers
644b9e20197SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
645b9e20197SAlan Somers << strerror(errno);
6467fc0921dSAlan Somers leak(fd);
647b9e20197SAlan Somers }
648b9e20197SAlan Somers
649b9e20197SAlan Somers /*
650032a5bd5SAlan Somers * VOP_STRATEGY should not query the server for the file's size, even if its
651032a5bd5SAlan Somers * cached attributes have expired.
652032a5bd5SAlan Somers * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
653032a5bd5SAlan Somers */
TEST_P(WriteEofDuringVnopStrategy,eof_during_vop_strategy)654032a5bd5SAlan Somers TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy)
655032a5bd5SAlan Somers {
656032a5bd5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
657032a5bd5SAlan Somers const char RELPATH[] = "some_file.txt";
658032a5bd5SAlan Somers Sequence seq;
659032a5bd5SAlan Somers const off_t filesize = 2 * m_maxbcachebuf;
6608bae22bbSAlan Somers char *contents;
661032a5bd5SAlan Somers uint64_t ino = 42;
662032a5bd5SAlan Somers uint64_t attr_valid = 0;
663032a5bd5SAlan Somers uint64_t attr_valid_nsec = 0;
664032a5bd5SAlan Somers mode_t mode = S_IFREG | 0644;
665032a5bd5SAlan Somers int fd;
666032a5bd5SAlan Somers int ngetattrs;
667032a5bd5SAlan Somers
668032a5bd5SAlan Somers ngetattrs = GetParam();
6698bae22bbSAlan Somers contents = new char[filesize]();
670032a5bd5SAlan Somers
671032a5bd5SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
672032a5bd5SAlan Somers .WillRepeatedly(Invoke(
673032a5bd5SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) {
674032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, entry);
675032a5bd5SAlan Somers out.body.entry.attr.mode = mode;
676032a5bd5SAlan Somers out.body.entry.nodeid = ino;
677032a5bd5SAlan Somers out.body.entry.attr.nlink = 1;
678032a5bd5SAlan Somers out.body.entry.attr.size = filesize;
679032a5bd5SAlan Somers out.body.entry.attr_valid = attr_valid;
680032a5bd5SAlan Somers out.body.entry.attr_valid_nsec = attr_valid_nsec;
681032a5bd5SAlan Somers })));
682032a5bd5SAlan Somers expect_open(ino, 0, 1);
683032a5bd5SAlan Somers EXPECT_CALL(*m_mock, process(
684032a5bd5SAlan Somers ResultOf([=](auto in) {
685032a5bd5SAlan Somers return (in.header.opcode == FUSE_GETATTR &&
686032a5bd5SAlan Somers in.header.nodeid == ino);
687032a5bd5SAlan Somers }, Eq(true)),
688032a5bd5SAlan Somers _)
689032a5bd5SAlan Somers ).Times(Between(ngetattrs - 1, ngetattrs))
690032a5bd5SAlan Somers .InSequence(seq)
691032a5bd5SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
692032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, attr);
693032a5bd5SAlan Somers out.body.attr.attr.ino = ino;
694032a5bd5SAlan Somers out.body.attr.attr.mode = mode;
695032a5bd5SAlan Somers out.body.attr.attr_valid = attr_valid;
696032a5bd5SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec;
697032a5bd5SAlan Somers out.body.attr.attr.size = filesize;
698032a5bd5SAlan Somers })));
699032a5bd5SAlan Somers EXPECT_CALL(*m_mock, process(
700032a5bd5SAlan Somers ResultOf([=](auto in) {
701032a5bd5SAlan Somers return (in.header.opcode == FUSE_GETATTR &&
702032a5bd5SAlan Somers in.header.nodeid == ino);
703032a5bd5SAlan Somers }, Eq(true)),
704032a5bd5SAlan Somers _)
705032a5bd5SAlan Somers ).InSequence(seq)
706032a5bd5SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
707032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, attr);
708032a5bd5SAlan Somers out.body.attr.attr.ino = ino;
709032a5bd5SAlan Somers out.body.attr.attr.mode = mode;
710032a5bd5SAlan Somers out.body.attr.attr_valid = attr_valid;
711032a5bd5SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec;
712032a5bd5SAlan Somers out.body.attr.attr.size = filesize / 2;
713032a5bd5SAlan Somers })));
714032a5bd5SAlan Somers expect_write(ino, 0, filesize / 2, filesize / 2, contents);
715032a5bd5SAlan Somers
716032a5bd5SAlan Somers fd = open(FULLPATH, O_RDWR);
717032a5bd5SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
718032a5bd5SAlan Somers ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2))
719032a5bd5SAlan Somers << strerror(errno);
720032a5bd5SAlan Somers
721032a5bd5SAlan Somers }
722032a5bd5SAlan Somers
723811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(W, WriteEofDuringVnopStrategy,
724032a5bd5SAlan Somers Values(1, 2, 3)
725032a5bd5SAlan Somers );
726032a5bd5SAlan Somers
727032a5bd5SAlan Somers /*
7289821f1d3SAlan Somers * If the kernel cannot be sure which uid, gid, or pid was responsible for a
7299821f1d3SAlan Somers * write, then it must set the FUSE_WRITE_CACHE bit
7309821f1d3SAlan Somers */
7319821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */
TEST_F(Write,mmap)732f8ebf1cdSAlan Somers TEST_F(Write, mmap)
7339821f1d3SAlan Somers {
7349821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
7359821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
7369821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
7379821f1d3SAlan Somers uint64_t ino = 42;
7389821f1d3SAlan Somers int fd;
7399821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
7409821f1d3SAlan Somers void *p;
7419821f1d3SAlan Somers uint64_t offset = 10;
7429821f1d3SAlan Somers size_t len;
7438bae22bbSAlan Somers char *zeros, *expected;
7449821f1d3SAlan Somers
7459821f1d3SAlan Somers len = getpagesize();
7469821f1d3SAlan Somers
7478bae22bbSAlan Somers zeros = new char[len]();
7488bae22bbSAlan Somers expected = new char[len]();
7499821f1d3SAlan Somers memmove((uint8_t*)expected + offset, CONTENTS, bufsize);
7509821f1d3SAlan Somers
7519821f1d3SAlan Somers expect_lookup(RELPATH, ino, len);
7529821f1d3SAlan Somers expect_open(ino, 0, 1);
7539821f1d3SAlan Somers expect_read(ino, 0, len, len, zeros);
7549821f1d3SAlan Somers /*
7559821f1d3SAlan Somers * Writes from the pager may or may not be associated with the correct
756cf437e2aSAlan Somers * pid, so they must set FUSE_WRITE_CACHE.
7579821f1d3SAlan Somers */
758bda39894SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected);
7599f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0));
7609821f1d3SAlan Somers expect_release(ino, ReturnErrno(0));
7619821f1d3SAlan Somers
7629821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
763d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
7649821f1d3SAlan Somers
7659821f1d3SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
7669821f1d3SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno);
7679821f1d3SAlan Somers
7689821f1d3SAlan Somers memmove((uint8_t*)p + offset, CONTENTS, bufsize);
7699821f1d3SAlan Somers
7709821f1d3SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
7719821f1d3SAlan Somers close(fd); // Write mmap'd data on close
7729821f1d3SAlan Somers
7738bae22bbSAlan Somers delete[] expected;
7748bae22bbSAlan Somers delete[] zeros;
7758e765737SAlan Somers
7768e765737SAlan Somers leak(fd);
7779821f1d3SAlan Somers }
7789821f1d3SAlan Somers
TEST_F(Write,pwrite)779f8ebf1cdSAlan Somers TEST_F(Write, pwrite)
7809821f1d3SAlan Somers {
7819821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
7829821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
7839821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
7849821f1d3SAlan Somers uint64_t ino = 42;
7856ca3b02bSAlan Somers uint64_t offset = m_maxbcachebuf;
7869821f1d3SAlan Somers int fd;
7879821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
7889821f1d3SAlan Somers
7899821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
7909821f1d3SAlan Somers expect_open(ino, 0, 1);
791bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS);
7929821f1d3SAlan Somers
7939821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY);
794d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
7959821f1d3SAlan Somers
7969821f1d3SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
7979821f1d3SAlan Somers << strerror(errno);
7987fc0921dSAlan Somers leak(fd);
7999821f1d3SAlan Somers }
8009821f1d3SAlan Somers
801788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */
TEST_F(Write,timestamps)802788af953SAlan Somers TEST_F(Write, timestamps)
803788af953SAlan Somers {
804788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
805788af953SAlan Somers const char RELPATH[] = "some_file.txt";
806788af953SAlan Somers const char *CONTENTS = "abcdefgh";
807788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS);
808788af953SAlan Somers uint64_t ino = 42;
809788af953SAlan Somers struct stat sb0, sb1;
810788af953SAlan Somers int fd;
811788af953SAlan Somers
812788af953SAlan Somers expect_lookup(RELPATH, ino, 0);
813788af953SAlan Somers expect_open(ino, 0, 1);
814788af953SAlan Somers maybe_expect_write(ino, 0, bufsize, CONTENTS);
815788af953SAlan Somers
816788af953SAlan Somers fd = open(FULLPATH, O_RDWR);
817d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
818788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno);
819788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
820788af953SAlan Somers
821788af953SAlan Somers nap();
822788af953SAlan Somers
823788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno);
824788af953SAlan Somers
825788af953SAlan Somers EXPECT_EQ(sb0.st_atime, sb1.st_atime);
826788af953SAlan Somers EXPECT_NE(sb0.st_mtime, sb1.st_mtime);
827788af953SAlan Somers EXPECT_NE(sb0.st_ctime, sb1.st_ctime);
8288e765737SAlan Somers
8298e765737SAlan Somers leak(fd);
830788af953SAlan Somers }
831788af953SAlan Somers
TEST_F(Write,write)8329821f1d3SAlan Somers TEST_F(Write, write)
8339821f1d3SAlan Somers {
8349821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
8359821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
8369821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
8379821f1d3SAlan Somers uint64_t ino = 42;
8389821f1d3SAlan Somers int fd;
8399821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
8409821f1d3SAlan Somers
8419821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
8429821f1d3SAlan Somers expect_open(ino, 0, 1);
843bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS);
8449821f1d3SAlan Somers
8459821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY);
846d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
8479821f1d3SAlan Somers
8489821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
8497fc0921dSAlan Somers leak(fd);
8509821f1d3SAlan Somers }
8519821f1d3SAlan Somers
8529821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */
TEST_F(WriteMaxWrite,write)853f928dbcbSAlan Somers TEST_F(WriteMaxWrite, write)
8549821f1d3SAlan Somers {
8559821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
8569821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
8579821f1d3SAlan Somers int *contents;
8589821f1d3SAlan Somers uint64_t ino = 42;
8599821f1d3SAlan Somers int fd;
8609821f1d3SAlan Somers ssize_t halfbufsize, bufsize;
8619821f1d3SAlan Somers
8628eecd9ceSAlan Somers halfbufsize = m_mock->m_maxwrite;
863e9b411d2SGleb Smirnoff if (halfbufsize >= m_maxbcachebuf ||
864e9b411d2SGleb Smirnoff (unsigned long )halfbufsize >= m_maxphys)
865f928dbcbSAlan Somers GTEST_SKIP() << "Must lower m_maxwrite for this test";
8669821f1d3SAlan Somers bufsize = halfbufsize * 2;
8678bae22bbSAlan Somers contents = new int[bufsize / sizeof(int)];
8689821f1d3SAlan Somers for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) {
8699821f1d3SAlan Somers contents[i] = i;
8709821f1d3SAlan Somers }
8719821f1d3SAlan Somers
8729821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
8739821f1d3SAlan Somers expect_open(ino, 0, 1);
874f2704f05SAlan Somers maybe_expect_write(ino, 0, halfbufsize, contents);
87584879e46SAlan Somers maybe_expect_write(ino, halfbufsize, halfbufsize,
8769821f1d3SAlan Somers &contents[halfbufsize / sizeof(int)]);
8779821f1d3SAlan Somers
8789821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY);
879d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
8809821f1d3SAlan Somers
8819821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno);
8827fc0921dSAlan Somers leak(fd);
8839821f1d3SAlan Somers
8848bae22bbSAlan Somers delete[] contents;
8859821f1d3SAlan Somers }
8869821f1d3SAlan Somers
TEST_F(Write,write_nothing)8879821f1d3SAlan Somers TEST_F(Write, write_nothing)
8889821f1d3SAlan Somers {
8899821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
8909821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
8919821f1d3SAlan Somers const char *CONTENTS = "";
8929821f1d3SAlan Somers uint64_t ino = 42;
8939821f1d3SAlan Somers int fd;
8949821f1d3SAlan Somers ssize_t bufsize = 0;
8959821f1d3SAlan Somers
8969821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
8979821f1d3SAlan Somers expect_open(ino, 0, 1);
8989821f1d3SAlan Somers
8999821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY);
900d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
9019821f1d3SAlan Somers
9029821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9037fc0921dSAlan Somers leak(fd);
9049821f1d3SAlan Somers }
9059821f1d3SAlan Somers
TEST_F(Write_7_8,write)90616bd2d47SAlan Somers TEST_F(Write_7_8, write)
90716bd2d47SAlan Somers {
90816bd2d47SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
90916bd2d47SAlan Somers const char RELPATH[] = "some_file.txt";
91016bd2d47SAlan Somers const char *CONTENTS = "abcdefgh";
91116bd2d47SAlan Somers uint64_t ino = 42;
91216bd2d47SAlan Somers int fd;
91316bd2d47SAlan Somers ssize_t bufsize = strlen(CONTENTS);
91416bd2d47SAlan Somers
91516bd2d47SAlan Somers expect_lookup(RELPATH, ino, 0);
91616bd2d47SAlan Somers expect_open(ino, 0, 1);
917bda39894SAlan Somers expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS);
91816bd2d47SAlan Somers
91916bd2d47SAlan Somers fd = open(FULLPATH, O_WRONLY);
920d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
92116bd2d47SAlan Somers
92216bd2d47SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9237fc0921dSAlan Somers leak(fd);
92416bd2d47SAlan Somers }
92516bd2d47SAlan Somers
9269821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */
TEST_F(WriteBackAsync,close)92784879e46SAlan Somers TEST_F(WriteBackAsync, close)
9289821f1d3SAlan Somers {
9299821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
9309821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
9319821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
9329821f1d3SAlan Somers uint64_t ino = 42;
9339821f1d3SAlan Somers int fd;
9349821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
9359821f1d3SAlan Somers
9369821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
9379821f1d3SAlan Somers expect_open(ino, 0, 1);
938bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS);
9399821f1d3SAlan Somers EXPECT_CALL(*m_mock, process(
9409821f1d3SAlan Somers ResultOf([=](auto in) {
94129edc611SAlan Somers return (in.header.opcode == FUSE_SETATTR);
9429821f1d3SAlan Somers }, Eq(true)),
9439821f1d3SAlan Somers _)
94429edc611SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
9459821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, attr);
94629edc611SAlan Somers out.body.attr.attr.ino = ino; // Must match nodeid
9479821f1d3SAlan Somers })));
9489f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0));
9499821f1d3SAlan Somers expect_release(ino, ReturnErrno(0));
9509821f1d3SAlan Somers
9519821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
9529821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
9539821f1d3SAlan Somers
9549821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
9559821f1d3SAlan Somers close(fd);
9569821f1d3SAlan Somers }
9579821f1d3SAlan Somers
9588eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */
TEST_F(WriteCluster,clustering)9598eecd9ceSAlan Somers TEST_F(WriteCluster, clustering)
9608eecd9ceSAlan Somers {
9618eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
9628eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt";
9638eecd9ceSAlan Somers uint64_t ino = 42;
9648eecd9ceSAlan Somers int i, fd;
9658bae22bbSAlan Somers char *wbuf, *wbuf2x;
9666ca3b02bSAlan Somers ssize_t bufsize = m_maxbcachebuf;
9676ca3b02bSAlan Somers off_t filesize = 5 * bufsize;
9688eecd9ceSAlan Somers
9698bae22bbSAlan Somers wbuf = new char[bufsize];
9708eecd9ceSAlan Somers memset(wbuf, 'X', bufsize);
9718bae22bbSAlan Somers wbuf2x = new char[2 * bufsize];
9728eecd9ceSAlan Somers memset(wbuf2x, 'X', 2 * bufsize);
9738eecd9ceSAlan Somers
9748eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize);
9758eecd9ceSAlan Somers expect_open(ino, 0, 1);
9768eecd9ceSAlan Somers /*
9778eecd9ceSAlan Somers * Writes of bufsize-bytes each should be clustered into greater sizes.
9788eecd9ceSAlan Somers * The amount of clustering is adaptive, so the first write actually
9798eecd9ceSAlan Somers * issued will be 2x bufsize and subsequent writes may be larger
9808eecd9ceSAlan Somers */
9818eecd9ceSAlan Somers expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x);
9828eecd9ceSAlan Somers expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x);
9838eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0));
9848eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0));
9858eecd9ceSAlan Somers
9868eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR);
9878eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno);
9888eecd9ceSAlan Somers
9898eecd9ceSAlan Somers for (i = 0; i < 4; i++) {
9908eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
9918eecd9ceSAlan Somers << strerror(errno);
9928eecd9ceSAlan Somers }
9938eecd9ceSAlan Somers close(fd);
9948bae22bbSAlan Somers delete[] wbuf2x;
9958bae22bbSAlan Somers delete[] wbuf;
9968eecd9ceSAlan Somers }
9978eecd9ceSAlan Somers
9988eecd9ceSAlan Somers /*
9998eecd9ceSAlan Somers * When clustering writes, an I/O error to any of the cluster's children should
10008eecd9ceSAlan Somers * not panic the system on unmount
10018eecd9ceSAlan Somers */
10028eecd9ceSAlan Somers /*
1003425bbe9eSAlan Somers * Regression test for bug 238585
10048eecd9ceSAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565
10058eecd9ceSAlan Somers */
TEST_F(WriteCluster,cluster_write_err)1006425bbe9eSAlan Somers TEST_F(WriteCluster, cluster_write_err)
10078eecd9ceSAlan Somers {
10088eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
10098eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt";
10108eecd9ceSAlan Somers uint64_t ino = 42;
10118eecd9ceSAlan Somers int i, fd;
10128bae22bbSAlan Somers char *wbuf;
10136ca3b02bSAlan Somers ssize_t bufsize = m_maxbcachebuf;
10146ca3b02bSAlan Somers off_t filesize = 4 * bufsize;
10158eecd9ceSAlan Somers
10168bae22bbSAlan Somers wbuf = new char[bufsize];
10178eecd9ceSAlan Somers memset(wbuf, 'X', bufsize);
10188eecd9ceSAlan Somers
10198eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize);
10208eecd9ceSAlan Somers expect_open(ino, 0, 1);
10218eecd9ceSAlan Somers EXPECT_CALL(*m_mock, process(
10228eecd9ceSAlan Somers ResultOf([=](auto in) {
10238eecd9ceSAlan Somers return (in.header.opcode == FUSE_WRITE);
10248eecd9ceSAlan Somers }, Eq(true)),
10258eecd9ceSAlan Somers _)
10268eecd9ceSAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(EIO)));
10278eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0));
10288eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0));
10298eecd9ceSAlan Somers
10308eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR);
10318eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno);
10328eecd9ceSAlan Somers
10338eecd9ceSAlan Somers for (i = 0; i < 3; i++) {
10348eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize))
10358eecd9ceSAlan Somers << strerror(errno);
10368eecd9ceSAlan Somers }
10378eecd9ceSAlan Somers close(fd);
10388bae22bbSAlan Somers delete[] wbuf;
10398eecd9ceSAlan Somers }
10408eecd9ceSAlan Somers
10419821f1d3SAlan Somers /*
10425fccbf31SAlan Somers * In writeback mode, writes to an O_WRONLY file could trigger reads from the
10435fccbf31SAlan Somers * server. The FUSE protocol explicitly allows that.
10445fccbf31SAlan Somers */
TEST_F(WriteBack,rmw)10455fccbf31SAlan Somers TEST_F(WriteBack, rmw)
10465fccbf31SAlan Somers {
10475fccbf31SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
10485fccbf31SAlan Somers const char RELPATH[] = "some_file.txt";
10495fccbf31SAlan Somers const char *CONTENTS = "abcdefgh";
10505fccbf31SAlan Somers const char *INITIAL = "XXXXXXXXXX";
10515fccbf31SAlan Somers uint64_t ino = 42;
10525fccbf31SAlan Somers uint64_t offset = 1;
10535fccbf31SAlan Somers off_t fsize = 10;
10545fccbf31SAlan Somers int fd;
10555fccbf31SAlan Somers ssize_t bufsize = strlen(CONTENTS);
10565fccbf31SAlan Somers
1057cad67791SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1);
10585fccbf31SAlan Somers expect_open(ino, 0, 1);
1059d4fd0c81SAlan Somers expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY);
106084879e46SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS);
10615fccbf31SAlan Somers
10625fccbf31SAlan Somers fd = open(FULLPATH, O_WRONLY);
1063d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
10645fccbf31SAlan Somers
10655fccbf31SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset))
10665fccbf31SAlan Somers << strerror(errno);
10677fc0921dSAlan Somers leak(fd);
10685fccbf31SAlan Somers }
10695fccbf31SAlan Somers
10705fccbf31SAlan Somers /*
10719821f1d3SAlan Somers * Without direct_io, writes should be committed to cache
10729821f1d3SAlan Somers */
TEST_F(WriteBack,cache)107384879e46SAlan Somers TEST_F(WriteBack, cache)
10749821f1d3SAlan Somers {
10759821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
10769821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
10779821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
10789821f1d3SAlan Somers uint64_t ino = 42;
10799821f1d3SAlan Somers int fd;
10809821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
10818e765737SAlan Somers uint8_t readbuf[bufsize];
10829821f1d3SAlan Somers
10839821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
10849821f1d3SAlan Somers expect_open(ino, 0, 1);
1085bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS);
10869821f1d3SAlan Somers
10879821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
1088d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
10899821f1d3SAlan Somers
10909821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
10919821f1d3SAlan Somers /*
10929821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the
10939821f1d3SAlan Somers * filesystem daemon
10949821f1d3SAlan Somers */
10959821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
10969821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
10977fc0921dSAlan Somers leak(fd);
10989821f1d3SAlan Somers }
10999821f1d3SAlan Somers
11009821f1d3SAlan Somers /*
11019821f1d3SAlan Somers * With O_DIRECT, writes should be not committed to cache. Admittedly this is
11029821f1d3SAlan Somers * an odd test, because it would be unusual to use O_DIRECT for writes but not
11039821f1d3SAlan Somers * reads.
11049821f1d3SAlan Somers */
TEST_F(WriteBack,o_direct)11059821f1d3SAlan Somers TEST_F(WriteBack, o_direct)
11069821f1d3SAlan Somers {
11079821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
11089821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
11099821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
11109821f1d3SAlan Somers uint64_t ino = 42;
11119821f1d3SAlan Somers int fd;
11129821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
11138e765737SAlan Somers uint8_t readbuf[bufsize];
11149821f1d3SAlan Somers
11159821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
11169821f1d3SAlan Somers expect_open(ino, 0, 1);
1117bda39894SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1118bda39894SAlan Somers CONTENTS);
11199821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS);
11209821f1d3SAlan Somers
11219821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_DIRECT);
1122d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
11239821f1d3SAlan Somers
11249821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
11259821f1d3SAlan Somers /* A subsequent read must query the daemon because cache is empty */
11269821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
11279821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
11289821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
11297fc0921dSAlan Somers leak(fd);
11309821f1d3SAlan Somers }
11319821f1d3SAlan Somers
TEST_F(WriteBack,direct_io)1132a62772a7SAlan Somers TEST_F(WriteBack, direct_io)
1133a62772a7SAlan Somers {
1134a62772a7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1135a62772a7SAlan Somers const char RELPATH[] = "some_file.txt";
1136a62772a7SAlan Somers const char *CONTENTS = "abcdefgh";
1137a62772a7SAlan Somers uint64_t ino = 42;
1138a62772a7SAlan Somers int fd;
1139a62772a7SAlan Somers ssize_t bufsize = strlen(CONTENTS);
1140a62772a7SAlan Somers uint8_t readbuf[bufsize];
1141a62772a7SAlan Somers
1142a62772a7SAlan Somers expect_lookup(RELPATH, ino, 0);
1143a62772a7SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1);
1144a62772a7SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE,
1145a62772a7SAlan Somers CONTENTS);
1146a62772a7SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS);
1147a62772a7SAlan Somers
1148a62772a7SAlan Somers fd = open(FULLPATH, O_RDWR);
1149d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1150a62772a7SAlan Somers
1151a62772a7SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1152a62772a7SAlan Somers /* A subsequent read must query the daemon because cache is empty */
1153a62772a7SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
1154a62772a7SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1155a62772a7SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
1156a62772a7SAlan Somers leak(fd);
1157a62772a7SAlan Somers }
1158a62772a7SAlan Somers
1159a62772a7SAlan Somers /*
1160a62772a7SAlan Somers * mmap should still be possible even if the server used direct_io. Mmap will
1161a62772a7SAlan Somers * still use the cache, though.
1162a62772a7SAlan Somers *
1163a62772a7SAlan Somers * Regression test for bug 247276
1164a62772a7SAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276
1165a62772a7SAlan Somers */
TEST_F(WriteBack,mmap_direct_io)1166a62772a7SAlan Somers TEST_F(WriteBack, mmap_direct_io)
1167a62772a7SAlan Somers {
1168a62772a7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1169a62772a7SAlan Somers const char RELPATH[] = "some_file.txt";
1170a62772a7SAlan Somers const char *CONTENTS = "abcdefgh";
1171a62772a7SAlan Somers uint64_t ino = 42;
1172a62772a7SAlan Somers int fd;
1173a62772a7SAlan Somers size_t len;
1174a62772a7SAlan Somers ssize_t bufsize = strlen(CONTENTS);
11758bae22bbSAlan Somers char *zeros;
11768bae22bbSAlan Somers void *p;
1177a62772a7SAlan Somers
1178a62772a7SAlan Somers len = getpagesize();
11798bae22bbSAlan Somers zeros = new char[len]();
1180a62772a7SAlan Somers
1181a62772a7SAlan Somers expect_lookup(RELPATH, ino, len);
1182a62772a7SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1);
1183a62772a7SAlan Somers expect_read(ino, 0, len, len, zeros);
1184a62772a7SAlan Somers expect_flush(ino, 1, ReturnErrno(0));
1185a62772a7SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros);
1186a62772a7SAlan Somers expect_release(ino, ReturnErrno(0));
1187a62772a7SAlan Somers
1188a62772a7SAlan Somers fd = open(FULLPATH, O_RDWR);
1189d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1190a62772a7SAlan Somers
1191a62772a7SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1192a62772a7SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno);
1193a62772a7SAlan Somers
1194a62772a7SAlan Somers memmove((uint8_t*)p, CONTENTS, bufsize);
1195a62772a7SAlan Somers
1196a62772a7SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno);
1197a62772a7SAlan Somers close(fd); // Write mmap'd data on close
1198a62772a7SAlan Somers
11998bae22bbSAlan Somers delete[] zeros;
1200a62772a7SAlan Somers }
1201a62772a7SAlan Somers
12029821f1d3SAlan Somers /*
120384879e46SAlan Somers * When mounted with -o async, the writeback cache mode should delay writes
120484879e46SAlan Somers */
TEST_F(WriteBackAsync,delay)120584879e46SAlan Somers TEST_F(WriteBackAsync, delay)
120684879e46SAlan Somers {
120784879e46SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
120884879e46SAlan Somers const char RELPATH[] = "some_file.txt";
120984879e46SAlan Somers const char *CONTENTS = "abcdefgh";
121084879e46SAlan Somers uint64_t ino = 42;
121184879e46SAlan Somers int fd;
121284879e46SAlan Somers ssize_t bufsize = strlen(CONTENTS);
121384879e46SAlan Somers
121484879e46SAlan Somers expect_lookup(RELPATH, ino, 0);
121584879e46SAlan Somers expect_open(ino, 0, 1);
121684879e46SAlan Somers /* Write should be cached, but FUSE_WRITE shouldn't be sent */
121784879e46SAlan Somers EXPECT_CALL(*m_mock, process(
121884879e46SAlan Somers ResultOf([=](auto in) {
121984879e46SAlan Somers return (in.header.opcode == FUSE_WRITE);
122084879e46SAlan Somers }, Eq(true)),
122184879e46SAlan Somers _)
122284879e46SAlan Somers ).Times(0);
122384879e46SAlan Somers
122484879e46SAlan Somers fd = open(FULLPATH, O_RDWR);
1225d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
122684879e46SAlan Somers
122784879e46SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
122884879e46SAlan Somers
122984879e46SAlan Somers /* Don't close the file because that would flush the cache */
12308e765737SAlan Somers leak(fd);
123184879e46SAlan Somers }
123284879e46SAlan Somers
123384879e46SAlan Somers /*
1234669a092aSAlan Somers * A direct write should not evict dirty cached data from outside of its own
1235669a092aSAlan Somers * byte range.
1236669a092aSAlan Somers */
TEST_F(WriteBackAsync,direct_io_ignores_unrelated_cached)1237669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached)
1238669a092aSAlan Somers {
1239669a092aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1240669a092aSAlan Somers const char RELPATH[] = "some_file.txt";
1241669a092aSAlan Somers const char CONTENTS0[] = "abcdefgh";
1242669a092aSAlan Somers const char CONTENTS1[] = "ijklmnop";
1243669a092aSAlan Somers uint64_t ino = 42;
1244669a092aSAlan Somers int fd;
1245669a092aSAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1;
1246669a092aSAlan Somers ssize_t fsize = 2 * m_maxbcachebuf;
1247669a092aSAlan Somers char readbuf[bufsize];
12488bae22bbSAlan Somers char *zeros;
1249669a092aSAlan Somers
12508bae22bbSAlan Somers zeros = new char[m_maxbcachebuf]();
1251669a092aSAlan Somers
1252669a092aSAlan Somers expect_lookup(RELPATH, ino, fsize);
1253669a092aSAlan Somers expect_open(ino, 0, 1);
1254669a092aSAlan Somers expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros);
1255669a092aSAlan Somers FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0,
1256669a092aSAlan Somers CONTENTS1);
1257669a092aSAlan Somers
1258669a092aSAlan Somers fd = open(FULLPATH, O_RDWR);
1259d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1260669a092aSAlan Somers
1261669a092aSAlan Somers // Cache first block with dirty data. This will entail first reading
1262669a092aSAlan Somers // the existing data.
1263669a092aSAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0))
1264669a092aSAlan Somers << strerror(errno);
1265669a092aSAlan Somers
1266669a092aSAlan Somers // Write directly to second block
1267669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1268669a092aSAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf))
1269669a092aSAlan Somers << strerror(errno);
1270669a092aSAlan Somers
1271669a092aSAlan Somers // Read from the first block again. Should be serviced by cache.
1272669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1273669a092aSAlan Somers ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno);
1274669a092aSAlan Somers ASSERT_STREQ(readbuf, CONTENTS0);
1275669a092aSAlan Somers
1276669a092aSAlan Somers leak(fd);
12778bae22bbSAlan Somers delete[] zeros;
1278669a092aSAlan Somers }
1279669a092aSAlan Somers
1280669a092aSAlan Somers /*
1281669a092aSAlan Somers * If a direct io write partially overlaps one or two blocks of dirty cached
1282669a092aSAlan Somers * data, No dirty data should be lost. Admittedly this is a weird test,
1283669a092aSAlan Somers * because it would be unusual to use O_DIRECT and the writeback cache.
1284669a092aSAlan Somers */
TEST_F(WriteBackAsync,direct_io_partially_overlaps_cached_block)1285669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block)
1286669a092aSAlan Somers {
1287669a092aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1288669a092aSAlan Somers const char RELPATH[] = "some_file.txt";
1289669a092aSAlan Somers uint64_t ino = 42;
1290669a092aSAlan Somers int fd;
1291669a092aSAlan Somers off_t bs = m_maxbcachebuf;
1292669a092aSAlan Somers ssize_t fsize = 3 * bs;
12938bae22bbSAlan Somers char *readbuf, *zeros, *ones, *zeroones, *onezeros;
1294669a092aSAlan Somers
12958bae22bbSAlan Somers readbuf = new char[bs];
12968bae22bbSAlan Somers zeros = new char[3 * bs]();
12978bae22bbSAlan Somers ones = new char[2 * bs];
1298669a092aSAlan Somers memset(ones, 1, 2 * bs);
12998bae22bbSAlan Somers zeroones = new char[bs]();
1300669a092aSAlan Somers memset((uint8_t*)zeroones + bs / 2, 1, bs / 2);
13018bae22bbSAlan Somers onezeros = new char[bs]();
1302669a092aSAlan Somers memset(onezeros, 1, bs / 2);
1303669a092aSAlan Somers
1304669a092aSAlan Somers expect_lookup(RELPATH, ino, fsize);
1305669a092aSAlan Somers expect_open(ino, 0, 1);
1306669a092aSAlan Somers
1307669a092aSAlan Somers fd = open(FULLPATH, O_RDWR);
1308d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1309669a092aSAlan Somers
1310669a092aSAlan Somers /* Cache first and third blocks with dirty data. */
1311669a092aSAlan Somers ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno);
1312669a092aSAlan Somers
1313669a092aSAlan Somers /*
1314669a092aSAlan Somers * Write directly to all three blocks. The partially written blocks
1315669a092aSAlan Somers * will be flushed because they're dirty.
1316669a092aSAlan Somers */
1317669a092aSAlan Somers FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros);
1318669a092aSAlan Somers FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros);
1319669a092aSAlan Somers /* The direct write is split in two because of the m_maxwrite value */
1320669a092aSAlan Somers FuseTest::expect_write(ino, bs / 2, bs, bs, 0, 0, ones);
1321669a092aSAlan Somers FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones);
1322669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno);
1323669a092aSAlan Somers ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno);
1324669a092aSAlan Somers
1325669a092aSAlan Somers /*
1326669a092aSAlan Somers * Read from both the valid and invalid portions of the first and third
1327669a092aSAlan Somers * blocks again. This will entail FUSE_READ operations because these
1328669a092aSAlan Somers * blocks were invalidated by the direct write.
1329669a092aSAlan Somers */
1330669a092aSAlan Somers expect_read(ino, 0, bs, bs, zeroones);
1331669a092aSAlan Somers expect_read(ino, 2 * bs, bs, bs, onezeros);
1332669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
1333669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno);
1334669a092aSAlan Somers EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1335669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2))
1336669a092aSAlan Somers << strerror(errno);
1337669a092aSAlan Somers EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2));
1338669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2))
1339669a092aSAlan Somers << strerror(errno);
1340669a092aSAlan Somers EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1341669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs))
1342669a092aSAlan Somers << strerror(errno);
1343669a092aSAlan Somers EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2));
1344669a092aSAlan Somers
1345669a092aSAlan Somers leak(fd);
13468bae22bbSAlan Somers delete[] zeroones;
13478bae22bbSAlan Somers delete[] onezeros;
13488bae22bbSAlan Somers delete[] ones;
13498bae22bbSAlan Somers delete[] zeros;
13508bae22bbSAlan Somers delete[] readbuf;
1351669a092aSAlan Somers }
1352669a092aSAlan Somers
1353669a092aSAlan Somers /*
1354aef22f2dSAlan Somers * In WriteBack mode, writes may be cached beyond what the server thinks is the
1355aef22f2dSAlan Somers * EOF. In this case, a short read at EOF should _not_ cause fusefs to update
1356aef22f2dSAlan Somers * the file's size.
1357aef22f2dSAlan Somers */
TEST_F(WriteBackAsync,eof)1358aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof)
1359aef22f2dSAlan Somers {
1360aef22f2dSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1361aef22f2dSAlan Somers const char RELPATH[] = "some_file.txt";
1362aef22f2dSAlan Somers const char *CONTENTS0 = "abcdefgh";
1363aef22f2dSAlan Somers const char *CONTENTS1 = "ijklmnop";
1364aef22f2dSAlan Somers uint64_t ino = 42;
1365aef22f2dSAlan Somers int fd;
1366aef22f2dSAlan Somers off_t offset = m_maxbcachebuf;
1367aef22f2dSAlan Somers ssize_t wbufsize = strlen(CONTENTS1);
1368aef22f2dSAlan Somers off_t old_filesize = (off_t)strlen(CONTENTS0);
1369aef22f2dSAlan Somers ssize_t rbufsize = 2 * old_filesize;
1370aef22f2dSAlan Somers char readbuf[rbufsize];
1371aef22f2dSAlan Somers size_t holesize = rbufsize - old_filesize;
1372aef22f2dSAlan Somers char hole[holesize];
1373aef22f2dSAlan Somers struct stat sb;
1374aef22f2dSAlan Somers ssize_t r;
1375aef22f2dSAlan Somers
1376aef22f2dSAlan Somers expect_lookup(RELPATH, ino, 0);
1377aef22f2dSAlan Somers expect_open(ino, 0, 1);
1378aef22f2dSAlan Somers expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0);
1379aef22f2dSAlan Somers
1380aef22f2dSAlan Somers fd = open(FULLPATH, O_RDWR);
1381d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1382aef22f2dSAlan Somers
1383aef22f2dSAlan Somers /* Write and cache data beyond EOF */
1384aef22f2dSAlan Somers ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset))
1385aef22f2dSAlan Somers << strerror(errno);
1386aef22f2dSAlan Somers
1387aef22f2dSAlan Somers /* Read from the old EOF */
1388aef22f2dSAlan Somers r = pread(fd, readbuf, rbufsize, 0);
1389aef22f2dSAlan Somers ASSERT_LE(0, r) << strerror(errno);
1390aef22f2dSAlan Somers EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole";
1391aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize));
1392aef22f2dSAlan Somers bzero(hole, holesize);
1393aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize));
1394aef22f2dSAlan Somers
1395aef22f2dSAlan Somers /* The file's size should still be what was established by pwrite */
1396aef22f2dSAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1397aef22f2dSAlan Somers EXPECT_EQ(offset + wbufsize, sb.st_size);
13987fc0921dSAlan Somers leak(fd);
1399aef22f2dSAlan Somers }
1400aef22f2dSAlan Somers
1401aef22f2dSAlan Somers /*
1402788af953SAlan Somers * When a file has dirty writes that haven't been flushed, the server's notion
1403788af953SAlan Somers * of its mtime and ctime will be wrong. The kernel should ignore those if it
1404788af953SAlan Somers * gets them from a FUSE_GETATTR before flushing.
1405788af953SAlan Somers */
TEST_F(WriteBackAsync,timestamps)1406788af953SAlan Somers TEST_F(WriteBackAsync, timestamps)
1407788af953SAlan Somers {
1408788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1409788af953SAlan Somers const char RELPATH[] = "some_file.txt";
1410788af953SAlan Somers const char *CONTENTS = "abcdefgh";
1411788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS);
1412788af953SAlan Somers uint64_t ino = 42;
1413788af953SAlan Somers uint64_t attr_valid = 0;
1414788af953SAlan Somers uint64_t attr_valid_nsec = 0;
1415788af953SAlan Somers uint64_t server_time = 12345;
1416788af953SAlan Somers mode_t mode = S_IFREG | 0644;
1417788af953SAlan Somers int fd;
1418788af953SAlan Somers
1419788af953SAlan Somers struct stat sb;
1420788af953SAlan Somers
1421788af953SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
1422788af953SAlan Somers .WillRepeatedly(Invoke(
1423788af953SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) {
1424788af953SAlan Somers SET_OUT_HEADER_LEN(out, entry);
1425788af953SAlan Somers out.body.entry.attr.mode = mode;
1426788af953SAlan Somers out.body.entry.nodeid = ino;
1427788af953SAlan Somers out.body.entry.attr.nlink = 1;
1428788af953SAlan Somers out.body.entry.attr_valid = attr_valid;
1429788af953SAlan Somers out.body.entry.attr_valid_nsec = attr_valid_nsec;
1430788af953SAlan Somers })));
1431788af953SAlan Somers expect_open(ino, 0, 1);
1432788af953SAlan Somers EXPECT_CALL(*m_mock, process(
1433788af953SAlan Somers ResultOf([=](auto in) {
1434788af953SAlan Somers return (in.header.opcode == FUSE_GETATTR &&
1435788af953SAlan Somers in.header.nodeid == ino);
1436788af953SAlan Somers }, Eq(true)),
1437788af953SAlan Somers _)
1438788af953SAlan Somers ).WillRepeatedly(Invoke(
1439788af953SAlan Somers ReturnImmediate([=](auto i __unused, auto& out) {
1440788af953SAlan Somers SET_OUT_HEADER_LEN(out, attr);
1441788af953SAlan Somers out.body.attr.attr.ino = ino;
1442788af953SAlan Somers out.body.attr.attr.mode = mode;
1443788af953SAlan Somers out.body.attr.attr_valid = attr_valid;
1444788af953SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec;
1445788af953SAlan Somers out.body.attr.attr.atime = server_time;
1446788af953SAlan Somers out.body.attr.attr.mtime = server_time;
1447788af953SAlan Somers out.body.attr.attr.ctime = server_time;
1448788af953SAlan Somers })));
1449788af953SAlan Somers
1450788af953SAlan Somers fd = open(FULLPATH, O_RDWR);
1451d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1452788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1453788af953SAlan Somers
1454788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
1455788af953SAlan Somers EXPECT_EQ((time_t)server_time, sb.st_atime);
1456788af953SAlan Somers EXPECT_NE((time_t)server_time, sb.st_mtime);
1457788af953SAlan Somers EXPECT_NE((time_t)server_time, sb.st_ctime);
14588e765737SAlan Somers
14598e765737SAlan Somers leak(fd);
1460788af953SAlan Somers }
1461788af953SAlan Somers
1462788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */
TEST_F(WriteBackAsync,timestamps_during_setattr)1463788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr)
1464788af953SAlan Somers {
1465788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1466788af953SAlan Somers const char RELPATH[] = "some_file.txt";
1467788af953SAlan Somers const char *CONTENTS = "abcdefgh";
1468788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS);
1469788af953SAlan Somers uint64_t ino = 42;
1470788af953SAlan Somers const mode_t newmode = 0755;
1471788af953SAlan Somers int fd;
1472788af953SAlan Somers
1473788af953SAlan Somers expect_lookup(RELPATH, ino, 0);
1474788af953SAlan Somers expect_open(ino, 0, 1);
1475788af953SAlan Somers EXPECT_CALL(*m_mock, process(
1476788af953SAlan Somers ResultOf([=](auto in) {
14770a8fe2d3SAlan Somers uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1478788af953SAlan Somers return (in.header.opcode == FUSE_SETATTR &&
1479788af953SAlan Somers in.header.nodeid == ino &&
1480788af953SAlan Somers in.body.setattr.valid == valid);
1481788af953SAlan Somers }, Eq(true)),
1482788af953SAlan Somers _)
1483788af953SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1484788af953SAlan Somers SET_OUT_HEADER_LEN(out, attr);
1485788af953SAlan Somers out.body.attr.attr.ino = ino;
1486788af953SAlan Somers out.body.attr.attr.mode = S_IFREG | newmode;
1487788af953SAlan Somers })));
1488788af953SAlan Somers
1489788af953SAlan Somers fd = open(FULLPATH, O_RDWR);
1490d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1491788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1492788af953SAlan Somers ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
14938e765737SAlan Somers
14948e765737SAlan Somers leak(fd);
1495788af953SAlan Somers }
1496788af953SAlan Somers
1497fef46454SAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */
TEST_P(TimeGran,timestamps_during_setattr)1498fef46454SAlan Somers TEST_P(TimeGran, timestamps_during_setattr)
1499fef46454SAlan Somers {
1500fef46454SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
1501fef46454SAlan Somers const char RELPATH[] = "some_file.txt";
1502fef46454SAlan Somers const char *CONTENTS = "abcdefgh";
1503fef46454SAlan Somers ssize_t bufsize = strlen(CONTENTS);
1504fef46454SAlan Somers uint64_t ino = 42;
1505fef46454SAlan Somers const mode_t newmode = 0755;
1506fef46454SAlan Somers int fd;
1507fef46454SAlan Somers
1508fef46454SAlan Somers expect_lookup(RELPATH, ino, 0);
1509fef46454SAlan Somers expect_open(ino, 0, 1);
1510fef46454SAlan Somers EXPECT_CALL(*m_mock, process(
1511fef46454SAlan Somers ResultOf([=](auto in) {
1512fef46454SAlan Somers uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME;
1513fef46454SAlan Somers return (in.header.opcode == FUSE_SETATTR &&
1514fef46454SAlan Somers in.header.nodeid == ino &&
1515fef46454SAlan Somers in.body.setattr.valid == valid &&
1516fef46454SAlan Somers in.body.setattr.mtimensec % m_time_gran == 0 &&
1517fef46454SAlan Somers in.body.setattr.ctimensec % m_time_gran == 0);
1518fef46454SAlan Somers }, Eq(true)),
1519fef46454SAlan Somers _)
1520fef46454SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) {
1521fef46454SAlan Somers SET_OUT_HEADER_LEN(out, attr);
1522fef46454SAlan Somers out.body.attr.attr.ino = ino;
1523fef46454SAlan Somers out.body.attr.attr.mode = S_IFREG | newmode;
1524fef46454SAlan Somers })));
1525fef46454SAlan Somers
1526fef46454SAlan Somers fd = open(FULLPATH, O_RDWR);
1527d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
1528fef46454SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
1529fef46454SAlan Somers ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno);
15308e765737SAlan Somers
15318e765737SAlan Somers leak(fd);
1532fef46454SAlan Somers }
1533fef46454SAlan Somers
1534811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(RA, TimeGran, Range(0u, 10u));
1535fef46454SAlan Somers
1536788af953SAlan Somers /*
15379821f1d3SAlan Somers * Without direct_io, writes should be committed to cache
15389821f1d3SAlan Somers */
TEST_F(Write,writethrough)1539f8ebf1cdSAlan Somers TEST_F(Write, writethrough)
15409821f1d3SAlan Somers {
15419821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
15429821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
15439821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
15449821f1d3SAlan Somers uint64_t ino = 42;
15459821f1d3SAlan Somers int fd;
15469821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
15478e765737SAlan Somers uint8_t readbuf[bufsize];
15489821f1d3SAlan Somers
15499821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
15509821f1d3SAlan Somers expect_open(ino, 0, 1);
1551bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS);
15529821f1d3SAlan Somers
15539821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
1554d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
15559821f1d3SAlan Somers
15569821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
15579821f1d3SAlan Somers /*
15589821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the
15599821f1d3SAlan Somers * filesystem daemon
15609821f1d3SAlan Somers */
1561b5aaf286SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
15629821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
15637fc0921dSAlan Somers leak(fd);
15649821f1d3SAlan Somers }
15659821f1d3SAlan Somers
15660d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */
TEST_F(Write,update_file_size)15670d3a88d7SAlan Somers TEST_F(Write, update_file_size)
15689821f1d3SAlan Somers {
15699821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt";
15709821f1d3SAlan Somers const char RELPATH[] = "some_file.txt";
15719821f1d3SAlan Somers const char *CONTENTS = "abcdefgh";
15729821f1d3SAlan Somers struct stat sb;
15739821f1d3SAlan Somers uint64_t ino = 42;
15749821f1d3SAlan Somers int fd;
15759821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS);
15769821f1d3SAlan Somers
15779821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0);
15789821f1d3SAlan Somers expect_open(ino, 0, 1);
1579bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS);
15809821f1d3SAlan Somers
15819821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR);
1582d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno);
15839821f1d3SAlan Somers
15849821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
15859821f1d3SAlan Somers /* Get cached attributes */
15869821f1d3SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno);
15879821f1d3SAlan Somers ASSERT_EQ(bufsize, sb.st_size);
15887fc0921dSAlan Somers leak(fd);
15899821f1d3SAlan Somers }
1590