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. 291fa8ebfbSAlan Somers * 301fa8ebfbSAlan Somers * $FreeBSD$ 319821f1d3SAlan Somers */ 329821f1d3SAlan Somers 339821f1d3SAlan Somers extern "C" { 348eecd9ceSAlan Somers #include <sys/param.h> 359821f1d3SAlan Somers #include <sys/mman.h> 36a639731bSAlan Somers #include <sys/resource.h> 379821f1d3SAlan Somers #include <sys/stat.h> 38a639731bSAlan Somers #include <sys/time.h> 399821f1d3SAlan Somers #include <sys/uio.h> 409821f1d3SAlan Somers 419821f1d3SAlan Somers #include <aio.h> 429821f1d3SAlan Somers #include <fcntl.h> 43a639731bSAlan Somers #include <signal.h> 449821f1d3SAlan Somers #include <unistd.h> 459821f1d3SAlan Somers } 469821f1d3SAlan Somers 479821f1d3SAlan Somers #include "mockfs.hh" 489821f1d3SAlan Somers #include "utils.hh" 499821f1d3SAlan Somers 509821f1d3SAlan Somers using namespace testing; 519821f1d3SAlan Somers 529821f1d3SAlan Somers class Write: public FuseTest { 539821f1d3SAlan Somers 549821f1d3SAlan Somers public: 55a639731bSAlan Somers void SetUp() { 56a639731bSAlan Somers FuseTest::SetUp(); 57a639731bSAlan Somers } 58a639731bSAlan Somers 59a639731bSAlan Somers void TearDown() { 60a639731bSAlan Somers struct sigaction sa; 61a639731bSAlan Somers 62a639731bSAlan Somers bzero(&sa, sizeof(sa)); 63a639731bSAlan Somers sa.sa_handler = SIG_DFL; 64a639731bSAlan Somers sigaction(SIGXFSZ, &sa, NULL); 65a639731bSAlan Somers 66a639731bSAlan Somers FuseTest::TearDown(); 67a639731bSAlan Somers } 689821f1d3SAlan Somers 699821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 709821f1d3SAlan Somers { 719821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); 729821f1d3SAlan Somers } 739821f1d3SAlan Somers 749821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r) 759821f1d3SAlan Somers { 769821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 779821f1d3SAlan Somers ResultOf([=](auto in) { 7829edc611SAlan Somers return (in.header.opcode == FUSE_RELEASE && 7929edc611SAlan Somers in.header.nodeid == ino); 809821f1d3SAlan Somers }, Eq(true)), 819821f1d3SAlan Somers _) 829821f1d3SAlan Somers ).WillRepeatedly(Invoke(r)); 839821f1d3SAlan Somers } 849821f1d3SAlan Somers 85bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 86bda39894SAlan Somers uint64_t osize, const void *contents) 87bda39894SAlan Somers { 88bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents); 89bda39894SAlan Somers } 90bda39894SAlan Somers 9184879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */ 9284879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size, 9384879e46SAlan Somers const void *contents) 9484879e46SAlan Somers { 9584879e46SAlan Somers EXPECT_CALL(*m_mock, process( 9684879e46SAlan Somers ResultOf([=](auto in) { 9784879e46SAlan Somers const char *buf = (const char*)in.body.bytes + 9884879e46SAlan Somers sizeof(struct fuse_write_in); 9984879e46SAlan Somers 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: 12116bd2d47SAlan Somers virtual void SetUp() { 12216bd2d47SAlan Somers m_kernel_minor_version = 8; 12316bd2d47SAlan Somers FuseTest::SetUp(); 12416bd2d47SAlan Somers } 12516bd2d47SAlan Somers 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 { 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: 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 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: 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: 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: 1808eecd9ceSAlan Somers virtual void SetUp() { 1818eecd9ceSAlan Somers m_async = true; 1828e765737SAlan Somers m_maxwrite = 1 << 25; // 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"; 187f2704f05SAlan Somers if (m_maxphys < 2 * 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: 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 209*be280f60SAlan Somers class WriteRlimitFsize: public Write, public WithParamInterface<int> { 210*be280f60SAlan Somers public: 211*be280f60SAlan Somers static sig_atomic_t s_sigxfsz; 212*be280f60SAlan Somers struct rlimit m_initial_limit; 213*be280f60SAlan Somers 214*be280f60SAlan Somers void SetUp() { 215*be280f60SAlan Somers s_sigxfsz = 0; 216*be280f60SAlan Somers getrlimit(RLIMIT_FSIZE, &m_initial_limit); 217*be280f60SAlan Somers FuseTest::SetUp(); 218*be280f60SAlan Somers } 219*be280f60SAlan Somers 220*be280f60SAlan Somers void TearDown() { 221*be280f60SAlan Somers setrlimit(RLIMIT_FSIZE, &m_initial_limit); 222*be280f60SAlan Somers 223*be280f60SAlan Somers FuseTest::TearDown(); 224*be280f60SAlan Somers } 225*be280f60SAlan Somers }; 226*be280f60SAlan Somers 227*be280f60SAlan Somers sig_atomic_t WriteRlimitFsize::s_sigxfsz = 0; 228*be280f60SAlan Somers 229a639731bSAlan Somers void sigxfsz_handler(int __unused sig) { 230*be280f60SAlan 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 */ 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 */ 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 */ 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 314a87e0831SAlan Somers oldcontents = (char*)calloc(1, oldsize); 3155a0b9a27SAlan Somers ASSERT_NE(nullptr, oldcontents) << strerror(errno); 316a87e0831SAlan Somers oldbuf = (char*)malloc(oldsize); 3175a0b9a27SAlan Somers ASSERT_NE(nullptr, oldbuf) << strerror(errno); 318a87e0831SAlan Somers 319a87e0831SAlan Somers expect_lookup(RELPATH, ino, oldsize); 320a87e0831SAlan Somers expect_open(ino, 0, 1); 321a87e0831SAlan Somers expect_read(ino, 0, oldsize, oldsize, oldcontents); 32284879e46SAlan Somers maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS); 323a87e0831SAlan Somers 324a87e0831SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ 325a87e0831SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND); 326d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 327a87e0831SAlan Somers 328a87e0831SAlan Somers /* Read the old data into the cache */ 329a87e0831SAlan Somers ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize)) 330a87e0831SAlan Somers << strerror(errno); 331a87e0831SAlan Somers 332a87e0831SAlan Somers /* Write the new data. There should be no more read operations */ 333a87e0831SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 3347fc0921dSAlan Somers leak(fd); 3357ee7e409SAlan Somers free(oldbuf); 3367ee7e409SAlan Somers free(oldcontents); 337a87e0831SAlan Somers } 338a87e0831SAlan Somers 3399821f1d3SAlan Somers TEST_F(Write, append_direct_io) 3409821f1d3SAlan Somers { 3419821f1d3SAlan Somers const ssize_t BUFSIZE = 9; 3429821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3439821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3449821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 3459821f1d3SAlan Somers uint64_t ino = 42; 3469821f1d3SAlan Somers uint64_t initial_offset = 4096; 3479821f1d3SAlan Somers int fd; 3489821f1d3SAlan Somers 3499821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset); 3509821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 351bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS); 3529821f1d3SAlan Somers 3539821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY | O_APPEND); 354d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 3559821f1d3SAlan Somers 3569821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 3577fc0921dSAlan Somers leak(fd); 3589821f1d3SAlan Somers } 3599821f1d3SAlan Somers 3609821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */ 3616af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache) 3629821f1d3SAlan Somers { 3639821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3649821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3659821f1d3SAlan Somers const char CONTENTS0[] = "abcdefgh"; 3669821f1d3SAlan Somers const char CONTENTS1[] = "ijklmnop"; 3679821f1d3SAlan Somers uint64_t ino = 42; 3689821f1d3SAlan Somers int fd; 3699821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1; 3709821f1d3SAlan Somers char readbuf[bufsize]; 3719821f1d3SAlan Somers 3729821f1d3SAlan Somers expect_lookup(RELPATH, ino, bufsize); 3739821f1d3SAlan Somers expect_open(ino, 0, 1); 3749821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS0); 375bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS1); 3769821f1d3SAlan Somers 3779821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 378d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 3799821f1d3SAlan Somers 3809821f1d3SAlan Somers // Prime cache 3819821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 3829821f1d3SAlan Somers 3839821f1d3SAlan Somers // Write directly, evicting cache 3849821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 3859821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 3869821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno); 3879821f1d3SAlan Somers 3889821f1d3SAlan Somers // Read again. Cache should be bypassed 3899821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS1); 3909821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 3919821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 3929821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 3939821f1d3SAlan Somers ASSERT_STREQ(readbuf, CONTENTS1); 3949821f1d3SAlan Somers 3957fc0921dSAlan Somers leak(fd); 3969821f1d3SAlan Somers } 3979821f1d3SAlan Somers 3989821f1d3SAlan Somers /* 39912292a99SAlan Somers * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not 40012292a99SAlan Somers * allowed to return a short write for that file handle. However, if it does 40112292a99SAlan Somers * then we should still do our darndest to handle it by resending the unwritten 40212292a99SAlan Somers * portion. 4039821f1d3SAlan Somers */ 40412292a99SAlan Somers TEST_F(Write, indirect_io_short_write) 4059821f1d3SAlan Somers { 4069821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4079821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 4089821f1d3SAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 4099821f1d3SAlan Somers uint64_t ino = 42; 4109821f1d3SAlan Somers int fd; 4119821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 41212292a99SAlan Somers ssize_t bufsize0 = 11; 41312292a99SAlan Somers ssize_t bufsize1 = strlen(CONTENTS) - bufsize0; 41412292a99SAlan Somers const char *contents1 = CONTENTS + bufsize0; 4159821f1d3SAlan Somers 4169821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 41712292a99SAlan Somers expect_open(ino, 0, 1); 418bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize0, CONTENTS); 419bda39894SAlan Somers expect_write(ino, bufsize0, bufsize1, bufsize1, contents1); 4209821f1d3SAlan Somers 4219821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 422d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 4239821f1d3SAlan Somers 4249821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 4257fc0921dSAlan Somers leak(fd); 4269821f1d3SAlan Somers } 4279821f1d3SAlan Somers 4283a1b3c6aSAlan Somers /* It is an error if the daemon claims to have written more data than we sent */ 4293a1b3c6aSAlan Somers TEST_F(Write, indirect_io_long_write) 4303a1b3c6aSAlan Somers { 4313a1b3c6aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4323a1b3c6aSAlan Somers const char RELPATH[] = "some_file.txt"; 4333a1b3c6aSAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 4343a1b3c6aSAlan Somers uint64_t ino = 42; 4353a1b3c6aSAlan Somers int fd; 4363a1b3c6aSAlan Somers ssize_t bufsize = strlen(CONTENTS); 4373a1b3c6aSAlan Somers ssize_t bufsize_out = 100; 4383a1b3c6aSAlan Somers off_t some_other_size = 25; 4393a1b3c6aSAlan Somers struct stat sb; 4403a1b3c6aSAlan Somers 4413a1b3c6aSAlan Somers expect_lookup(RELPATH, ino, 0); 4423a1b3c6aSAlan Somers expect_open(ino, 0, 1); 4433a1b3c6aSAlan Somers expect_write(ino, 0, bufsize, bufsize_out, CONTENTS); 4443a1b3c6aSAlan Somers expect_getattr(ino, some_other_size); 4453a1b3c6aSAlan Somers 4463a1b3c6aSAlan Somers fd = open(FULLPATH, O_WRONLY); 4473a1b3c6aSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 4483a1b3c6aSAlan Somers 4493a1b3c6aSAlan Somers ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno); 4503a1b3c6aSAlan Somers ASSERT_EQ(EINVAL, errno); 4513a1b3c6aSAlan Somers 4523a1b3c6aSAlan Somers /* 4533a1b3c6aSAlan Somers * Following such an error, we should requery the server for the file's 4543a1b3c6aSAlan Somers * size. 4553a1b3c6aSAlan Somers */ 4563a1b3c6aSAlan Somers fstat(fd, &sb); 4573a1b3c6aSAlan Somers ASSERT_EQ(sb.st_size, some_other_size); 4583a1b3c6aSAlan Somers 4593a1b3c6aSAlan Somers leak(fd); 4603a1b3c6aSAlan Somers } 4613a1b3c6aSAlan Somers 4623a1b3c6aSAlan Somers /* 4633a1b3c6aSAlan Somers * Don't crash if the server returns a write that can't be represented as a 4643a1b3c6aSAlan Somers * signed 32 bit number. Regression test for 4653a1b3c6aSAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=263263 4663a1b3c6aSAlan Somers */ 4673a1b3c6aSAlan Somers TEST_F(Write, indirect_io_very_long_write) 4683a1b3c6aSAlan Somers { 4693a1b3c6aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4703a1b3c6aSAlan Somers const char RELPATH[] = "some_file.txt"; 4713a1b3c6aSAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 4723a1b3c6aSAlan Somers uint64_t ino = 42; 4733a1b3c6aSAlan Somers int fd; 4743a1b3c6aSAlan Somers ssize_t bufsize = strlen(CONTENTS); 4753a1b3c6aSAlan Somers ssize_t bufsize_out = 3 << 30; 4763a1b3c6aSAlan Somers 4773a1b3c6aSAlan Somers expect_lookup(RELPATH, ino, 0); 4783a1b3c6aSAlan Somers expect_open(ino, 0, 1); 4793a1b3c6aSAlan Somers expect_write(ino, 0, bufsize, bufsize_out, CONTENTS); 4803a1b3c6aSAlan Somers 4813a1b3c6aSAlan Somers fd = open(FULLPATH, O_WRONLY); 4823a1b3c6aSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 4833a1b3c6aSAlan Somers 4843a1b3c6aSAlan Somers ASSERT_EQ(-1, write(fd, CONTENTS, bufsize)) << strerror(errno); 4853a1b3c6aSAlan Somers ASSERT_EQ(EINVAL, errno); 4863a1b3c6aSAlan Somers leak(fd); 4873a1b3c6aSAlan Somers } 4883a1b3c6aSAlan Somers 4899821f1d3SAlan Somers /* 49012292a99SAlan Somers * When the direct_io option is used, filesystems are allowed to write less 49112292a99SAlan Somers * data than requested. We should return the short write to userland. 49212292a99SAlan Somers */ 49312292a99SAlan Somers TEST_F(Write, direct_io_short_write) 49412292a99SAlan Somers { 49512292a99SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 49612292a99SAlan Somers const char RELPATH[] = "some_file.txt"; 49712292a99SAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 49812292a99SAlan Somers uint64_t ino = 42; 49912292a99SAlan Somers int fd; 50012292a99SAlan Somers ssize_t bufsize = strlen(CONTENTS); 50112292a99SAlan Somers ssize_t halfbufsize = bufsize / 2; 50212292a99SAlan Somers 50312292a99SAlan Somers expect_lookup(RELPATH, ino, 0); 50412292a99SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 505bda39894SAlan Somers expect_write(ino, 0, bufsize, halfbufsize, CONTENTS); 50612292a99SAlan Somers 50712292a99SAlan Somers fd = open(FULLPATH, O_WRONLY); 508d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 50912292a99SAlan Somers 51012292a99SAlan Somers ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 5117fc0921dSAlan Somers leak(fd); 51212292a99SAlan Somers } 51312292a99SAlan Somers 51412292a99SAlan Somers /* 5159821f1d3SAlan Somers * An insidious edge case: the filesystem returns a short write, and the 5169821f1d3SAlan Somers * difference between what we requested and what it actually wrote crosses an 5179821f1d3SAlan Somers * iov element boundary 5189821f1d3SAlan Somers */ 51912292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov) 5209821f1d3SAlan Somers { 5219821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 5229821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 5239821f1d3SAlan Somers const char *CONTENTS0 = "abcdefgh"; 5249821f1d3SAlan Somers const char *CONTENTS1 = "ijklmnop"; 5259821f1d3SAlan Somers const char *EXPECTED0 = "abcdefghijklmnop"; 5269821f1d3SAlan Somers uint64_t ino = 42; 5279821f1d3SAlan Somers int fd; 5289821f1d3SAlan Somers ssize_t size0 = strlen(CONTENTS0) - 1; 5299821f1d3SAlan Somers ssize_t size1 = strlen(CONTENTS1) + 1; 5309821f1d3SAlan Somers ssize_t totalsize = size0 + size1; 5319821f1d3SAlan Somers struct iovec iov[2]; 5329821f1d3SAlan Somers 5339821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 5349821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 535bda39894SAlan Somers expect_write(ino, 0, totalsize, size0, EXPECTED0); 5369821f1d3SAlan Somers 5379821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 538d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 5399821f1d3SAlan Somers 5405a0b9a27SAlan Somers iov[0].iov_base = __DECONST(void*, CONTENTS0); 5419821f1d3SAlan Somers iov[0].iov_len = strlen(CONTENTS0); 5425a0b9a27SAlan Somers iov[1].iov_base = __DECONST(void*, CONTENTS1); 5439821f1d3SAlan Somers iov[1].iov_len = strlen(CONTENTS1); 54412292a99SAlan Somers ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno); 5457fc0921dSAlan Somers leak(fd); 5469821f1d3SAlan Somers } 5479821f1d3SAlan Somers 548a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */ 549*be280f60SAlan Somers TEST_P(WriteRlimitFsize, rlimit_fsize) 550a639731bSAlan Somers { 551a639731bSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 552a639731bSAlan Somers const char RELPATH[] = "some_file.txt"; 553a639731bSAlan Somers const char *CONTENTS = "abcdefgh"; 554a639731bSAlan Somers struct rlimit rl; 555a639731bSAlan Somers ssize_t bufsize = strlen(CONTENTS); 556a639731bSAlan Somers off_t offset = 1'000'000'000; 557a639731bSAlan Somers uint64_t ino = 42; 558*be280f60SAlan Somers int fd, oflag; 559*be280f60SAlan Somers 560*be280f60SAlan Somers oflag = GetParam(); 561a639731bSAlan Somers 562a639731bSAlan Somers expect_lookup(RELPATH, ino, 0); 563a639731bSAlan Somers expect_open(ino, 0, 1); 564a639731bSAlan Somers 565a639731bSAlan Somers rl.rlim_cur = offset; 566*be280f60SAlan Somers rl.rlim_max = m_initial_limit.rlim_max; 567a639731bSAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno); 568a639731bSAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno); 569a639731bSAlan Somers 570*be280f60SAlan Somers fd = open(FULLPATH, O_WRONLY | oflag); 571a639731bSAlan Somers 572d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 573a639731bSAlan Somers 574a639731bSAlan Somers ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset)); 575a639731bSAlan Somers EXPECT_EQ(EFBIG, errno); 576a639731bSAlan Somers EXPECT_EQ(1, s_sigxfsz); 5777fc0921dSAlan Somers leak(fd); 578a639731bSAlan Somers } 579a639731bSAlan Somers 5809821f1d3SAlan Somers /* 581*be280f60SAlan Somers * When crossing the RLIMIT_FSIZE boundary, writes should be truncated, not 582*be280f60SAlan Somers * aborted. 583*be280f60SAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=164793 584*be280f60SAlan Somers */ 585*be280f60SAlan Somers TEST_P(WriteRlimitFsize, rlimit_fsize_truncate) 586*be280f60SAlan Somers { 587*be280f60SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 588*be280f60SAlan Somers const char RELPATH[] = "some_file.txt"; 589*be280f60SAlan Somers const char *CONTENTS = "abcdefghijklmnopqrstuvwxyz"; 590*be280f60SAlan Somers struct rlimit rl; 591*be280f60SAlan Somers ssize_t bufsize = strlen(CONTENTS); 592*be280f60SAlan Somers uint64_t ino = 42; 593*be280f60SAlan Somers off_t offset = 1 << 30; 594*be280f60SAlan Somers off_t limit = offset + strlen(CONTENTS) / 2; 595*be280f60SAlan Somers int fd, oflag; 596*be280f60SAlan Somers 597*be280f60SAlan Somers oflag = GetParam(); 598*be280f60SAlan Somers 599*be280f60SAlan Somers expect_lookup(RELPATH, ino, 0); 600*be280f60SAlan Somers expect_open(ino, 0, 1); 601*be280f60SAlan Somers expect_write(ino, offset, bufsize / 2, bufsize / 2, CONTENTS); 602*be280f60SAlan Somers 603*be280f60SAlan Somers rl.rlim_cur = limit; 604*be280f60SAlan Somers rl.rlim_max = m_initial_limit.rlim_max; 605*be280f60SAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno); 606*be280f60SAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno); 607*be280f60SAlan Somers 608*be280f60SAlan Somers fd = open(FULLPATH, O_WRONLY | oflag); 609*be280f60SAlan Somers 610*be280f60SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 611*be280f60SAlan Somers 612*be280f60SAlan Somers ASSERT_EQ(bufsize / 2, pwrite(fd, CONTENTS, bufsize, offset)) 613*be280f60SAlan Somers << strerror(errno); 614*be280f60SAlan Somers leak(fd); 615*be280f60SAlan Somers } 616*be280f60SAlan Somers 617*be280f60SAlan Somers INSTANTIATE_TEST_CASE_P(W, WriteRlimitFsize, 618*be280f60SAlan Somers Values(0, O_DIRECT) 619*be280f60SAlan Somers ); 620*be280f60SAlan Somers 621*be280f60SAlan Somers /* 622b9e20197SAlan Somers * A short read indicates EOF. Test that nothing bad happens if we get EOF 623b9e20197SAlan Somers * during the R of a RMW operation. 624b9e20197SAlan Somers */ 625f8ebf1cdSAlan Somers TEST_F(Write, eof_during_rmw) 626b9e20197SAlan Somers { 627b9e20197SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 628b9e20197SAlan Somers const char RELPATH[] = "some_file.txt"; 629b9e20197SAlan Somers const char *CONTENTS = "abcdefgh"; 630b9e20197SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 631b9e20197SAlan Somers uint64_t ino = 42; 632b9e20197SAlan Somers uint64_t offset = 1; 633ae39db74SAlan Somers ssize_t bufsize = strlen(CONTENTS) + 1; 634b9e20197SAlan Somers off_t orig_fsize = 10; 635b9e20197SAlan Somers off_t truncated_fsize = 5; 636b9e20197SAlan Somers int fd; 637b9e20197SAlan Somers 638b9e20197SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1); 639b9e20197SAlan Somers expect_open(ino, 0, 1); 640b9e20197SAlan Somers expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR); 641b9e20197SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS); 642b9e20197SAlan Somers 643b9e20197SAlan Somers fd = open(FULLPATH, O_RDWR); 644d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 645b9e20197SAlan Somers 646b9e20197SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 647b9e20197SAlan Somers << strerror(errno); 6487fc0921dSAlan Somers leak(fd); 649b9e20197SAlan Somers } 650b9e20197SAlan Somers 651b9e20197SAlan Somers /* 652032a5bd5SAlan Somers * VOP_STRATEGY should not query the server for the file's size, even if its 653032a5bd5SAlan Somers * cached attributes have expired. 654032a5bd5SAlan Somers * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937 655032a5bd5SAlan Somers */ 656032a5bd5SAlan Somers TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy) 657032a5bd5SAlan Somers { 658032a5bd5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 659032a5bd5SAlan Somers const char RELPATH[] = "some_file.txt"; 660032a5bd5SAlan Somers Sequence seq; 661032a5bd5SAlan Somers const off_t filesize = 2 * m_maxbcachebuf; 662032a5bd5SAlan Somers void *contents; 663032a5bd5SAlan Somers uint64_t ino = 42; 664032a5bd5SAlan Somers uint64_t attr_valid = 0; 665032a5bd5SAlan Somers uint64_t attr_valid_nsec = 0; 666032a5bd5SAlan Somers mode_t mode = S_IFREG | 0644; 667032a5bd5SAlan Somers int fd; 668032a5bd5SAlan Somers int ngetattrs; 669032a5bd5SAlan Somers 670032a5bd5SAlan Somers ngetattrs = GetParam(); 671032a5bd5SAlan Somers contents = calloc(1, filesize); 672032a5bd5SAlan Somers 673032a5bd5SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 674032a5bd5SAlan Somers .WillRepeatedly(Invoke( 675032a5bd5SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 676032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, entry); 677032a5bd5SAlan Somers out.body.entry.attr.mode = mode; 678032a5bd5SAlan Somers out.body.entry.nodeid = ino; 679032a5bd5SAlan Somers out.body.entry.attr.nlink = 1; 680032a5bd5SAlan Somers out.body.entry.attr.size = filesize; 681032a5bd5SAlan Somers out.body.entry.attr_valid = attr_valid; 682032a5bd5SAlan Somers out.body.entry.attr_valid_nsec = attr_valid_nsec; 683032a5bd5SAlan Somers }))); 684032a5bd5SAlan Somers expect_open(ino, 0, 1); 685032a5bd5SAlan Somers EXPECT_CALL(*m_mock, process( 686032a5bd5SAlan Somers ResultOf([=](auto in) { 687032a5bd5SAlan Somers return (in.header.opcode == FUSE_GETATTR && 688032a5bd5SAlan Somers in.header.nodeid == ino); 689032a5bd5SAlan Somers }, Eq(true)), 690032a5bd5SAlan Somers _) 691032a5bd5SAlan Somers ).Times(Between(ngetattrs - 1, ngetattrs)) 692032a5bd5SAlan Somers .InSequence(seq) 693032a5bd5SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 694032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, attr); 695032a5bd5SAlan Somers out.body.attr.attr.ino = ino; 696032a5bd5SAlan Somers out.body.attr.attr.mode = mode; 697032a5bd5SAlan Somers out.body.attr.attr_valid = attr_valid; 698032a5bd5SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec; 699032a5bd5SAlan Somers out.body.attr.attr.size = filesize; 700032a5bd5SAlan Somers }))); 701032a5bd5SAlan Somers EXPECT_CALL(*m_mock, process( 702032a5bd5SAlan Somers ResultOf([=](auto in) { 703032a5bd5SAlan Somers return (in.header.opcode == FUSE_GETATTR && 704032a5bd5SAlan Somers in.header.nodeid == ino); 705032a5bd5SAlan Somers }, Eq(true)), 706032a5bd5SAlan Somers _) 707032a5bd5SAlan Somers ).InSequence(seq) 708032a5bd5SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 709032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, attr); 710032a5bd5SAlan Somers out.body.attr.attr.ino = ino; 711032a5bd5SAlan Somers out.body.attr.attr.mode = mode; 712032a5bd5SAlan Somers out.body.attr.attr_valid = attr_valid; 713032a5bd5SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec; 714032a5bd5SAlan Somers out.body.attr.attr.size = filesize / 2; 715032a5bd5SAlan Somers }))); 716032a5bd5SAlan Somers expect_write(ino, 0, filesize / 2, filesize / 2, contents); 717032a5bd5SAlan Somers 718032a5bd5SAlan Somers fd = open(FULLPATH, O_RDWR); 719032a5bd5SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 720032a5bd5SAlan Somers ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2)) 721032a5bd5SAlan Somers << strerror(errno); 722032a5bd5SAlan Somers 723032a5bd5SAlan Somers } 724032a5bd5SAlan Somers 725032a5bd5SAlan Somers INSTANTIATE_TEST_CASE_P(W, WriteEofDuringVnopStrategy, 726032a5bd5SAlan Somers Values(1, 2, 3) 727032a5bd5SAlan Somers ); 728032a5bd5SAlan Somers 729032a5bd5SAlan Somers /* 7309821f1d3SAlan Somers * If the kernel cannot be sure which uid, gid, or pid was responsible for a 7319821f1d3SAlan Somers * write, then it must set the FUSE_WRITE_CACHE bit 7329821f1d3SAlan Somers */ 7339821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */ 734f8ebf1cdSAlan Somers TEST_F(Write, mmap) 7359821f1d3SAlan Somers { 7369821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7379821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7389821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 7399821f1d3SAlan Somers uint64_t ino = 42; 7409821f1d3SAlan Somers int fd; 7419821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 7429821f1d3SAlan Somers void *p; 7439821f1d3SAlan Somers uint64_t offset = 10; 7449821f1d3SAlan Somers size_t len; 7459821f1d3SAlan Somers void *zeros, *expected; 7469821f1d3SAlan Somers 7479821f1d3SAlan Somers len = getpagesize(); 7489821f1d3SAlan Somers 7499821f1d3SAlan Somers zeros = calloc(1, len); 7505a0b9a27SAlan Somers ASSERT_NE(nullptr, zeros); 7519821f1d3SAlan Somers expected = calloc(1, len); 7525a0b9a27SAlan Somers ASSERT_NE(nullptr, expected); 7539821f1d3SAlan Somers memmove((uint8_t*)expected + offset, CONTENTS, bufsize); 7549821f1d3SAlan Somers 7559821f1d3SAlan Somers expect_lookup(RELPATH, ino, len); 7569821f1d3SAlan Somers expect_open(ino, 0, 1); 7579821f1d3SAlan Somers expect_read(ino, 0, len, len, zeros); 7589821f1d3SAlan Somers /* 7599821f1d3SAlan Somers * Writes from the pager may or may not be associated with the correct 760cf437e2aSAlan Somers * pid, so they must set FUSE_WRITE_CACHE. 7619821f1d3SAlan Somers */ 762bda39894SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected); 7639f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 7649821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 7659821f1d3SAlan Somers 7669821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 767d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7689821f1d3SAlan Somers 7699821f1d3SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 7709821f1d3SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno); 7719821f1d3SAlan Somers 7729821f1d3SAlan Somers memmove((uint8_t*)p + offset, CONTENTS, bufsize); 7739821f1d3SAlan Somers 7749821f1d3SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 7759821f1d3SAlan Somers close(fd); // Write mmap'd data on close 7769821f1d3SAlan Somers 7779821f1d3SAlan Somers free(expected); 7789821f1d3SAlan Somers free(zeros); 7798e765737SAlan Somers 7808e765737SAlan Somers leak(fd); 7819821f1d3SAlan Somers } 7829821f1d3SAlan Somers 783f8ebf1cdSAlan Somers TEST_F(Write, pwrite) 7849821f1d3SAlan Somers { 7859821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7869821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7879821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 7889821f1d3SAlan Somers uint64_t ino = 42; 7896ca3b02bSAlan Somers uint64_t offset = m_maxbcachebuf; 7909821f1d3SAlan Somers int fd; 7919821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 7929821f1d3SAlan Somers 7939821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 7949821f1d3SAlan Somers expect_open(ino, 0, 1); 795bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS); 7969821f1d3SAlan Somers 7979821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 798d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7999821f1d3SAlan Somers 8009821f1d3SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 8019821f1d3SAlan Somers << strerror(errno); 8027fc0921dSAlan Somers leak(fd); 8039821f1d3SAlan Somers } 8049821f1d3SAlan Somers 805788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */ 806788af953SAlan Somers TEST_F(Write, timestamps) 807788af953SAlan Somers { 808788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 809788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 810788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 811788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 812788af953SAlan Somers uint64_t ino = 42; 813788af953SAlan Somers struct stat sb0, sb1; 814788af953SAlan Somers int fd; 815788af953SAlan Somers 816788af953SAlan Somers expect_lookup(RELPATH, ino, 0); 817788af953SAlan Somers expect_open(ino, 0, 1); 818788af953SAlan Somers maybe_expect_write(ino, 0, bufsize, CONTENTS); 819788af953SAlan Somers 820788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 821d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 822788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno); 823788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 824788af953SAlan Somers 825788af953SAlan Somers nap(); 826788af953SAlan Somers 827788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno); 828788af953SAlan Somers 829788af953SAlan Somers EXPECT_EQ(sb0.st_atime, sb1.st_atime); 830788af953SAlan Somers EXPECT_NE(sb0.st_mtime, sb1.st_mtime); 831788af953SAlan Somers EXPECT_NE(sb0.st_ctime, sb1.st_ctime); 8328e765737SAlan Somers 8338e765737SAlan Somers leak(fd); 834788af953SAlan Somers } 835788af953SAlan Somers 8369821f1d3SAlan Somers TEST_F(Write, write) 8379821f1d3SAlan Somers { 8389821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8399821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8409821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 8419821f1d3SAlan Somers uint64_t ino = 42; 8429821f1d3SAlan Somers int fd; 8439821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8449821f1d3SAlan Somers 8459821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 8469821f1d3SAlan Somers expect_open(ino, 0, 1); 847bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 8489821f1d3SAlan Somers 8499821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 850d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 8519821f1d3SAlan Somers 8529821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 8537fc0921dSAlan Somers leak(fd); 8549821f1d3SAlan Somers } 8559821f1d3SAlan Somers 8569821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */ 857f928dbcbSAlan Somers TEST_F(WriteMaxWrite, write) 8589821f1d3SAlan Somers { 8599821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8609821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8619821f1d3SAlan Somers int *contents; 8629821f1d3SAlan Somers uint64_t ino = 42; 8639821f1d3SAlan Somers int fd; 8649821f1d3SAlan Somers ssize_t halfbufsize, bufsize; 8659821f1d3SAlan Somers 8668eecd9ceSAlan Somers halfbufsize = m_mock->m_maxwrite; 867f928dbcbSAlan Somers if (halfbufsize >= m_maxbcachebuf || halfbufsize >= m_maxphys) 868f928dbcbSAlan Somers GTEST_SKIP() << "Must lower m_maxwrite for this test"; 8699821f1d3SAlan Somers bufsize = halfbufsize * 2; 8709821f1d3SAlan Somers contents = (int*)malloc(bufsize); 8715a0b9a27SAlan Somers ASSERT_NE(nullptr, contents); 8729821f1d3SAlan Somers for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) { 8739821f1d3SAlan Somers contents[i] = i; 8749821f1d3SAlan Somers } 8759821f1d3SAlan Somers 8769821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 8779821f1d3SAlan Somers expect_open(ino, 0, 1); 878f2704f05SAlan Somers maybe_expect_write(ino, 0, halfbufsize, contents); 87984879e46SAlan Somers maybe_expect_write(ino, halfbufsize, halfbufsize, 8809821f1d3SAlan Somers &contents[halfbufsize / sizeof(int)]); 8819821f1d3SAlan Somers 8829821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 883d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 8849821f1d3SAlan Somers 8859821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno); 8867fc0921dSAlan Somers leak(fd); 8879821f1d3SAlan Somers 8889821f1d3SAlan Somers free(contents); 8899821f1d3SAlan Somers } 8909821f1d3SAlan Somers 8919821f1d3SAlan Somers TEST_F(Write, write_nothing) 8929821f1d3SAlan Somers { 8939821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8949821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8959821f1d3SAlan Somers const char *CONTENTS = ""; 8969821f1d3SAlan Somers uint64_t ino = 42; 8979821f1d3SAlan Somers int fd; 8989821f1d3SAlan Somers ssize_t bufsize = 0; 8999821f1d3SAlan Somers 9009821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 9019821f1d3SAlan Somers expect_open(ino, 0, 1); 9029821f1d3SAlan Somers 9039821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 904d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 9059821f1d3SAlan Somers 9069821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 9077fc0921dSAlan Somers leak(fd); 9089821f1d3SAlan Somers } 9099821f1d3SAlan Somers 91016bd2d47SAlan Somers TEST_F(Write_7_8, write) 91116bd2d47SAlan Somers { 91216bd2d47SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 91316bd2d47SAlan Somers const char RELPATH[] = "some_file.txt"; 91416bd2d47SAlan Somers const char *CONTENTS = "abcdefgh"; 91516bd2d47SAlan Somers uint64_t ino = 42; 91616bd2d47SAlan Somers int fd; 91716bd2d47SAlan Somers ssize_t bufsize = strlen(CONTENTS); 91816bd2d47SAlan Somers 91916bd2d47SAlan Somers expect_lookup(RELPATH, ino, 0); 92016bd2d47SAlan Somers expect_open(ino, 0, 1); 921bda39894SAlan Somers expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS); 92216bd2d47SAlan Somers 92316bd2d47SAlan Somers fd = open(FULLPATH, O_WRONLY); 924d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 92516bd2d47SAlan Somers 92616bd2d47SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 9277fc0921dSAlan Somers leak(fd); 92816bd2d47SAlan Somers } 92916bd2d47SAlan Somers 9309821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */ 93184879e46SAlan Somers TEST_F(WriteBackAsync, close) 9329821f1d3SAlan Somers { 9339821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9349821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 9359821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 9369821f1d3SAlan Somers uint64_t ino = 42; 9379821f1d3SAlan Somers int fd; 9389821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 9399821f1d3SAlan Somers 9409821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 9419821f1d3SAlan Somers expect_open(ino, 0, 1); 942bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 9439821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 9449821f1d3SAlan Somers ResultOf([=](auto in) { 94529edc611SAlan Somers return (in.header.opcode == FUSE_SETATTR); 9469821f1d3SAlan Somers }, Eq(true)), 9479821f1d3SAlan Somers _) 94829edc611SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 9499821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, attr); 95029edc611SAlan Somers out.body.attr.attr.ino = ino; // Must match nodeid 9519821f1d3SAlan Somers }))); 9529f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 9539821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 9549821f1d3SAlan Somers 9559821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 9569821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 9579821f1d3SAlan Somers 9589821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 9599821f1d3SAlan Somers close(fd); 9609821f1d3SAlan Somers } 9619821f1d3SAlan Somers 9628eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */ 9638eecd9ceSAlan Somers TEST_F(WriteCluster, clustering) 9648eecd9ceSAlan Somers { 9658eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9668eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 9678eecd9ceSAlan Somers uint64_t ino = 42; 9688eecd9ceSAlan Somers int i, fd; 9698eecd9ceSAlan Somers void *wbuf, *wbuf2x; 9706ca3b02bSAlan Somers ssize_t bufsize = m_maxbcachebuf; 9716ca3b02bSAlan Somers off_t filesize = 5 * bufsize; 9728eecd9ceSAlan Somers 9738eecd9ceSAlan Somers wbuf = malloc(bufsize); 9745a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf) << strerror(errno); 9758eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 9768eecd9ceSAlan Somers wbuf2x = malloc(2 * bufsize); 9775a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf2x) << strerror(errno); 9788eecd9ceSAlan Somers memset(wbuf2x, 'X', 2 * bufsize); 9798eecd9ceSAlan Somers 9808eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 9818eecd9ceSAlan Somers expect_open(ino, 0, 1); 9828eecd9ceSAlan Somers /* 9838eecd9ceSAlan Somers * Writes of bufsize-bytes each should be clustered into greater sizes. 9848eecd9ceSAlan Somers * The amount of clustering is adaptive, so the first write actually 9858eecd9ceSAlan Somers * issued will be 2x bufsize and subsequent writes may be larger 9868eecd9ceSAlan Somers */ 9878eecd9ceSAlan Somers expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x); 9888eecd9ceSAlan Somers expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x); 9898eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 9908eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 9918eecd9ceSAlan Somers 9928eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 9938eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 9948eecd9ceSAlan Somers 9958eecd9ceSAlan Somers for (i = 0; i < 4; i++) { 9968eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 9978eecd9ceSAlan Somers << strerror(errno); 9988eecd9ceSAlan Somers } 9998eecd9ceSAlan Somers close(fd); 10007ee7e409SAlan Somers free(wbuf2x); 10017ee7e409SAlan Somers free(wbuf); 10028eecd9ceSAlan Somers } 10038eecd9ceSAlan Somers 10048eecd9ceSAlan Somers /* 10058eecd9ceSAlan Somers * When clustering writes, an I/O error to any of the cluster's children should 10068eecd9ceSAlan Somers * not panic the system on unmount 10078eecd9ceSAlan Somers */ 10088eecd9ceSAlan Somers /* 1009425bbe9eSAlan Somers * Regression test for bug 238585 10108eecd9ceSAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565 10118eecd9ceSAlan Somers */ 1012425bbe9eSAlan Somers TEST_F(WriteCluster, cluster_write_err) 10138eecd9ceSAlan Somers { 10148eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 10158eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 10168eecd9ceSAlan Somers uint64_t ino = 42; 10178eecd9ceSAlan Somers int i, fd; 10188eecd9ceSAlan Somers void *wbuf; 10196ca3b02bSAlan Somers ssize_t bufsize = m_maxbcachebuf; 10206ca3b02bSAlan Somers off_t filesize = 4 * bufsize; 10218eecd9ceSAlan Somers 10228eecd9ceSAlan Somers wbuf = malloc(bufsize); 10235a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf) << strerror(errno); 10248eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 10258eecd9ceSAlan Somers 10268eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 10278eecd9ceSAlan Somers expect_open(ino, 0, 1); 10288eecd9ceSAlan Somers EXPECT_CALL(*m_mock, process( 10298eecd9ceSAlan Somers ResultOf([=](auto in) { 10308eecd9ceSAlan Somers return (in.header.opcode == FUSE_WRITE); 10318eecd9ceSAlan Somers }, Eq(true)), 10328eecd9ceSAlan Somers _) 10338eecd9ceSAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(EIO))); 10348eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 10358eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 10368eecd9ceSAlan Somers 10378eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 10388eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 10398eecd9ceSAlan Somers 10408eecd9ceSAlan Somers for (i = 0; i < 3; i++) { 10418eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 10428eecd9ceSAlan Somers << strerror(errno); 10438eecd9ceSAlan Somers } 10448eecd9ceSAlan Somers close(fd); 10457ee7e409SAlan Somers free(wbuf); 10468eecd9ceSAlan Somers } 10478eecd9ceSAlan Somers 10489821f1d3SAlan Somers /* 10495fccbf31SAlan Somers * In writeback mode, writes to an O_WRONLY file could trigger reads from the 10505fccbf31SAlan Somers * server. The FUSE protocol explicitly allows that. 10515fccbf31SAlan Somers */ 10525fccbf31SAlan Somers TEST_F(WriteBack, rmw) 10535fccbf31SAlan Somers { 10545fccbf31SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 10555fccbf31SAlan Somers const char RELPATH[] = "some_file.txt"; 10565fccbf31SAlan Somers const char *CONTENTS = "abcdefgh"; 10575fccbf31SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 10585fccbf31SAlan Somers uint64_t ino = 42; 10595fccbf31SAlan Somers uint64_t offset = 1; 10605fccbf31SAlan Somers off_t fsize = 10; 10615fccbf31SAlan Somers int fd; 10625fccbf31SAlan Somers ssize_t bufsize = strlen(CONTENTS); 10635fccbf31SAlan Somers 1064cad67791SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1); 10655fccbf31SAlan Somers expect_open(ino, 0, 1); 1066d4fd0c81SAlan Somers expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY); 106784879e46SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS); 10685fccbf31SAlan Somers 10695fccbf31SAlan Somers fd = open(FULLPATH, O_WRONLY); 1070d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 10715fccbf31SAlan Somers 10725fccbf31SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 10735fccbf31SAlan Somers << strerror(errno); 10747fc0921dSAlan Somers leak(fd); 10755fccbf31SAlan Somers } 10765fccbf31SAlan Somers 10775fccbf31SAlan Somers /* 10789821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 10799821f1d3SAlan Somers */ 108084879e46SAlan Somers TEST_F(WriteBack, cache) 10819821f1d3SAlan Somers { 10829821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 10839821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 10849821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 10859821f1d3SAlan Somers uint64_t ino = 42; 10869821f1d3SAlan Somers int fd; 10879821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 10888e765737SAlan Somers uint8_t readbuf[bufsize]; 10899821f1d3SAlan Somers 10909821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 10919821f1d3SAlan Somers expect_open(ino, 0, 1); 1092bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 10939821f1d3SAlan Somers 10949821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 1095d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 10969821f1d3SAlan Somers 10979821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 10989821f1d3SAlan Somers /* 10999821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 11009821f1d3SAlan Somers * filesystem daemon 11019821f1d3SAlan Somers */ 11029821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 11039821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 11047fc0921dSAlan Somers leak(fd); 11059821f1d3SAlan Somers } 11069821f1d3SAlan Somers 11079821f1d3SAlan Somers /* 11089821f1d3SAlan Somers * With O_DIRECT, writes should be not committed to cache. Admittedly this is 11099821f1d3SAlan Somers * an odd test, because it would be unusual to use O_DIRECT for writes but not 11109821f1d3SAlan Somers * reads. 11119821f1d3SAlan Somers */ 11129821f1d3SAlan Somers TEST_F(WriteBack, o_direct) 11139821f1d3SAlan Somers { 11149821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 11159821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 11169821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 11179821f1d3SAlan Somers uint64_t ino = 42; 11189821f1d3SAlan Somers int fd; 11199821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 11208e765737SAlan Somers uint8_t readbuf[bufsize]; 11219821f1d3SAlan Somers 11229821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 11239821f1d3SAlan Somers expect_open(ino, 0, 1); 1124bda39894SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE, 1125bda39894SAlan Somers CONTENTS); 11269821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS); 11279821f1d3SAlan Somers 11289821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_DIRECT); 1129d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 11309821f1d3SAlan Somers 11319821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 11329821f1d3SAlan Somers /* A subsequent read must query the daemon because cache is empty */ 11339821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 11349821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 11359821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 11367fc0921dSAlan Somers leak(fd); 11379821f1d3SAlan Somers } 11389821f1d3SAlan Somers 1139a62772a7SAlan Somers TEST_F(WriteBack, direct_io) 1140a62772a7SAlan Somers { 1141a62772a7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1142a62772a7SAlan Somers const char RELPATH[] = "some_file.txt"; 1143a62772a7SAlan Somers const char *CONTENTS = "abcdefgh"; 1144a62772a7SAlan Somers uint64_t ino = 42; 1145a62772a7SAlan Somers int fd; 1146a62772a7SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1147a62772a7SAlan Somers uint8_t readbuf[bufsize]; 1148a62772a7SAlan Somers 1149a62772a7SAlan Somers expect_lookup(RELPATH, ino, 0); 1150a62772a7SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 1151a62772a7SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE, 1152a62772a7SAlan Somers CONTENTS); 1153a62772a7SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1154a62772a7SAlan Somers 1155a62772a7SAlan Somers fd = open(FULLPATH, O_RDWR); 1156d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1157a62772a7SAlan Somers 1158a62772a7SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1159a62772a7SAlan Somers /* A subsequent read must query the daemon because cache is empty */ 1160a62772a7SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 1161a62772a7SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 1162a62772a7SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 1163a62772a7SAlan Somers leak(fd); 1164a62772a7SAlan Somers } 1165a62772a7SAlan Somers 1166a62772a7SAlan Somers /* 1167a62772a7SAlan Somers * mmap should still be possible even if the server used direct_io. Mmap will 1168a62772a7SAlan Somers * still use the cache, though. 1169a62772a7SAlan Somers * 1170a62772a7SAlan Somers * Regression test for bug 247276 1171a62772a7SAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276 1172a62772a7SAlan Somers */ 1173a62772a7SAlan Somers TEST_F(WriteBack, mmap_direct_io) 1174a62772a7SAlan Somers { 1175a62772a7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1176a62772a7SAlan Somers const char RELPATH[] = "some_file.txt"; 1177a62772a7SAlan Somers const char *CONTENTS = "abcdefgh"; 1178a62772a7SAlan Somers uint64_t ino = 42; 1179a62772a7SAlan Somers int fd; 1180a62772a7SAlan Somers size_t len; 1181a62772a7SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1182a62772a7SAlan Somers void *p, *zeros; 1183a62772a7SAlan Somers 1184a62772a7SAlan Somers len = getpagesize(); 1185a62772a7SAlan Somers zeros = calloc(1, len); 1186a62772a7SAlan Somers ASSERT_NE(nullptr, zeros); 1187a62772a7SAlan Somers 1188a62772a7SAlan Somers expect_lookup(RELPATH, ino, len); 1189a62772a7SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 1190a62772a7SAlan Somers expect_read(ino, 0, len, len, zeros); 1191a62772a7SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 1192a62772a7SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros); 1193a62772a7SAlan Somers expect_release(ino, ReturnErrno(0)); 1194a62772a7SAlan Somers 1195a62772a7SAlan Somers fd = open(FULLPATH, O_RDWR); 1196d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1197a62772a7SAlan Somers 1198a62772a7SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 1199a62772a7SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno); 1200a62772a7SAlan Somers 1201a62772a7SAlan Somers memmove((uint8_t*)p, CONTENTS, bufsize); 1202a62772a7SAlan Somers 1203a62772a7SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 1204a62772a7SAlan Somers close(fd); // Write mmap'd data on close 1205a62772a7SAlan Somers 1206a62772a7SAlan Somers free(zeros); 1207a62772a7SAlan Somers } 1208a62772a7SAlan Somers 12099821f1d3SAlan Somers /* 121084879e46SAlan Somers * When mounted with -o async, the writeback cache mode should delay writes 121184879e46SAlan Somers */ 121284879e46SAlan Somers TEST_F(WriteBackAsync, delay) 121384879e46SAlan Somers { 121484879e46SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 121584879e46SAlan Somers const char RELPATH[] = "some_file.txt"; 121684879e46SAlan Somers const char *CONTENTS = "abcdefgh"; 121784879e46SAlan Somers uint64_t ino = 42; 121884879e46SAlan Somers int fd; 121984879e46SAlan Somers ssize_t bufsize = strlen(CONTENTS); 122084879e46SAlan Somers 122184879e46SAlan Somers expect_lookup(RELPATH, ino, 0); 122284879e46SAlan Somers expect_open(ino, 0, 1); 122384879e46SAlan Somers /* Write should be cached, but FUSE_WRITE shouldn't be sent */ 122484879e46SAlan Somers EXPECT_CALL(*m_mock, process( 122584879e46SAlan Somers ResultOf([=](auto in) { 122684879e46SAlan Somers return (in.header.opcode == FUSE_WRITE); 122784879e46SAlan Somers }, Eq(true)), 122884879e46SAlan Somers _) 122984879e46SAlan Somers ).Times(0); 123084879e46SAlan Somers 123184879e46SAlan Somers fd = open(FULLPATH, O_RDWR); 1232d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 123384879e46SAlan Somers 123484879e46SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 123584879e46SAlan Somers 123684879e46SAlan Somers /* Don't close the file because that would flush the cache */ 12378e765737SAlan Somers leak(fd); 123884879e46SAlan Somers } 123984879e46SAlan Somers 124084879e46SAlan Somers /* 1241669a092aSAlan Somers * A direct write should not evict dirty cached data from outside of its own 1242669a092aSAlan Somers * byte range. 1243669a092aSAlan Somers */ 1244669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached) 1245669a092aSAlan Somers { 1246669a092aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1247669a092aSAlan Somers const char RELPATH[] = "some_file.txt"; 1248669a092aSAlan Somers const char CONTENTS0[] = "abcdefgh"; 1249669a092aSAlan Somers const char CONTENTS1[] = "ijklmnop"; 1250669a092aSAlan Somers uint64_t ino = 42; 1251669a092aSAlan Somers int fd; 1252669a092aSAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1; 1253669a092aSAlan Somers ssize_t fsize = 2 * m_maxbcachebuf; 1254669a092aSAlan Somers char readbuf[bufsize]; 1255669a092aSAlan Somers void *zeros; 1256669a092aSAlan Somers 1257669a092aSAlan Somers zeros = calloc(1, m_maxbcachebuf); 1258669a092aSAlan Somers ASSERT_NE(nullptr, zeros); 1259669a092aSAlan Somers 1260669a092aSAlan Somers expect_lookup(RELPATH, ino, fsize); 1261669a092aSAlan Somers expect_open(ino, 0, 1); 1262669a092aSAlan Somers expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros); 1263669a092aSAlan Somers FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0, 1264669a092aSAlan Somers CONTENTS1); 1265669a092aSAlan Somers 1266669a092aSAlan Somers fd = open(FULLPATH, O_RDWR); 1267d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1268669a092aSAlan Somers 1269669a092aSAlan Somers // Cache first block with dirty data. This will entail first reading 1270669a092aSAlan Somers // the existing data. 1271669a092aSAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0)) 1272669a092aSAlan Somers << strerror(errno); 1273669a092aSAlan Somers 1274669a092aSAlan Somers // Write directly to second block 1275669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 1276669a092aSAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf)) 1277669a092aSAlan Somers << strerror(errno); 1278669a092aSAlan Somers 1279669a092aSAlan Somers // Read from the first block again. Should be serviced by cache. 1280669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 1281669a092aSAlan Somers ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno); 1282669a092aSAlan Somers ASSERT_STREQ(readbuf, CONTENTS0); 1283669a092aSAlan Somers 1284669a092aSAlan Somers leak(fd); 1285669a092aSAlan Somers free(zeros); 1286669a092aSAlan Somers } 1287669a092aSAlan Somers 1288669a092aSAlan Somers /* 1289669a092aSAlan Somers * If a direct io write partially overlaps one or two blocks of dirty cached 1290669a092aSAlan Somers * data, No dirty data should be lost. Admittedly this is a weird test, 1291669a092aSAlan Somers * because it would be unusual to use O_DIRECT and the writeback cache. 1292669a092aSAlan Somers */ 1293669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block) 1294669a092aSAlan Somers { 1295669a092aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1296669a092aSAlan Somers const char RELPATH[] = "some_file.txt"; 1297669a092aSAlan Somers uint64_t ino = 42; 1298669a092aSAlan Somers int fd; 1299669a092aSAlan Somers off_t bs = m_maxbcachebuf; 1300669a092aSAlan Somers ssize_t fsize = 3 * bs; 1301669a092aSAlan Somers void *readbuf, *zeros, *ones, *zeroones, *onezeros; 1302669a092aSAlan Somers 1303669a092aSAlan Somers readbuf = malloc(bs); 1304669a092aSAlan Somers ASSERT_NE(nullptr, readbuf) << strerror(errno); 1305669a092aSAlan Somers zeros = calloc(1, 3 * bs); 1306669a092aSAlan Somers ASSERT_NE(nullptr, zeros); 1307669a092aSAlan Somers ones = calloc(1, 2 * bs); 1308669a092aSAlan Somers ASSERT_NE(nullptr, ones); 1309669a092aSAlan Somers memset(ones, 1, 2 * bs); 1310669a092aSAlan Somers zeroones = calloc(1, bs); 1311669a092aSAlan Somers ASSERT_NE(nullptr, zeroones); 1312669a092aSAlan Somers memset((uint8_t*)zeroones + bs / 2, 1, bs / 2); 1313669a092aSAlan Somers onezeros = calloc(1, bs); 1314669a092aSAlan Somers ASSERT_NE(nullptr, onezeros); 1315669a092aSAlan Somers memset(onezeros, 1, bs / 2); 1316669a092aSAlan Somers 1317669a092aSAlan Somers expect_lookup(RELPATH, ino, fsize); 1318669a092aSAlan Somers expect_open(ino, 0, 1); 1319669a092aSAlan Somers 1320669a092aSAlan Somers fd = open(FULLPATH, O_RDWR); 1321d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1322669a092aSAlan Somers 1323669a092aSAlan Somers /* Cache first and third blocks with dirty data. */ 1324669a092aSAlan Somers ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno); 1325669a092aSAlan Somers 1326669a092aSAlan Somers /* 1327669a092aSAlan Somers * Write directly to all three blocks. The partially written blocks 1328669a092aSAlan Somers * will be flushed because they're dirty. 1329669a092aSAlan Somers */ 1330669a092aSAlan Somers FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros); 1331669a092aSAlan Somers FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros); 1332669a092aSAlan Somers /* The direct write is split in two because of the m_maxwrite value */ 1333669a092aSAlan Somers FuseTest::expect_write(ino, bs / 2, bs, bs, 0, 0, ones); 1334669a092aSAlan Somers FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones); 1335669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 1336669a092aSAlan Somers ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno); 1337669a092aSAlan Somers 1338669a092aSAlan Somers /* 1339669a092aSAlan Somers * Read from both the valid and invalid portions of the first and third 1340669a092aSAlan Somers * blocks again. This will entail FUSE_READ operations because these 1341669a092aSAlan Somers * blocks were invalidated by the direct write. 1342669a092aSAlan Somers */ 1343669a092aSAlan Somers expect_read(ino, 0, bs, bs, zeroones); 1344669a092aSAlan Somers expect_read(ino, 2 * bs, bs, bs, onezeros); 1345669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 1346669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno); 1347669a092aSAlan Somers EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2)); 1348669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2)) 1349669a092aSAlan Somers << strerror(errno); 1350669a092aSAlan Somers EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2)); 1351669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2)) 1352669a092aSAlan Somers << strerror(errno); 1353669a092aSAlan Somers EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2)); 1354669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs)) 1355669a092aSAlan Somers << strerror(errno); 1356669a092aSAlan Somers EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2)); 1357669a092aSAlan Somers 1358669a092aSAlan Somers leak(fd); 1359669a092aSAlan Somers free(zeroones); 1360669a092aSAlan Somers free(onezeros); 1361669a092aSAlan Somers free(ones); 1362669a092aSAlan Somers free(zeros); 1363669a092aSAlan Somers free(readbuf); 1364669a092aSAlan Somers } 1365669a092aSAlan Somers 1366669a092aSAlan Somers /* 1367aef22f2dSAlan Somers * In WriteBack mode, writes may be cached beyond what the server thinks is the 1368aef22f2dSAlan Somers * EOF. In this case, a short read at EOF should _not_ cause fusefs to update 1369aef22f2dSAlan Somers * the file's size. 1370aef22f2dSAlan Somers */ 1371aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof) 1372aef22f2dSAlan Somers { 1373aef22f2dSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1374aef22f2dSAlan Somers const char RELPATH[] = "some_file.txt"; 1375aef22f2dSAlan Somers const char *CONTENTS0 = "abcdefgh"; 1376aef22f2dSAlan Somers const char *CONTENTS1 = "ijklmnop"; 1377aef22f2dSAlan Somers uint64_t ino = 42; 1378aef22f2dSAlan Somers int fd; 1379aef22f2dSAlan Somers off_t offset = m_maxbcachebuf; 1380aef22f2dSAlan Somers ssize_t wbufsize = strlen(CONTENTS1); 1381aef22f2dSAlan Somers off_t old_filesize = (off_t)strlen(CONTENTS0); 1382aef22f2dSAlan Somers ssize_t rbufsize = 2 * old_filesize; 1383aef22f2dSAlan Somers char readbuf[rbufsize]; 1384aef22f2dSAlan Somers size_t holesize = rbufsize - old_filesize; 1385aef22f2dSAlan Somers char hole[holesize]; 1386aef22f2dSAlan Somers struct stat sb; 1387aef22f2dSAlan Somers ssize_t r; 1388aef22f2dSAlan Somers 1389aef22f2dSAlan Somers expect_lookup(RELPATH, ino, 0); 1390aef22f2dSAlan Somers expect_open(ino, 0, 1); 1391aef22f2dSAlan Somers expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0); 1392aef22f2dSAlan Somers 1393aef22f2dSAlan Somers fd = open(FULLPATH, O_RDWR); 1394d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1395aef22f2dSAlan Somers 1396aef22f2dSAlan Somers /* Write and cache data beyond EOF */ 1397aef22f2dSAlan Somers ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset)) 1398aef22f2dSAlan Somers << strerror(errno); 1399aef22f2dSAlan Somers 1400aef22f2dSAlan Somers /* Read from the old EOF */ 1401aef22f2dSAlan Somers r = pread(fd, readbuf, rbufsize, 0); 1402aef22f2dSAlan Somers ASSERT_LE(0, r) << strerror(errno); 1403aef22f2dSAlan Somers EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole"; 1404aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize)); 1405aef22f2dSAlan Somers bzero(hole, holesize); 1406aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize)); 1407aef22f2dSAlan Somers 1408aef22f2dSAlan Somers /* The file's size should still be what was established by pwrite */ 1409aef22f2dSAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1410aef22f2dSAlan Somers EXPECT_EQ(offset + wbufsize, sb.st_size); 14117fc0921dSAlan Somers leak(fd); 1412aef22f2dSAlan Somers } 1413aef22f2dSAlan Somers 1414aef22f2dSAlan Somers /* 1415788af953SAlan Somers * When a file has dirty writes that haven't been flushed, the server's notion 1416788af953SAlan Somers * of its mtime and ctime will be wrong. The kernel should ignore those if it 1417788af953SAlan Somers * gets them from a FUSE_GETATTR before flushing. 1418788af953SAlan Somers */ 1419788af953SAlan Somers TEST_F(WriteBackAsync, timestamps) 1420788af953SAlan Somers { 1421788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1422788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 1423788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 1424788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1425788af953SAlan Somers uint64_t ino = 42; 1426788af953SAlan Somers uint64_t attr_valid = 0; 1427788af953SAlan Somers uint64_t attr_valid_nsec = 0; 1428788af953SAlan Somers uint64_t server_time = 12345; 1429788af953SAlan Somers mode_t mode = S_IFREG | 0644; 1430788af953SAlan Somers int fd; 1431788af953SAlan Somers 1432788af953SAlan Somers struct stat sb; 1433788af953SAlan Somers 1434788af953SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 1435788af953SAlan Somers .WillRepeatedly(Invoke( 1436788af953SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 1437788af953SAlan Somers SET_OUT_HEADER_LEN(out, entry); 1438788af953SAlan Somers out.body.entry.attr.mode = mode; 1439788af953SAlan Somers out.body.entry.nodeid = ino; 1440788af953SAlan Somers out.body.entry.attr.nlink = 1; 1441788af953SAlan Somers out.body.entry.attr_valid = attr_valid; 1442788af953SAlan Somers out.body.entry.attr_valid_nsec = attr_valid_nsec; 1443788af953SAlan Somers }))); 1444788af953SAlan Somers expect_open(ino, 0, 1); 1445788af953SAlan Somers EXPECT_CALL(*m_mock, process( 1446788af953SAlan Somers ResultOf([=](auto in) { 1447788af953SAlan Somers return (in.header.opcode == FUSE_GETATTR && 1448788af953SAlan Somers in.header.nodeid == ino); 1449788af953SAlan Somers }, Eq(true)), 1450788af953SAlan Somers _) 1451788af953SAlan Somers ).WillRepeatedly(Invoke( 1452788af953SAlan Somers ReturnImmediate([=](auto i __unused, auto& out) { 1453788af953SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1454788af953SAlan Somers out.body.attr.attr.ino = ino; 1455788af953SAlan Somers out.body.attr.attr.mode = mode; 1456788af953SAlan Somers out.body.attr.attr_valid = attr_valid; 1457788af953SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec; 1458788af953SAlan Somers out.body.attr.attr.atime = server_time; 1459788af953SAlan Somers out.body.attr.attr.mtime = server_time; 1460788af953SAlan Somers out.body.attr.attr.ctime = server_time; 1461788af953SAlan Somers }))); 1462788af953SAlan Somers 1463788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 1464d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1465788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1466788af953SAlan Somers 1467788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1468788af953SAlan Somers EXPECT_EQ((time_t)server_time, sb.st_atime); 1469788af953SAlan Somers EXPECT_NE((time_t)server_time, sb.st_mtime); 1470788af953SAlan Somers EXPECT_NE((time_t)server_time, sb.st_ctime); 14718e765737SAlan Somers 14728e765737SAlan Somers leak(fd); 1473788af953SAlan Somers } 1474788af953SAlan Somers 1475788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */ 1476788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr) 1477788af953SAlan Somers { 1478788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1479788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 1480788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 1481788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1482788af953SAlan Somers uint64_t ino = 42; 1483788af953SAlan Somers const mode_t newmode = 0755; 1484788af953SAlan Somers int fd; 1485788af953SAlan Somers 1486788af953SAlan Somers expect_lookup(RELPATH, ino, 0); 1487788af953SAlan Somers expect_open(ino, 0, 1); 1488788af953SAlan Somers EXPECT_CALL(*m_mock, process( 1489788af953SAlan Somers ResultOf([=](auto in) { 14900a8fe2d3SAlan Somers uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME; 1491788af953SAlan Somers return (in.header.opcode == FUSE_SETATTR && 1492788af953SAlan Somers in.header.nodeid == ino && 1493788af953SAlan Somers in.body.setattr.valid == valid); 1494788af953SAlan Somers }, Eq(true)), 1495788af953SAlan Somers _) 1496788af953SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1497788af953SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1498788af953SAlan Somers out.body.attr.attr.ino = ino; 1499788af953SAlan Somers out.body.attr.attr.mode = S_IFREG | newmode; 1500788af953SAlan Somers }))); 1501788af953SAlan Somers 1502788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 1503d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1504788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1505788af953SAlan Somers ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 15068e765737SAlan Somers 15078e765737SAlan Somers leak(fd); 1508788af953SAlan Somers } 1509788af953SAlan Somers 1510fef46454SAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */ 1511fef46454SAlan Somers TEST_P(TimeGran, timestamps_during_setattr) 1512fef46454SAlan Somers { 1513fef46454SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1514fef46454SAlan Somers const char RELPATH[] = "some_file.txt"; 1515fef46454SAlan Somers const char *CONTENTS = "abcdefgh"; 1516fef46454SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1517fef46454SAlan Somers uint64_t ino = 42; 1518fef46454SAlan Somers const mode_t newmode = 0755; 1519fef46454SAlan Somers int fd; 1520fef46454SAlan Somers 1521fef46454SAlan Somers expect_lookup(RELPATH, ino, 0); 1522fef46454SAlan Somers expect_open(ino, 0, 1); 1523fef46454SAlan Somers EXPECT_CALL(*m_mock, process( 1524fef46454SAlan Somers ResultOf([=](auto in) { 1525fef46454SAlan Somers uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME; 1526fef46454SAlan Somers return (in.header.opcode == FUSE_SETATTR && 1527fef46454SAlan Somers in.header.nodeid == ino && 1528fef46454SAlan Somers in.body.setattr.valid == valid && 1529fef46454SAlan Somers in.body.setattr.mtimensec % m_time_gran == 0 && 1530fef46454SAlan Somers in.body.setattr.ctimensec % m_time_gran == 0); 1531fef46454SAlan Somers }, Eq(true)), 1532fef46454SAlan Somers _) 1533fef46454SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1534fef46454SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1535fef46454SAlan Somers out.body.attr.attr.ino = ino; 1536fef46454SAlan Somers out.body.attr.attr.mode = S_IFREG | newmode; 1537fef46454SAlan Somers }))); 1538fef46454SAlan Somers 1539fef46454SAlan Somers fd = open(FULLPATH, O_RDWR); 1540d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1541fef46454SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1542fef46454SAlan Somers ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 15438e765737SAlan Somers 15448e765737SAlan Somers leak(fd); 1545fef46454SAlan Somers } 1546fef46454SAlan Somers 1547fef46454SAlan Somers INSTANTIATE_TEST_CASE_P(RA, TimeGran, Range(0u, 10u)); 1548fef46454SAlan Somers 1549788af953SAlan Somers /* 15509821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 15519821f1d3SAlan Somers */ 1552f8ebf1cdSAlan Somers TEST_F(Write, writethrough) 15539821f1d3SAlan Somers { 15549821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 15559821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 15569821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 15579821f1d3SAlan Somers uint64_t ino = 42; 15589821f1d3SAlan Somers int fd; 15599821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 15608e765737SAlan Somers uint8_t readbuf[bufsize]; 15619821f1d3SAlan Somers 15629821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 15639821f1d3SAlan Somers expect_open(ino, 0, 1); 1564bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 15659821f1d3SAlan Somers 15669821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 1567d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 15689821f1d3SAlan Somers 15699821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 15709821f1d3SAlan Somers /* 15719821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 15729821f1d3SAlan Somers * filesystem daemon 15739821f1d3SAlan Somers */ 1574b5aaf286SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 15759821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 15767fc0921dSAlan Somers leak(fd); 15779821f1d3SAlan Somers } 15789821f1d3SAlan Somers 15790d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */ 15800d3a88d7SAlan Somers TEST_F(Write, update_file_size) 15819821f1d3SAlan Somers { 15829821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 15839821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 15849821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 15859821f1d3SAlan Somers struct stat sb; 15869821f1d3SAlan Somers uint64_t ino = 42; 15879821f1d3SAlan Somers int fd; 15889821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 15899821f1d3SAlan Somers 15909821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 15919821f1d3SAlan Somers expect_open(ino, 0, 1); 1592bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 15939821f1d3SAlan Somers 15949821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 1595d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 15969821f1d3SAlan Somers 15979821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 15989821f1d3SAlan Somers /* Get cached attributes */ 15999821f1d3SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 16009821f1d3SAlan Somers ASSERT_EQ(bufsize, sb.st_size); 16017fc0921dSAlan Somers leak(fd); 16029821f1d3SAlan Somers } 1603