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 static sig_atomic_t s_sigxfsz; 56a639731bSAlan Somers 57a639731bSAlan Somers void SetUp() { 58a639731bSAlan Somers s_sigxfsz = 0; 59a639731bSAlan Somers FuseTest::SetUp(); 60a639731bSAlan Somers } 61a639731bSAlan Somers 62a639731bSAlan Somers void TearDown() { 63a639731bSAlan Somers struct sigaction sa; 64a639731bSAlan Somers 65a639731bSAlan Somers bzero(&sa, sizeof(sa)); 66a639731bSAlan Somers sa.sa_handler = SIG_DFL; 67a639731bSAlan Somers sigaction(SIGXFSZ, &sa, NULL); 68a639731bSAlan Somers 69a639731bSAlan Somers FuseTest::TearDown(); 70a639731bSAlan Somers } 719821f1d3SAlan Somers 729821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 739821f1d3SAlan Somers { 749821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); 759821f1d3SAlan Somers } 769821f1d3SAlan Somers 779821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r) 789821f1d3SAlan Somers { 799821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 809821f1d3SAlan Somers ResultOf([=](auto in) { 8129edc611SAlan Somers return (in.header.opcode == FUSE_RELEASE && 8229edc611SAlan Somers in.header.nodeid == ino); 839821f1d3SAlan Somers }, Eq(true)), 849821f1d3SAlan Somers _) 859821f1d3SAlan Somers ).WillRepeatedly(Invoke(r)); 869821f1d3SAlan Somers } 879821f1d3SAlan Somers 88bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 89bda39894SAlan Somers uint64_t osize, const void *contents) 90bda39894SAlan Somers { 91bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents); 92bda39894SAlan Somers } 93bda39894SAlan Somers 9484879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */ 9584879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size, 9684879e46SAlan Somers const void *contents) 9784879e46SAlan Somers { 9884879e46SAlan Somers EXPECT_CALL(*m_mock, process( 9984879e46SAlan Somers ResultOf([=](auto in) { 10084879e46SAlan Somers const char *buf = (const char*)in.body.bytes + 10184879e46SAlan Somers sizeof(struct fuse_write_in); 10284879e46SAlan Somers 10384879e46SAlan Somers return (in.header.opcode == FUSE_WRITE && 10484879e46SAlan Somers in.header.nodeid == ino && 10584879e46SAlan Somers in.body.write.offset == offset && 10684879e46SAlan Somers in.body.write.size == size && 10784879e46SAlan Somers 0 == bcmp(buf, contents, size)); 10884879e46SAlan Somers }, Eq(true)), 10984879e46SAlan Somers _) 11084879e46SAlan Somers ).Times(AtMost(1)) 11184879e46SAlan Somers .WillRepeatedly(Invoke( 11284879e46SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 11384879e46SAlan Somers SET_OUT_HEADER_LEN(out, write); 11484879e46SAlan Somers out.body.write.size = size; 11584879e46SAlan Somers }) 11684879e46SAlan Somers )); 11784879e46SAlan Somers } 11884879e46SAlan Somers 1199821f1d3SAlan Somers }; 1209821f1d3SAlan Somers 121a639731bSAlan Somers sig_atomic_t Write::s_sigxfsz = 0; 122a639731bSAlan Somers 12316bd2d47SAlan Somers class Write_7_8: public FuseTest { 12416bd2d47SAlan Somers 12516bd2d47SAlan Somers public: 12616bd2d47SAlan Somers virtual void SetUp() { 12716bd2d47SAlan Somers m_kernel_minor_version = 8; 12816bd2d47SAlan Somers FuseTest::SetUp(); 12916bd2d47SAlan Somers } 13016bd2d47SAlan Somers 13116bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 13216bd2d47SAlan Somers { 13316bd2d47SAlan Somers FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1); 13416bd2d47SAlan Somers } 13516bd2d47SAlan Somers 13616bd2d47SAlan Somers }; 13716bd2d47SAlan Somers 1389821f1d3SAlan Somers class AioWrite: public Write { 1399821f1d3SAlan Somers virtual void SetUp() { 140c2265ae7SAlan Somers if (!is_unsafe_aio_enabled()) 1419821f1d3SAlan Somers GTEST_SKIP() << 1429821f1d3SAlan Somers "vfs.aio.enable_unsafe must be set for this test"; 143c2265ae7SAlan Somers FuseTest::SetUp(); 1449821f1d3SAlan Somers } 1459821f1d3SAlan Somers }; 1469821f1d3SAlan Somers 1479821f1d3SAlan Somers /* Tests for the writeback cache mode */ 1489821f1d3SAlan Somers class WriteBack: public Write { 149bda39894SAlan Somers public: 1509821f1d3SAlan Somers virtual void SetUp() { 151f8ebf1cdSAlan Somers m_init_flags |= FUSE_WRITEBACK_CACHE; 1529821f1d3SAlan Somers FuseTest::SetUp(); 1539821f1d3SAlan Somers if (IsSkipped()) 1549821f1d3SAlan Somers return; 1559821f1d3SAlan Somers } 1569821f1d3SAlan Somers 157bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 158bda39894SAlan Somers uint64_t osize, const void *contents) 159bda39894SAlan Somers { 160bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0, 161bda39894SAlan Somers contents); 162bda39894SAlan Somers } 1639821f1d3SAlan Somers }; 1649821f1d3SAlan Somers 16584879e46SAlan Somers class WriteBackAsync: public WriteBack { 16684879e46SAlan Somers public: 16784879e46SAlan Somers virtual void SetUp() { 16884879e46SAlan Somers m_async = true; 169f928dbcbSAlan Somers m_maxwrite = 65536; 17084879e46SAlan Somers WriteBack::SetUp(); 17184879e46SAlan Somers } 17284879e46SAlan Somers }; 17384879e46SAlan Somers 174fef46454SAlan Somers class TimeGran: public WriteBackAsync, public WithParamInterface<unsigned> { 175fef46454SAlan Somers public: 176fef46454SAlan Somers virtual void SetUp() { 177fef46454SAlan Somers m_time_gran = 1 << GetParam(); 178fef46454SAlan Somers WriteBackAsync::SetUp(); 179fef46454SAlan Somers } 180fef46454SAlan Somers }; 181fef46454SAlan Somers 1828eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */ 1838eecd9ceSAlan Somers class WriteCluster: public WriteBack { 1848eecd9ceSAlan Somers public: 1858eecd9ceSAlan Somers virtual void SetUp() { 1868eecd9ceSAlan Somers m_async = true; 1878e765737SAlan Somers m_maxwrite = 1 << 25; // Anything larger than MAXPHYS will suffice 1888eecd9ceSAlan Somers WriteBack::SetUp(); 189f8ebf1cdSAlan Somers if (m_maxphys < 2 * DFLTPHYS) 190f8ebf1cdSAlan Somers GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS" 191f8ebf1cdSAlan Somers << " for this test"; 192f2704f05SAlan Somers if (m_maxphys < 2 * m_maxbcachebuf) 1936ca3b02bSAlan Somers GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf" 1946ca3b02bSAlan Somers << " for this test"; 1958eecd9ceSAlan Somers } 1968eecd9ceSAlan Somers }; 1978eecd9ceSAlan Somers 198f928dbcbSAlan Somers /* Tests relating to the server's max_write property */ 199f928dbcbSAlan Somers class WriteMaxWrite: public Write { 200f928dbcbSAlan Somers public: 201f928dbcbSAlan Somers virtual void SetUp() { 202f928dbcbSAlan Somers /* 203f928dbcbSAlan Somers * For this test, m_maxwrite must be less than either m_maxbcachebuf or 204f928dbcbSAlan Somers * maxphys. 205f928dbcbSAlan Somers */ 206f928dbcbSAlan Somers m_maxwrite = 32768; 207f928dbcbSAlan Somers Write::SetUp(); 208f928dbcbSAlan Somers } 209f928dbcbSAlan Somers }; 210f928dbcbSAlan Somers 211*032a5bd5SAlan Somers class WriteEofDuringVnopStrategy: public Write, public WithParamInterface<int> 212*032a5bd5SAlan Somers {}; 213*032a5bd5SAlan Somers 214a639731bSAlan Somers void sigxfsz_handler(int __unused sig) { 215a639731bSAlan Somers Write::s_sigxfsz = 1; 216a639731bSAlan Somers } 217a639731bSAlan Somers 2189821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */ 2199821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ 2209821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write) 2219821f1d3SAlan Somers { 2229821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2239821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2249821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 2259821f1d3SAlan Somers uint64_t ino = 42; 2269821f1d3SAlan Somers uint64_t offset = 4096; 2279821f1d3SAlan Somers int fd; 2289821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 2299821f1d3SAlan Somers struct aiocb iocb, *piocb; 2309821f1d3SAlan Somers 2319821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 2329821f1d3SAlan Somers expect_open(ino, 0, 1); 233bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS); 2349821f1d3SAlan Somers 2359821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 236d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 2379821f1d3SAlan Somers 2389821f1d3SAlan Somers iocb.aio_nbytes = bufsize; 2399821f1d3SAlan Somers iocb.aio_fildes = fd; 2405a0b9a27SAlan Somers iocb.aio_buf = __DECONST(void *, CONTENTS); 2419821f1d3SAlan Somers iocb.aio_offset = offset; 2429821f1d3SAlan Somers iocb.aio_sigevent.sigev_notify = SIGEV_NONE; 2439821f1d3SAlan Somers ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno); 2449821f1d3SAlan Somers ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); 2457fc0921dSAlan Somers leak(fd); 2469821f1d3SAlan Somers } 2479821f1d3SAlan Somers 2489821f1d3SAlan Somers /* 2499821f1d3SAlan Somers * When a file is opened with O_APPEND, we should forward that flag to 2509821f1d3SAlan Somers * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the 2519821f1d3SAlan Somers * offset internally. That way we'll work both with filesystems that 2529821f1d3SAlan Somers * understand O_APPEND (and ignore the offset) and filesystems that don't (and 2539821f1d3SAlan Somers * simply use the offset). 2549821f1d3SAlan Somers * 2559821f1d3SAlan Somers * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the 2569821f1d3SAlan Somers * Open.o_append test. 2579821f1d3SAlan Somers */ 2589821f1d3SAlan Somers TEST_F(Write, append) 2599821f1d3SAlan Somers { 2609821f1d3SAlan Somers const ssize_t BUFSIZE = 9; 2619821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2629821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2639821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 2649821f1d3SAlan Somers uint64_t ino = 42; 2659821f1d3SAlan Somers /* 2669821f1d3SAlan Somers * Set offset to a maxbcachebuf boundary so we don't need to RMW when 2679821f1d3SAlan Somers * using writeback caching 2689821f1d3SAlan Somers */ 2699821f1d3SAlan Somers uint64_t initial_offset = m_maxbcachebuf; 2709821f1d3SAlan Somers int fd; 2719821f1d3SAlan Somers 2729821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset); 2739821f1d3SAlan Somers expect_open(ino, 0, 1); 274bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS); 2759821f1d3SAlan Somers 2769821f1d3SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ 2779821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND); 278d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 2799821f1d3SAlan Somers 2809821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 2817fc0921dSAlan Somers leak(fd); 2829821f1d3SAlan Somers } 2839821f1d3SAlan Somers 284a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */ 285a87e0831SAlan Somers TEST_F(Write, append_to_cached) 286a87e0831SAlan Somers { 287a87e0831SAlan Somers const ssize_t BUFSIZE = 9; 288a87e0831SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 289a87e0831SAlan Somers const char RELPATH[] = "some_file.txt"; 290a87e0831SAlan Somers char *oldcontents, *oldbuf; 291a87e0831SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 292a87e0831SAlan Somers uint64_t ino = 42; 293a87e0831SAlan Somers /* 294a87e0831SAlan Somers * Set offset in between maxbcachebuf boundary to test buffer handling 295a87e0831SAlan Somers */ 296a87e0831SAlan Somers uint64_t oldsize = m_maxbcachebuf / 2; 297a87e0831SAlan Somers int fd; 298a87e0831SAlan Somers 299a87e0831SAlan Somers oldcontents = (char*)calloc(1, oldsize); 3005a0b9a27SAlan Somers ASSERT_NE(nullptr, oldcontents) << strerror(errno); 301a87e0831SAlan Somers oldbuf = (char*)malloc(oldsize); 3025a0b9a27SAlan Somers ASSERT_NE(nullptr, oldbuf) << strerror(errno); 303a87e0831SAlan Somers 304a87e0831SAlan Somers expect_lookup(RELPATH, ino, oldsize); 305a87e0831SAlan Somers expect_open(ino, 0, 1); 306a87e0831SAlan Somers expect_read(ino, 0, oldsize, oldsize, oldcontents); 30784879e46SAlan Somers maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS); 308a87e0831SAlan Somers 309a87e0831SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ 310a87e0831SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND); 311d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 312a87e0831SAlan Somers 313a87e0831SAlan Somers /* Read the old data into the cache */ 314a87e0831SAlan Somers ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize)) 315a87e0831SAlan Somers << strerror(errno); 316a87e0831SAlan Somers 317a87e0831SAlan Somers /* Write the new data. There should be no more read operations */ 318a87e0831SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 3197fc0921dSAlan Somers leak(fd); 3207ee7e409SAlan Somers free(oldbuf); 3217ee7e409SAlan Somers free(oldcontents); 322a87e0831SAlan Somers } 323a87e0831SAlan Somers 3249821f1d3SAlan Somers TEST_F(Write, append_direct_io) 3259821f1d3SAlan Somers { 3269821f1d3SAlan Somers const ssize_t BUFSIZE = 9; 3279821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3289821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3299821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 3309821f1d3SAlan Somers uint64_t ino = 42; 3319821f1d3SAlan Somers uint64_t initial_offset = 4096; 3329821f1d3SAlan Somers int fd; 3339821f1d3SAlan Somers 3349821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset); 3359821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 336bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS); 3379821f1d3SAlan Somers 3389821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY | O_APPEND); 339d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 3409821f1d3SAlan Somers 3419821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 3427fc0921dSAlan Somers leak(fd); 3439821f1d3SAlan Somers } 3449821f1d3SAlan Somers 3459821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */ 3466af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache) 3479821f1d3SAlan Somers { 3489821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3499821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3509821f1d3SAlan Somers const char CONTENTS0[] = "abcdefgh"; 3519821f1d3SAlan Somers const char CONTENTS1[] = "ijklmnop"; 3529821f1d3SAlan Somers uint64_t ino = 42; 3539821f1d3SAlan Somers int fd; 3549821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1; 3559821f1d3SAlan Somers char readbuf[bufsize]; 3569821f1d3SAlan Somers 3579821f1d3SAlan Somers expect_lookup(RELPATH, ino, bufsize); 3589821f1d3SAlan Somers expect_open(ino, 0, 1); 3599821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS0); 360bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS1); 3619821f1d3SAlan Somers 3629821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 363d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 3649821f1d3SAlan Somers 3659821f1d3SAlan Somers // Prime cache 3669821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 3679821f1d3SAlan Somers 3689821f1d3SAlan Somers // Write directly, evicting cache 3699821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 3709821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 3719821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno); 3729821f1d3SAlan Somers 3739821f1d3SAlan Somers // Read again. Cache should be bypassed 3749821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS1); 3759821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 3769821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 3779821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 3789821f1d3SAlan Somers ASSERT_STREQ(readbuf, CONTENTS1); 3799821f1d3SAlan Somers 3807fc0921dSAlan Somers leak(fd); 3819821f1d3SAlan Somers } 3829821f1d3SAlan Somers 3839821f1d3SAlan Somers /* 38412292a99SAlan Somers * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not 38512292a99SAlan Somers * allowed to return a short write for that file handle. However, if it does 38612292a99SAlan Somers * then we should still do our darndest to handle it by resending the unwritten 38712292a99SAlan Somers * portion. 3889821f1d3SAlan Somers */ 38912292a99SAlan Somers TEST_F(Write, indirect_io_short_write) 3909821f1d3SAlan Somers { 3919821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3929821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3939821f1d3SAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 3949821f1d3SAlan Somers uint64_t ino = 42; 3959821f1d3SAlan Somers int fd; 3969821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 39712292a99SAlan Somers ssize_t bufsize0 = 11; 39812292a99SAlan Somers ssize_t bufsize1 = strlen(CONTENTS) - bufsize0; 39912292a99SAlan Somers const char *contents1 = CONTENTS + bufsize0; 4009821f1d3SAlan Somers 4019821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 40212292a99SAlan Somers expect_open(ino, 0, 1); 403bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize0, CONTENTS); 404bda39894SAlan Somers expect_write(ino, bufsize0, bufsize1, bufsize1, contents1); 4059821f1d3SAlan Somers 4069821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 407d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 4089821f1d3SAlan Somers 4099821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 4107fc0921dSAlan Somers leak(fd); 4119821f1d3SAlan Somers } 4129821f1d3SAlan Somers 4139821f1d3SAlan Somers /* 41412292a99SAlan Somers * When the direct_io option is used, filesystems are allowed to write less 41512292a99SAlan Somers * data than requested. We should return the short write to userland. 41612292a99SAlan Somers */ 41712292a99SAlan Somers TEST_F(Write, direct_io_short_write) 41812292a99SAlan Somers { 41912292a99SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 42012292a99SAlan Somers const char RELPATH[] = "some_file.txt"; 42112292a99SAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 42212292a99SAlan Somers uint64_t ino = 42; 42312292a99SAlan Somers int fd; 42412292a99SAlan Somers ssize_t bufsize = strlen(CONTENTS); 42512292a99SAlan Somers ssize_t halfbufsize = bufsize / 2; 42612292a99SAlan Somers 42712292a99SAlan Somers expect_lookup(RELPATH, ino, 0); 42812292a99SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 429bda39894SAlan Somers expect_write(ino, 0, bufsize, halfbufsize, CONTENTS); 43012292a99SAlan Somers 43112292a99SAlan Somers fd = open(FULLPATH, O_WRONLY); 432d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 43312292a99SAlan Somers 43412292a99SAlan Somers ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 4357fc0921dSAlan Somers leak(fd); 43612292a99SAlan Somers } 43712292a99SAlan Somers 43812292a99SAlan Somers /* 4399821f1d3SAlan Somers * An insidious edge case: the filesystem returns a short write, and the 4409821f1d3SAlan Somers * difference between what we requested and what it actually wrote crosses an 4419821f1d3SAlan Somers * iov element boundary 4429821f1d3SAlan Somers */ 44312292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov) 4449821f1d3SAlan Somers { 4459821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4469821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 4479821f1d3SAlan Somers const char *CONTENTS0 = "abcdefgh"; 4489821f1d3SAlan Somers const char *CONTENTS1 = "ijklmnop"; 4499821f1d3SAlan Somers const char *EXPECTED0 = "abcdefghijklmnop"; 4509821f1d3SAlan Somers uint64_t ino = 42; 4519821f1d3SAlan Somers int fd; 4529821f1d3SAlan Somers ssize_t size0 = strlen(CONTENTS0) - 1; 4539821f1d3SAlan Somers ssize_t size1 = strlen(CONTENTS1) + 1; 4549821f1d3SAlan Somers ssize_t totalsize = size0 + size1; 4559821f1d3SAlan Somers struct iovec iov[2]; 4569821f1d3SAlan Somers 4579821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 4589821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 459bda39894SAlan Somers expect_write(ino, 0, totalsize, size0, EXPECTED0); 4609821f1d3SAlan Somers 4619821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 462d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 4639821f1d3SAlan Somers 4645a0b9a27SAlan Somers iov[0].iov_base = __DECONST(void*, CONTENTS0); 4659821f1d3SAlan Somers iov[0].iov_len = strlen(CONTENTS0); 4665a0b9a27SAlan Somers iov[1].iov_base = __DECONST(void*, CONTENTS1); 4679821f1d3SAlan Somers iov[1].iov_len = strlen(CONTENTS1); 46812292a99SAlan Somers ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno); 4697fc0921dSAlan Somers leak(fd); 4709821f1d3SAlan Somers } 4719821f1d3SAlan Somers 472a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */ 473a639731bSAlan Somers TEST_F(Write, rlimit_fsize) 474a639731bSAlan Somers { 475a639731bSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 476a639731bSAlan Somers const char RELPATH[] = "some_file.txt"; 477a639731bSAlan Somers const char *CONTENTS = "abcdefgh"; 478a639731bSAlan Somers struct rlimit rl; 479a639731bSAlan Somers ssize_t bufsize = strlen(CONTENTS); 480a639731bSAlan Somers off_t offset = 1'000'000'000; 481a639731bSAlan Somers uint64_t ino = 42; 482a639731bSAlan Somers int fd; 483a639731bSAlan Somers 484a639731bSAlan Somers expect_lookup(RELPATH, ino, 0); 485a639731bSAlan Somers expect_open(ino, 0, 1); 486a639731bSAlan Somers 487a639731bSAlan Somers rl.rlim_cur = offset; 488a639731bSAlan Somers rl.rlim_max = 10 * offset; 489a639731bSAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno); 490a639731bSAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno); 491a639731bSAlan Somers 492a639731bSAlan Somers fd = open(FULLPATH, O_WRONLY); 493a639731bSAlan Somers 494d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 495a639731bSAlan Somers 496a639731bSAlan Somers ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset)); 497a639731bSAlan Somers EXPECT_EQ(EFBIG, errno); 498a639731bSAlan Somers EXPECT_EQ(1, s_sigxfsz); 4997fc0921dSAlan Somers leak(fd); 500a639731bSAlan Somers } 501a639731bSAlan Somers 5029821f1d3SAlan Somers /* 503b9e20197SAlan Somers * A short read indicates EOF. Test that nothing bad happens if we get EOF 504b9e20197SAlan Somers * during the R of a RMW operation. 505b9e20197SAlan Somers */ 506f8ebf1cdSAlan Somers TEST_F(Write, eof_during_rmw) 507b9e20197SAlan Somers { 508b9e20197SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 509b9e20197SAlan Somers const char RELPATH[] = "some_file.txt"; 510b9e20197SAlan Somers const char *CONTENTS = "abcdefgh"; 511b9e20197SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 512b9e20197SAlan Somers uint64_t ino = 42; 513b9e20197SAlan Somers uint64_t offset = 1; 514ae39db74SAlan Somers ssize_t bufsize = strlen(CONTENTS) + 1; 515b9e20197SAlan Somers off_t orig_fsize = 10; 516b9e20197SAlan Somers off_t truncated_fsize = 5; 517b9e20197SAlan Somers int fd; 518b9e20197SAlan Somers 519b9e20197SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1); 520b9e20197SAlan Somers expect_open(ino, 0, 1); 521b9e20197SAlan Somers expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR); 522b9e20197SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS); 523b9e20197SAlan Somers 524b9e20197SAlan Somers fd = open(FULLPATH, O_RDWR); 525d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 526b9e20197SAlan Somers 527b9e20197SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 528b9e20197SAlan Somers << strerror(errno); 5297fc0921dSAlan Somers leak(fd); 530b9e20197SAlan Somers } 531b9e20197SAlan Somers 532b9e20197SAlan Somers /* 533*032a5bd5SAlan Somers * VOP_STRATEGY should not query the server for the file's size, even if its 534*032a5bd5SAlan Somers * cached attributes have expired. 535*032a5bd5SAlan Somers * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937 536*032a5bd5SAlan Somers */ 537*032a5bd5SAlan Somers TEST_P(WriteEofDuringVnopStrategy, eof_during_vop_strategy) 538*032a5bd5SAlan Somers { 539*032a5bd5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 540*032a5bd5SAlan Somers const char RELPATH[] = "some_file.txt"; 541*032a5bd5SAlan Somers Sequence seq; 542*032a5bd5SAlan Somers const off_t filesize = 2 * m_maxbcachebuf; 543*032a5bd5SAlan Somers void *contents; 544*032a5bd5SAlan Somers uint64_t ino = 42; 545*032a5bd5SAlan Somers uint64_t attr_valid = 0; 546*032a5bd5SAlan Somers uint64_t attr_valid_nsec = 0; 547*032a5bd5SAlan Somers mode_t mode = S_IFREG | 0644; 548*032a5bd5SAlan Somers int fd; 549*032a5bd5SAlan Somers int ngetattrs; 550*032a5bd5SAlan Somers 551*032a5bd5SAlan Somers ngetattrs = GetParam(); 552*032a5bd5SAlan Somers contents = calloc(1, filesize); 553*032a5bd5SAlan Somers 554*032a5bd5SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 555*032a5bd5SAlan Somers .WillRepeatedly(Invoke( 556*032a5bd5SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 557*032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, entry); 558*032a5bd5SAlan Somers out.body.entry.attr.mode = mode; 559*032a5bd5SAlan Somers out.body.entry.nodeid = ino; 560*032a5bd5SAlan Somers out.body.entry.attr.nlink = 1; 561*032a5bd5SAlan Somers out.body.entry.attr.size = filesize; 562*032a5bd5SAlan Somers out.body.entry.attr_valid = attr_valid; 563*032a5bd5SAlan Somers out.body.entry.attr_valid_nsec = attr_valid_nsec; 564*032a5bd5SAlan Somers }))); 565*032a5bd5SAlan Somers expect_open(ino, 0, 1); 566*032a5bd5SAlan Somers EXPECT_CALL(*m_mock, process( 567*032a5bd5SAlan Somers ResultOf([=](auto in) { 568*032a5bd5SAlan Somers return (in.header.opcode == FUSE_GETATTR && 569*032a5bd5SAlan Somers in.header.nodeid == ino); 570*032a5bd5SAlan Somers }, Eq(true)), 571*032a5bd5SAlan Somers _) 572*032a5bd5SAlan Somers ).Times(Between(ngetattrs - 1, ngetattrs)) 573*032a5bd5SAlan Somers .InSequence(seq) 574*032a5bd5SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 575*032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, attr); 576*032a5bd5SAlan Somers out.body.attr.attr.ino = ino; 577*032a5bd5SAlan Somers out.body.attr.attr.mode = mode; 578*032a5bd5SAlan Somers out.body.attr.attr_valid = attr_valid; 579*032a5bd5SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec; 580*032a5bd5SAlan Somers out.body.attr.attr.size = filesize; 581*032a5bd5SAlan Somers }))); 582*032a5bd5SAlan Somers EXPECT_CALL(*m_mock, process( 583*032a5bd5SAlan Somers ResultOf([=](auto in) { 584*032a5bd5SAlan Somers return (in.header.opcode == FUSE_GETATTR && 585*032a5bd5SAlan Somers in.header.nodeid == ino); 586*032a5bd5SAlan Somers }, Eq(true)), 587*032a5bd5SAlan Somers _) 588*032a5bd5SAlan Somers ).InSequence(seq) 589*032a5bd5SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 590*032a5bd5SAlan Somers SET_OUT_HEADER_LEN(out, attr); 591*032a5bd5SAlan Somers out.body.attr.attr.ino = ino; 592*032a5bd5SAlan Somers out.body.attr.attr.mode = mode; 593*032a5bd5SAlan Somers out.body.attr.attr_valid = attr_valid; 594*032a5bd5SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec; 595*032a5bd5SAlan Somers out.body.attr.attr.size = filesize / 2; 596*032a5bd5SAlan Somers }))); 597*032a5bd5SAlan Somers expect_write(ino, 0, filesize / 2, filesize / 2, contents); 598*032a5bd5SAlan Somers 599*032a5bd5SAlan Somers fd = open(FULLPATH, O_RDWR); 600*032a5bd5SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 601*032a5bd5SAlan Somers ASSERT_EQ(filesize / 2, write(fd, contents, filesize / 2)) 602*032a5bd5SAlan Somers << strerror(errno); 603*032a5bd5SAlan Somers 604*032a5bd5SAlan Somers } 605*032a5bd5SAlan Somers 606*032a5bd5SAlan Somers INSTANTIATE_TEST_CASE_P(W, WriteEofDuringVnopStrategy, 607*032a5bd5SAlan Somers Values(1, 2, 3) 608*032a5bd5SAlan Somers ); 609*032a5bd5SAlan Somers 610*032a5bd5SAlan Somers /* 6119821f1d3SAlan Somers * If the kernel cannot be sure which uid, gid, or pid was responsible for a 6129821f1d3SAlan Somers * write, then it must set the FUSE_WRITE_CACHE bit 6139821f1d3SAlan Somers */ 6149821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */ 615f8ebf1cdSAlan Somers TEST_F(Write, mmap) 6169821f1d3SAlan Somers { 6179821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6189821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6199821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 6209821f1d3SAlan Somers uint64_t ino = 42; 6219821f1d3SAlan Somers int fd; 6229821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 6239821f1d3SAlan Somers void *p; 6249821f1d3SAlan Somers uint64_t offset = 10; 6259821f1d3SAlan Somers size_t len; 6269821f1d3SAlan Somers void *zeros, *expected; 6279821f1d3SAlan Somers 6289821f1d3SAlan Somers len = getpagesize(); 6299821f1d3SAlan Somers 6309821f1d3SAlan Somers zeros = calloc(1, len); 6315a0b9a27SAlan Somers ASSERT_NE(nullptr, zeros); 6329821f1d3SAlan Somers expected = calloc(1, len); 6335a0b9a27SAlan Somers ASSERT_NE(nullptr, expected); 6349821f1d3SAlan Somers memmove((uint8_t*)expected + offset, CONTENTS, bufsize); 6359821f1d3SAlan Somers 6369821f1d3SAlan Somers expect_lookup(RELPATH, ino, len); 6379821f1d3SAlan Somers expect_open(ino, 0, 1); 6389821f1d3SAlan Somers expect_read(ino, 0, len, len, zeros); 6399821f1d3SAlan Somers /* 6409821f1d3SAlan Somers * Writes from the pager may or may not be associated with the correct 641cf437e2aSAlan Somers * pid, so they must set FUSE_WRITE_CACHE. 6429821f1d3SAlan Somers */ 643bda39894SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected); 6449f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 6459821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 6469821f1d3SAlan Somers 6479821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 648d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 6499821f1d3SAlan Somers 6509821f1d3SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 6519821f1d3SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno); 6529821f1d3SAlan Somers 6539821f1d3SAlan Somers memmove((uint8_t*)p + offset, CONTENTS, bufsize); 6549821f1d3SAlan Somers 6559821f1d3SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 6569821f1d3SAlan Somers close(fd); // Write mmap'd data on close 6579821f1d3SAlan Somers 6589821f1d3SAlan Somers free(expected); 6599821f1d3SAlan Somers free(zeros); 6608e765737SAlan Somers 6618e765737SAlan Somers leak(fd); 6629821f1d3SAlan Somers } 6639821f1d3SAlan Somers 664f8ebf1cdSAlan Somers TEST_F(Write, pwrite) 6659821f1d3SAlan Somers { 6669821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6679821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6689821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 6699821f1d3SAlan Somers uint64_t ino = 42; 6706ca3b02bSAlan Somers uint64_t offset = m_maxbcachebuf; 6719821f1d3SAlan Somers int fd; 6729821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 6739821f1d3SAlan Somers 6749821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6759821f1d3SAlan Somers expect_open(ino, 0, 1); 676bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS); 6779821f1d3SAlan Somers 6789821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 679d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 6809821f1d3SAlan Somers 6819821f1d3SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 6829821f1d3SAlan Somers << strerror(errno); 6837fc0921dSAlan Somers leak(fd); 6849821f1d3SAlan Somers } 6859821f1d3SAlan Somers 686788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */ 687788af953SAlan Somers TEST_F(Write, timestamps) 688788af953SAlan Somers { 689788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 690788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 691788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 692788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 693788af953SAlan Somers uint64_t ino = 42; 694788af953SAlan Somers struct stat sb0, sb1; 695788af953SAlan Somers int fd; 696788af953SAlan Somers 697788af953SAlan Somers expect_lookup(RELPATH, ino, 0); 698788af953SAlan Somers expect_open(ino, 0, 1); 699788af953SAlan Somers maybe_expect_write(ino, 0, bufsize, CONTENTS); 700788af953SAlan Somers 701788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 702d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 703788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno); 704788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 705788af953SAlan Somers 706788af953SAlan Somers nap(); 707788af953SAlan Somers 708788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno); 709788af953SAlan Somers 710788af953SAlan Somers EXPECT_EQ(sb0.st_atime, sb1.st_atime); 711788af953SAlan Somers EXPECT_NE(sb0.st_mtime, sb1.st_mtime); 712788af953SAlan Somers EXPECT_NE(sb0.st_ctime, sb1.st_ctime); 7138e765737SAlan Somers 7148e765737SAlan Somers leak(fd); 715788af953SAlan Somers } 716788af953SAlan Somers 7179821f1d3SAlan Somers TEST_F(Write, write) 7189821f1d3SAlan Somers { 7199821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7209821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7219821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 7229821f1d3SAlan Somers uint64_t ino = 42; 7239821f1d3SAlan Somers int fd; 7249821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 7259821f1d3SAlan Somers 7269821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 7279821f1d3SAlan Somers expect_open(ino, 0, 1); 728bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 7299821f1d3SAlan Somers 7309821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 731d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7329821f1d3SAlan Somers 7339821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 7347fc0921dSAlan Somers leak(fd); 7359821f1d3SAlan Somers } 7369821f1d3SAlan Somers 7379821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */ 738f928dbcbSAlan Somers TEST_F(WriteMaxWrite, write) 7399821f1d3SAlan Somers { 7409821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7419821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7429821f1d3SAlan Somers int *contents; 7439821f1d3SAlan Somers uint64_t ino = 42; 7449821f1d3SAlan Somers int fd; 7459821f1d3SAlan Somers ssize_t halfbufsize, bufsize; 7469821f1d3SAlan Somers 7478eecd9ceSAlan Somers halfbufsize = m_mock->m_maxwrite; 748f928dbcbSAlan Somers if (halfbufsize >= m_maxbcachebuf || halfbufsize >= m_maxphys) 749f928dbcbSAlan Somers GTEST_SKIP() << "Must lower m_maxwrite for this test"; 7509821f1d3SAlan Somers bufsize = halfbufsize * 2; 7519821f1d3SAlan Somers contents = (int*)malloc(bufsize); 7525a0b9a27SAlan Somers ASSERT_NE(nullptr, contents); 7539821f1d3SAlan Somers for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) { 7549821f1d3SAlan Somers contents[i] = i; 7559821f1d3SAlan Somers } 7569821f1d3SAlan Somers 7579821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 7589821f1d3SAlan Somers expect_open(ino, 0, 1); 759f2704f05SAlan Somers maybe_expect_write(ino, 0, halfbufsize, contents); 76084879e46SAlan Somers maybe_expect_write(ino, halfbufsize, halfbufsize, 7619821f1d3SAlan Somers &contents[halfbufsize / sizeof(int)]); 7629821f1d3SAlan Somers 7639821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 764d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7659821f1d3SAlan Somers 7669821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno); 7677fc0921dSAlan Somers leak(fd); 7689821f1d3SAlan Somers 7699821f1d3SAlan Somers free(contents); 7709821f1d3SAlan Somers } 7719821f1d3SAlan Somers 7729821f1d3SAlan Somers TEST_F(Write, write_nothing) 7739821f1d3SAlan Somers { 7749821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7759821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7769821f1d3SAlan Somers const char *CONTENTS = ""; 7779821f1d3SAlan Somers uint64_t ino = 42; 7789821f1d3SAlan Somers int fd; 7799821f1d3SAlan Somers ssize_t bufsize = 0; 7809821f1d3SAlan Somers 7819821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 7829821f1d3SAlan Somers expect_open(ino, 0, 1); 7839821f1d3SAlan Somers 7849821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 785d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7869821f1d3SAlan Somers 7879821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 7887fc0921dSAlan Somers leak(fd); 7899821f1d3SAlan Somers } 7909821f1d3SAlan Somers 79116bd2d47SAlan Somers TEST_F(Write_7_8, write) 79216bd2d47SAlan Somers { 79316bd2d47SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 79416bd2d47SAlan Somers const char RELPATH[] = "some_file.txt"; 79516bd2d47SAlan Somers const char *CONTENTS = "abcdefgh"; 79616bd2d47SAlan Somers uint64_t ino = 42; 79716bd2d47SAlan Somers int fd; 79816bd2d47SAlan Somers ssize_t bufsize = strlen(CONTENTS); 79916bd2d47SAlan Somers 80016bd2d47SAlan Somers expect_lookup(RELPATH, ino, 0); 80116bd2d47SAlan Somers expect_open(ino, 0, 1); 802bda39894SAlan Somers expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS); 80316bd2d47SAlan Somers 80416bd2d47SAlan Somers fd = open(FULLPATH, O_WRONLY); 805d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 80616bd2d47SAlan Somers 80716bd2d47SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 8087fc0921dSAlan Somers leak(fd); 80916bd2d47SAlan Somers } 81016bd2d47SAlan Somers 8119821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */ 81284879e46SAlan Somers TEST_F(WriteBackAsync, close) 8139821f1d3SAlan Somers { 8149821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8159821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8169821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 8179821f1d3SAlan Somers uint64_t ino = 42; 8189821f1d3SAlan Somers int fd; 8199821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8209821f1d3SAlan Somers 8219821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 8229821f1d3SAlan Somers expect_open(ino, 0, 1); 823bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 8249821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 8259821f1d3SAlan Somers ResultOf([=](auto in) { 82629edc611SAlan Somers return (in.header.opcode == FUSE_SETATTR); 8279821f1d3SAlan Somers }, Eq(true)), 8289821f1d3SAlan Somers _) 82929edc611SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 8309821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, attr); 83129edc611SAlan Somers out.body.attr.attr.ino = ino; // Must match nodeid 8329821f1d3SAlan Somers }))); 8339f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 8349821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 8359821f1d3SAlan Somers 8369821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 8379821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 8389821f1d3SAlan Somers 8399821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 8409821f1d3SAlan Somers close(fd); 8419821f1d3SAlan Somers } 8429821f1d3SAlan Somers 8438eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */ 8448eecd9ceSAlan Somers TEST_F(WriteCluster, clustering) 8458eecd9ceSAlan Somers { 8468eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8478eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 8488eecd9ceSAlan Somers uint64_t ino = 42; 8498eecd9ceSAlan Somers int i, fd; 8508eecd9ceSAlan Somers void *wbuf, *wbuf2x; 8516ca3b02bSAlan Somers ssize_t bufsize = m_maxbcachebuf; 8526ca3b02bSAlan Somers off_t filesize = 5 * bufsize; 8538eecd9ceSAlan Somers 8548eecd9ceSAlan Somers wbuf = malloc(bufsize); 8555a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf) << strerror(errno); 8568eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 8578eecd9ceSAlan Somers wbuf2x = malloc(2 * bufsize); 8585a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf2x) << strerror(errno); 8598eecd9ceSAlan Somers memset(wbuf2x, 'X', 2 * bufsize); 8608eecd9ceSAlan Somers 8618eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 8628eecd9ceSAlan Somers expect_open(ino, 0, 1); 8638eecd9ceSAlan Somers /* 8648eecd9ceSAlan Somers * Writes of bufsize-bytes each should be clustered into greater sizes. 8658eecd9ceSAlan Somers * The amount of clustering is adaptive, so the first write actually 8668eecd9ceSAlan Somers * issued will be 2x bufsize and subsequent writes may be larger 8678eecd9ceSAlan Somers */ 8688eecd9ceSAlan Somers expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x); 8698eecd9ceSAlan Somers expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x); 8708eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 8718eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 8728eecd9ceSAlan Somers 8738eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 8748eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 8758eecd9ceSAlan Somers 8768eecd9ceSAlan Somers for (i = 0; i < 4; i++) { 8778eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 8788eecd9ceSAlan Somers << strerror(errno); 8798eecd9ceSAlan Somers } 8808eecd9ceSAlan Somers close(fd); 8817ee7e409SAlan Somers free(wbuf2x); 8827ee7e409SAlan Somers free(wbuf); 8838eecd9ceSAlan Somers } 8848eecd9ceSAlan Somers 8858eecd9ceSAlan Somers /* 8868eecd9ceSAlan Somers * When clustering writes, an I/O error to any of the cluster's children should 8878eecd9ceSAlan Somers * not panic the system on unmount 8888eecd9ceSAlan Somers */ 8898eecd9ceSAlan Somers /* 890425bbe9eSAlan Somers * Regression test for bug 238585 8918eecd9ceSAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565 8928eecd9ceSAlan Somers */ 893425bbe9eSAlan Somers TEST_F(WriteCluster, cluster_write_err) 8948eecd9ceSAlan Somers { 8958eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8968eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 8978eecd9ceSAlan Somers uint64_t ino = 42; 8988eecd9ceSAlan Somers int i, fd; 8998eecd9ceSAlan Somers void *wbuf; 9006ca3b02bSAlan Somers ssize_t bufsize = m_maxbcachebuf; 9016ca3b02bSAlan Somers off_t filesize = 4 * bufsize; 9028eecd9ceSAlan Somers 9038eecd9ceSAlan Somers wbuf = malloc(bufsize); 9045a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf) << strerror(errno); 9058eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 9068eecd9ceSAlan Somers 9078eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 9088eecd9ceSAlan Somers expect_open(ino, 0, 1); 9098eecd9ceSAlan Somers EXPECT_CALL(*m_mock, process( 9108eecd9ceSAlan Somers ResultOf([=](auto in) { 9118eecd9ceSAlan Somers return (in.header.opcode == FUSE_WRITE); 9128eecd9ceSAlan Somers }, Eq(true)), 9138eecd9ceSAlan Somers _) 9148eecd9ceSAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(EIO))); 9158eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 9168eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 9178eecd9ceSAlan Somers 9188eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 9198eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 9208eecd9ceSAlan Somers 9218eecd9ceSAlan Somers for (i = 0; i < 3; i++) { 9228eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 9238eecd9ceSAlan Somers << strerror(errno); 9248eecd9ceSAlan Somers } 9258eecd9ceSAlan Somers close(fd); 9267ee7e409SAlan Somers free(wbuf); 9278eecd9ceSAlan Somers } 9288eecd9ceSAlan Somers 9299821f1d3SAlan Somers /* 9305fccbf31SAlan Somers * In writeback mode, writes to an O_WRONLY file could trigger reads from the 9315fccbf31SAlan Somers * server. The FUSE protocol explicitly allows that. 9325fccbf31SAlan Somers */ 9335fccbf31SAlan Somers TEST_F(WriteBack, rmw) 9345fccbf31SAlan Somers { 9355fccbf31SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9365fccbf31SAlan Somers const char RELPATH[] = "some_file.txt"; 9375fccbf31SAlan Somers const char *CONTENTS = "abcdefgh"; 9385fccbf31SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 9395fccbf31SAlan Somers uint64_t ino = 42; 9405fccbf31SAlan Somers uint64_t offset = 1; 9415fccbf31SAlan Somers off_t fsize = 10; 9425fccbf31SAlan Somers int fd; 9435fccbf31SAlan Somers ssize_t bufsize = strlen(CONTENTS); 9445fccbf31SAlan Somers 945cad67791SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1); 9465fccbf31SAlan Somers expect_open(ino, 0, 1); 947d4fd0c81SAlan Somers expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY); 94884879e46SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS); 9495fccbf31SAlan Somers 9505fccbf31SAlan Somers fd = open(FULLPATH, O_WRONLY); 951d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 9525fccbf31SAlan Somers 9535fccbf31SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 9545fccbf31SAlan Somers << strerror(errno); 9557fc0921dSAlan Somers leak(fd); 9565fccbf31SAlan Somers } 9575fccbf31SAlan Somers 9585fccbf31SAlan Somers /* 9599821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 9609821f1d3SAlan Somers */ 96184879e46SAlan Somers TEST_F(WriteBack, cache) 9629821f1d3SAlan Somers { 9639821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9649821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 9659821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 9669821f1d3SAlan Somers uint64_t ino = 42; 9679821f1d3SAlan Somers int fd; 9689821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 9698e765737SAlan Somers uint8_t readbuf[bufsize]; 9709821f1d3SAlan Somers 9719821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 9729821f1d3SAlan Somers expect_open(ino, 0, 1); 973bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 9749821f1d3SAlan Somers 9759821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 976d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 9779821f1d3SAlan Somers 9789821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 9799821f1d3SAlan Somers /* 9809821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 9819821f1d3SAlan Somers * filesystem daemon 9829821f1d3SAlan Somers */ 9839821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 9849821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 9857fc0921dSAlan Somers leak(fd); 9869821f1d3SAlan Somers } 9879821f1d3SAlan Somers 9889821f1d3SAlan Somers /* 9899821f1d3SAlan Somers * With O_DIRECT, writes should be not committed to cache. Admittedly this is 9909821f1d3SAlan Somers * an odd test, because it would be unusual to use O_DIRECT for writes but not 9919821f1d3SAlan Somers * reads. 9929821f1d3SAlan Somers */ 9939821f1d3SAlan Somers TEST_F(WriteBack, o_direct) 9949821f1d3SAlan Somers { 9959821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9969821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 9979821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 9989821f1d3SAlan Somers uint64_t ino = 42; 9999821f1d3SAlan Somers int fd; 10009821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 10018e765737SAlan Somers uint8_t readbuf[bufsize]; 10029821f1d3SAlan Somers 10039821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 10049821f1d3SAlan Somers expect_open(ino, 0, 1); 1005bda39894SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE, 1006bda39894SAlan Somers CONTENTS); 10079821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS); 10089821f1d3SAlan Somers 10099821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_DIRECT); 1010d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 10119821f1d3SAlan Somers 10129821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 10139821f1d3SAlan Somers /* A subsequent read must query the daemon because cache is empty */ 10149821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 10159821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 10169821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 10177fc0921dSAlan Somers leak(fd); 10189821f1d3SAlan Somers } 10199821f1d3SAlan Somers 1020a62772a7SAlan Somers TEST_F(WriteBack, direct_io) 1021a62772a7SAlan Somers { 1022a62772a7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1023a62772a7SAlan Somers const char RELPATH[] = "some_file.txt"; 1024a62772a7SAlan Somers const char *CONTENTS = "abcdefgh"; 1025a62772a7SAlan Somers uint64_t ino = 42; 1026a62772a7SAlan Somers int fd; 1027a62772a7SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1028a62772a7SAlan Somers uint8_t readbuf[bufsize]; 1029a62772a7SAlan Somers 1030a62772a7SAlan Somers expect_lookup(RELPATH, ino, 0); 1031a62772a7SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 1032a62772a7SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE, 1033a62772a7SAlan Somers CONTENTS); 1034a62772a7SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS); 1035a62772a7SAlan Somers 1036a62772a7SAlan Somers fd = open(FULLPATH, O_RDWR); 1037d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1038a62772a7SAlan Somers 1039a62772a7SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1040a62772a7SAlan Somers /* A subsequent read must query the daemon because cache is empty */ 1041a62772a7SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 1042a62772a7SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 1043a62772a7SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 1044a62772a7SAlan Somers leak(fd); 1045a62772a7SAlan Somers } 1046a62772a7SAlan Somers 1047a62772a7SAlan Somers /* 1048a62772a7SAlan Somers * mmap should still be possible even if the server used direct_io. Mmap will 1049a62772a7SAlan Somers * still use the cache, though. 1050a62772a7SAlan Somers * 1051a62772a7SAlan Somers * Regression test for bug 247276 1052a62772a7SAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=247276 1053a62772a7SAlan Somers */ 1054a62772a7SAlan Somers TEST_F(WriteBack, mmap_direct_io) 1055a62772a7SAlan Somers { 1056a62772a7SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1057a62772a7SAlan Somers const char RELPATH[] = "some_file.txt"; 1058a62772a7SAlan Somers const char *CONTENTS = "abcdefgh"; 1059a62772a7SAlan Somers uint64_t ino = 42; 1060a62772a7SAlan Somers int fd; 1061a62772a7SAlan Somers size_t len; 1062a62772a7SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1063a62772a7SAlan Somers void *p, *zeros; 1064a62772a7SAlan Somers 1065a62772a7SAlan Somers len = getpagesize(); 1066a62772a7SAlan Somers zeros = calloc(1, len); 1067a62772a7SAlan Somers ASSERT_NE(nullptr, zeros); 1068a62772a7SAlan Somers 1069a62772a7SAlan Somers expect_lookup(RELPATH, ino, len); 1070a62772a7SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 1071a62772a7SAlan Somers expect_read(ino, 0, len, len, zeros); 1072a62772a7SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 1073a62772a7SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, zeros); 1074a62772a7SAlan Somers expect_release(ino, ReturnErrno(0)); 1075a62772a7SAlan Somers 1076a62772a7SAlan Somers fd = open(FULLPATH, O_RDWR); 1077d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1078a62772a7SAlan Somers 1079a62772a7SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 1080a62772a7SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno); 1081a62772a7SAlan Somers 1082a62772a7SAlan Somers memmove((uint8_t*)p, CONTENTS, bufsize); 1083a62772a7SAlan Somers 1084a62772a7SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 1085a62772a7SAlan Somers close(fd); // Write mmap'd data on close 1086a62772a7SAlan Somers 1087a62772a7SAlan Somers free(zeros); 1088a62772a7SAlan Somers } 1089a62772a7SAlan Somers 10909821f1d3SAlan Somers /* 109184879e46SAlan Somers * When mounted with -o async, the writeback cache mode should delay writes 109284879e46SAlan Somers */ 109384879e46SAlan Somers TEST_F(WriteBackAsync, delay) 109484879e46SAlan Somers { 109584879e46SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 109684879e46SAlan Somers const char RELPATH[] = "some_file.txt"; 109784879e46SAlan Somers const char *CONTENTS = "abcdefgh"; 109884879e46SAlan Somers uint64_t ino = 42; 109984879e46SAlan Somers int fd; 110084879e46SAlan Somers ssize_t bufsize = strlen(CONTENTS); 110184879e46SAlan Somers 110284879e46SAlan Somers expect_lookup(RELPATH, ino, 0); 110384879e46SAlan Somers expect_open(ino, 0, 1); 110484879e46SAlan Somers /* Write should be cached, but FUSE_WRITE shouldn't be sent */ 110584879e46SAlan Somers EXPECT_CALL(*m_mock, process( 110684879e46SAlan Somers ResultOf([=](auto in) { 110784879e46SAlan Somers return (in.header.opcode == FUSE_WRITE); 110884879e46SAlan Somers }, Eq(true)), 110984879e46SAlan Somers _) 111084879e46SAlan Somers ).Times(0); 111184879e46SAlan Somers 111284879e46SAlan Somers fd = open(FULLPATH, O_RDWR); 1113d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 111484879e46SAlan Somers 111584879e46SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 111684879e46SAlan Somers 111784879e46SAlan Somers /* Don't close the file because that would flush the cache */ 11188e765737SAlan Somers leak(fd); 111984879e46SAlan Somers } 112084879e46SAlan Somers 112184879e46SAlan Somers /* 1122669a092aSAlan Somers * A direct write should not evict dirty cached data from outside of its own 1123669a092aSAlan Somers * byte range. 1124669a092aSAlan Somers */ 1125669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached) 1126669a092aSAlan Somers { 1127669a092aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1128669a092aSAlan Somers const char RELPATH[] = "some_file.txt"; 1129669a092aSAlan Somers const char CONTENTS0[] = "abcdefgh"; 1130669a092aSAlan Somers const char CONTENTS1[] = "ijklmnop"; 1131669a092aSAlan Somers uint64_t ino = 42; 1132669a092aSAlan Somers int fd; 1133669a092aSAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1; 1134669a092aSAlan Somers ssize_t fsize = 2 * m_maxbcachebuf; 1135669a092aSAlan Somers char readbuf[bufsize]; 1136669a092aSAlan Somers void *zeros; 1137669a092aSAlan Somers 1138669a092aSAlan Somers zeros = calloc(1, m_maxbcachebuf); 1139669a092aSAlan Somers ASSERT_NE(nullptr, zeros); 1140669a092aSAlan Somers 1141669a092aSAlan Somers expect_lookup(RELPATH, ino, fsize); 1142669a092aSAlan Somers expect_open(ino, 0, 1); 1143669a092aSAlan Somers expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros); 1144669a092aSAlan Somers FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0, 1145669a092aSAlan Somers CONTENTS1); 1146669a092aSAlan Somers 1147669a092aSAlan Somers fd = open(FULLPATH, O_RDWR); 1148d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1149669a092aSAlan Somers 1150669a092aSAlan Somers // Cache first block with dirty data. This will entail first reading 1151669a092aSAlan Somers // the existing data. 1152669a092aSAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0)) 1153669a092aSAlan Somers << strerror(errno); 1154669a092aSAlan Somers 1155669a092aSAlan Somers // Write directly to second block 1156669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 1157669a092aSAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf)) 1158669a092aSAlan Somers << strerror(errno); 1159669a092aSAlan Somers 1160669a092aSAlan Somers // Read from the first block again. Should be serviced by cache. 1161669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 1162669a092aSAlan Somers ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno); 1163669a092aSAlan Somers ASSERT_STREQ(readbuf, CONTENTS0); 1164669a092aSAlan Somers 1165669a092aSAlan Somers leak(fd); 1166669a092aSAlan Somers free(zeros); 1167669a092aSAlan Somers } 1168669a092aSAlan Somers 1169669a092aSAlan Somers /* 1170669a092aSAlan Somers * If a direct io write partially overlaps one or two blocks of dirty cached 1171669a092aSAlan Somers * data, No dirty data should be lost. Admittedly this is a weird test, 1172669a092aSAlan Somers * because it would be unusual to use O_DIRECT and the writeback cache. 1173669a092aSAlan Somers */ 1174669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block) 1175669a092aSAlan Somers { 1176669a092aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1177669a092aSAlan Somers const char RELPATH[] = "some_file.txt"; 1178669a092aSAlan Somers uint64_t ino = 42; 1179669a092aSAlan Somers int fd; 1180669a092aSAlan Somers off_t bs = m_maxbcachebuf; 1181669a092aSAlan Somers ssize_t fsize = 3 * bs; 1182669a092aSAlan Somers void *readbuf, *zeros, *ones, *zeroones, *onezeros; 1183669a092aSAlan Somers 1184669a092aSAlan Somers readbuf = malloc(bs); 1185669a092aSAlan Somers ASSERT_NE(nullptr, readbuf) << strerror(errno); 1186669a092aSAlan Somers zeros = calloc(1, 3 * bs); 1187669a092aSAlan Somers ASSERT_NE(nullptr, zeros); 1188669a092aSAlan Somers ones = calloc(1, 2 * bs); 1189669a092aSAlan Somers ASSERT_NE(nullptr, ones); 1190669a092aSAlan Somers memset(ones, 1, 2 * bs); 1191669a092aSAlan Somers zeroones = calloc(1, bs); 1192669a092aSAlan Somers ASSERT_NE(nullptr, zeroones); 1193669a092aSAlan Somers memset((uint8_t*)zeroones + bs / 2, 1, bs / 2); 1194669a092aSAlan Somers onezeros = calloc(1, bs); 1195669a092aSAlan Somers ASSERT_NE(nullptr, onezeros); 1196669a092aSAlan Somers memset(onezeros, 1, bs / 2); 1197669a092aSAlan Somers 1198669a092aSAlan Somers expect_lookup(RELPATH, ino, fsize); 1199669a092aSAlan Somers expect_open(ino, 0, 1); 1200669a092aSAlan Somers 1201669a092aSAlan Somers fd = open(FULLPATH, O_RDWR); 1202d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1203669a092aSAlan Somers 1204669a092aSAlan Somers /* Cache first and third blocks with dirty data. */ 1205669a092aSAlan Somers ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno); 1206669a092aSAlan Somers 1207669a092aSAlan Somers /* 1208669a092aSAlan Somers * Write directly to all three blocks. The partially written blocks 1209669a092aSAlan Somers * will be flushed because they're dirty. 1210669a092aSAlan Somers */ 1211669a092aSAlan Somers FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros); 1212669a092aSAlan Somers FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros); 1213669a092aSAlan Somers /* The direct write is split in two because of the m_maxwrite value */ 1214669a092aSAlan Somers FuseTest::expect_write(ino, bs / 2, bs, bs, 0, 0, ones); 1215669a092aSAlan Somers FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones); 1216669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 1217669a092aSAlan Somers ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno); 1218669a092aSAlan Somers 1219669a092aSAlan Somers /* 1220669a092aSAlan Somers * Read from both the valid and invalid portions of the first and third 1221669a092aSAlan Somers * blocks again. This will entail FUSE_READ operations because these 1222669a092aSAlan Somers * blocks were invalidated by the direct write. 1223669a092aSAlan Somers */ 1224669a092aSAlan Somers expect_read(ino, 0, bs, bs, zeroones); 1225669a092aSAlan Somers expect_read(ino, 2 * bs, bs, bs, onezeros); 1226669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 1227669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno); 1228669a092aSAlan Somers EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2)); 1229669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2)) 1230669a092aSAlan Somers << strerror(errno); 1231669a092aSAlan Somers EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2)); 1232669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2)) 1233669a092aSAlan Somers << strerror(errno); 1234669a092aSAlan Somers EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2)); 1235669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs)) 1236669a092aSAlan Somers << strerror(errno); 1237669a092aSAlan Somers EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2)); 1238669a092aSAlan Somers 1239669a092aSAlan Somers leak(fd); 1240669a092aSAlan Somers free(zeroones); 1241669a092aSAlan Somers free(onezeros); 1242669a092aSAlan Somers free(ones); 1243669a092aSAlan Somers free(zeros); 1244669a092aSAlan Somers free(readbuf); 1245669a092aSAlan Somers } 1246669a092aSAlan Somers 1247669a092aSAlan Somers /* 1248aef22f2dSAlan Somers * In WriteBack mode, writes may be cached beyond what the server thinks is the 1249aef22f2dSAlan Somers * EOF. In this case, a short read at EOF should _not_ cause fusefs to update 1250aef22f2dSAlan Somers * the file's size. 1251aef22f2dSAlan Somers */ 1252aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof) 1253aef22f2dSAlan Somers { 1254aef22f2dSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1255aef22f2dSAlan Somers const char RELPATH[] = "some_file.txt"; 1256aef22f2dSAlan Somers const char *CONTENTS0 = "abcdefgh"; 1257aef22f2dSAlan Somers const char *CONTENTS1 = "ijklmnop"; 1258aef22f2dSAlan Somers uint64_t ino = 42; 1259aef22f2dSAlan Somers int fd; 1260aef22f2dSAlan Somers off_t offset = m_maxbcachebuf; 1261aef22f2dSAlan Somers ssize_t wbufsize = strlen(CONTENTS1); 1262aef22f2dSAlan Somers off_t old_filesize = (off_t)strlen(CONTENTS0); 1263aef22f2dSAlan Somers ssize_t rbufsize = 2 * old_filesize; 1264aef22f2dSAlan Somers char readbuf[rbufsize]; 1265aef22f2dSAlan Somers size_t holesize = rbufsize - old_filesize; 1266aef22f2dSAlan Somers char hole[holesize]; 1267aef22f2dSAlan Somers struct stat sb; 1268aef22f2dSAlan Somers ssize_t r; 1269aef22f2dSAlan Somers 1270aef22f2dSAlan Somers expect_lookup(RELPATH, ino, 0); 1271aef22f2dSAlan Somers expect_open(ino, 0, 1); 1272aef22f2dSAlan Somers expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0); 1273aef22f2dSAlan Somers 1274aef22f2dSAlan Somers fd = open(FULLPATH, O_RDWR); 1275d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1276aef22f2dSAlan Somers 1277aef22f2dSAlan Somers /* Write and cache data beyond EOF */ 1278aef22f2dSAlan Somers ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset)) 1279aef22f2dSAlan Somers << strerror(errno); 1280aef22f2dSAlan Somers 1281aef22f2dSAlan Somers /* Read from the old EOF */ 1282aef22f2dSAlan Somers r = pread(fd, readbuf, rbufsize, 0); 1283aef22f2dSAlan Somers ASSERT_LE(0, r) << strerror(errno); 1284aef22f2dSAlan Somers EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole"; 1285aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize)); 1286aef22f2dSAlan Somers bzero(hole, holesize); 1287aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize)); 1288aef22f2dSAlan Somers 1289aef22f2dSAlan Somers /* The file's size should still be what was established by pwrite */ 1290aef22f2dSAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1291aef22f2dSAlan Somers EXPECT_EQ(offset + wbufsize, sb.st_size); 12927fc0921dSAlan Somers leak(fd); 1293aef22f2dSAlan Somers } 1294aef22f2dSAlan Somers 1295aef22f2dSAlan Somers /* 1296788af953SAlan Somers * When a file has dirty writes that haven't been flushed, the server's notion 1297788af953SAlan Somers * of its mtime and ctime will be wrong. The kernel should ignore those if it 1298788af953SAlan Somers * gets them from a FUSE_GETATTR before flushing. 1299788af953SAlan Somers */ 1300788af953SAlan Somers TEST_F(WriteBackAsync, timestamps) 1301788af953SAlan Somers { 1302788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1303788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 1304788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 1305788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1306788af953SAlan Somers uint64_t ino = 42; 1307788af953SAlan Somers uint64_t attr_valid = 0; 1308788af953SAlan Somers uint64_t attr_valid_nsec = 0; 1309788af953SAlan Somers uint64_t server_time = 12345; 1310788af953SAlan Somers mode_t mode = S_IFREG | 0644; 1311788af953SAlan Somers int fd; 1312788af953SAlan Somers 1313788af953SAlan Somers struct stat sb; 1314788af953SAlan Somers 1315788af953SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 1316788af953SAlan Somers .WillRepeatedly(Invoke( 1317788af953SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 1318788af953SAlan Somers SET_OUT_HEADER_LEN(out, entry); 1319788af953SAlan Somers out.body.entry.attr.mode = mode; 1320788af953SAlan Somers out.body.entry.nodeid = ino; 1321788af953SAlan Somers out.body.entry.attr.nlink = 1; 1322788af953SAlan Somers out.body.entry.attr_valid = attr_valid; 1323788af953SAlan Somers out.body.entry.attr_valid_nsec = attr_valid_nsec; 1324788af953SAlan Somers }))); 1325788af953SAlan Somers expect_open(ino, 0, 1); 1326788af953SAlan Somers EXPECT_CALL(*m_mock, process( 1327788af953SAlan Somers ResultOf([=](auto in) { 1328788af953SAlan Somers return (in.header.opcode == FUSE_GETATTR && 1329788af953SAlan Somers in.header.nodeid == ino); 1330788af953SAlan Somers }, Eq(true)), 1331788af953SAlan Somers _) 1332788af953SAlan Somers ).WillRepeatedly(Invoke( 1333788af953SAlan Somers ReturnImmediate([=](auto i __unused, auto& out) { 1334788af953SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1335788af953SAlan Somers out.body.attr.attr.ino = ino; 1336788af953SAlan Somers out.body.attr.attr.mode = mode; 1337788af953SAlan Somers out.body.attr.attr_valid = attr_valid; 1338788af953SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec; 1339788af953SAlan Somers out.body.attr.attr.atime = server_time; 1340788af953SAlan Somers out.body.attr.attr.mtime = server_time; 1341788af953SAlan Somers out.body.attr.attr.ctime = server_time; 1342788af953SAlan Somers }))); 1343788af953SAlan Somers 1344788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 1345d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1346788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1347788af953SAlan Somers 1348788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1349788af953SAlan Somers EXPECT_EQ((time_t)server_time, sb.st_atime); 1350788af953SAlan Somers EXPECT_NE((time_t)server_time, sb.st_mtime); 1351788af953SAlan Somers EXPECT_NE((time_t)server_time, sb.st_ctime); 13528e765737SAlan Somers 13538e765737SAlan Somers leak(fd); 1354788af953SAlan Somers } 1355788af953SAlan Somers 1356788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */ 1357788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr) 1358788af953SAlan Somers { 1359788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1360788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 1361788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 1362788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1363788af953SAlan Somers uint64_t ino = 42; 1364788af953SAlan Somers const mode_t newmode = 0755; 1365788af953SAlan Somers int fd; 1366788af953SAlan Somers 1367788af953SAlan Somers expect_lookup(RELPATH, ino, 0); 1368788af953SAlan Somers expect_open(ino, 0, 1); 1369788af953SAlan Somers EXPECT_CALL(*m_mock, process( 1370788af953SAlan Somers ResultOf([=](auto in) { 13710a8fe2d3SAlan Somers uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME; 1372788af953SAlan Somers return (in.header.opcode == FUSE_SETATTR && 1373788af953SAlan Somers in.header.nodeid == ino && 1374788af953SAlan Somers in.body.setattr.valid == valid); 1375788af953SAlan Somers }, Eq(true)), 1376788af953SAlan Somers _) 1377788af953SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1378788af953SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1379788af953SAlan Somers out.body.attr.attr.ino = ino; 1380788af953SAlan Somers out.body.attr.attr.mode = S_IFREG | newmode; 1381788af953SAlan Somers }))); 1382788af953SAlan Somers 1383788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 1384d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1385788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1386788af953SAlan Somers ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 13878e765737SAlan Somers 13888e765737SAlan Somers leak(fd); 1389788af953SAlan Somers } 1390788af953SAlan Somers 1391fef46454SAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */ 1392fef46454SAlan Somers TEST_P(TimeGran, timestamps_during_setattr) 1393fef46454SAlan Somers { 1394fef46454SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1395fef46454SAlan Somers const char RELPATH[] = "some_file.txt"; 1396fef46454SAlan Somers const char *CONTENTS = "abcdefgh"; 1397fef46454SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1398fef46454SAlan Somers uint64_t ino = 42; 1399fef46454SAlan Somers const mode_t newmode = 0755; 1400fef46454SAlan Somers int fd; 1401fef46454SAlan Somers 1402fef46454SAlan Somers expect_lookup(RELPATH, ino, 0); 1403fef46454SAlan Somers expect_open(ino, 0, 1); 1404fef46454SAlan Somers EXPECT_CALL(*m_mock, process( 1405fef46454SAlan Somers ResultOf([=](auto in) { 1406fef46454SAlan Somers uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME; 1407fef46454SAlan Somers return (in.header.opcode == FUSE_SETATTR && 1408fef46454SAlan Somers in.header.nodeid == ino && 1409fef46454SAlan Somers in.body.setattr.valid == valid && 1410fef46454SAlan Somers in.body.setattr.mtimensec % m_time_gran == 0 && 1411fef46454SAlan Somers in.body.setattr.ctimensec % m_time_gran == 0); 1412fef46454SAlan Somers }, Eq(true)), 1413fef46454SAlan Somers _) 1414fef46454SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1415fef46454SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1416fef46454SAlan Somers out.body.attr.attr.ino = ino; 1417fef46454SAlan Somers out.body.attr.attr.mode = S_IFREG | newmode; 1418fef46454SAlan Somers }))); 1419fef46454SAlan Somers 1420fef46454SAlan Somers fd = open(FULLPATH, O_RDWR); 1421d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1422fef46454SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1423fef46454SAlan Somers ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 14248e765737SAlan Somers 14258e765737SAlan Somers leak(fd); 1426fef46454SAlan Somers } 1427fef46454SAlan Somers 1428fef46454SAlan Somers INSTANTIATE_TEST_CASE_P(RA, TimeGran, Range(0u, 10u)); 1429fef46454SAlan Somers 1430788af953SAlan Somers /* 14319821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 14329821f1d3SAlan Somers */ 1433f8ebf1cdSAlan Somers TEST_F(Write, writethrough) 14349821f1d3SAlan Somers { 14359821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 14369821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 14379821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 14389821f1d3SAlan Somers uint64_t ino = 42; 14399821f1d3SAlan Somers int fd; 14409821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 14418e765737SAlan Somers uint8_t readbuf[bufsize]; 14429821f1d3SAlan Somers 14439821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 14449821f1d3SAlan Somers expect_open(ino, 0, 1); 1445bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 14469821f1d3SAlan Somers 14479821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 1448d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 14499821f1d3SAlan Somers 14509821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 14519821f1d3SAlan Somers /* 14529821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 14539821f1d3SAlan Somers * filesystem daemon 14549821f1d3SAlan Somers */ 1455b5aaf286SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 14569821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 14577fc0921dSAlan Somers leak(fd); 14589821f1d3SAlan Somers } 14599821f1d3SAlan Somers 14600d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */ 14610d3a88d7SAlan Somers TEST_F(Write, update_file_size) 14629821f1d3SAlan Somers { 14639821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 14649821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 14659821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 14669821f1d3SAlan Somers struct stat sb; 14679821f1d3SAlan Somers uint64_t ino = 42; 14689821f1d3SAlan Somers int fd; 14699821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 14709821f1d3SAlan Somers 14719821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 14729821f1d3SAlan Somers expect_open(ino, 0, 1); 1473bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 14749821f1d3SAlan Somers 14759821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 1476d2621689SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 14779821f1d3SAlan Somers 14789821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 14799821f1d3SAlan Somers /* Get cached attributes */ 14809821f1d3SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 14819821f1d3SAlan Somers ASSERT_EQ(bufsize, sb.st_size); 14827fc0921dSAlan Somers leak(fd); 14839821f1d3SAlan Somers } 1484