19821f1d3SAlan Somers /*- 29821f1d3SAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39821f1d3SAlan Somers * 49821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 59821f1d3SAlan Somers * 69821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 79821f1d3SAlan Somers * from the FreeBSD Foundation. 89821f1d3SAlan Somers * 99821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without 109821f1d3SAlan Somers * modification, are permitted provided that the following conditions 119821f1d3SAlan Somers * are met: 129821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright 139821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer. 149821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 159821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the 169821f1d3SAlan Somers * documentation and/or other materials provided with the distribution. 179821f1d3SAlan Somers * 189821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 199821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 209821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 219821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 229821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 239821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 249821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 259821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 269821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 279821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 289821f1d3SAlan Somers * SUCH DAMAGE. 299821f1d3SAlan Somers */ 309821f1d3SAlan Somers 319821f1d3SAlan Somers extern "C" { 328eecd9ceSAlan Somers #include <sys/param.h> 339821f1d3SAlan Somers #include <sys/mman.h> 34a639731bSAlan Somers #include <sys/resource.h> 359821f1d3SAlan Somers #include <sys/stat.h> 36a639731bSAlan Somers #include <sys/time.h> 379821f1d3SAlan Somers #include <sys/uio.h> 389821f1d3SAlan Somers 399821f1d3SAlan Somers #include <aio.h> 409821f1d3SAlan Somers #include <fcntl.h> 41a639731bSAlan Somers #include <signal.h> 429821f1d3SAlan Somers #include <unistd.h> 439821f1d3SAlan Somers } 449821f1d3SAlan Somers 459821f1d3SAlan Somers #include "mockfs.hh" 469821f1d3SAlan Somers #include "utils.hh" 479821f1d3SAlan Somers 489821f1d3SAlan Somers using namespace testing; 499821f1d3SAlan Somers 509821f1d3SAlan Somers class Write: public FuseTest { 519821f1d3SAlan Somers 529821f1d3SAlan Somers public: 53a639731bSAlan Somers static sig_atomic_t s_sigxfsz; 54a639731bSAlan Somers 55a639731bSAlan Somers void SetUp() { 56a639731bSAlan Somers s_sigxfsz = 0; 57a639731bSAlan Somers FuseTest::SetUp(); 58a639731bSAlan Somers } 59a639731bSAlan Somers 60a639731bSAlan Somers void TearDown() { 61a639731bSAlan Somers struct sigaction sa; 62a639731bSAlan Somers 63a639731bSAlan Somers bzero(&sa, sizeof(sa)); 64a639731bSAlan Somers sa.sa_handler = SIG_DFL; 65a639731bSAlan Somers sigaction(SIGXFSZ, &sa, NULL); 66a639731bSAlan Somers 67a639731bSAlan Somers FuseTest::TearDown(); 68a639731bSAlan Somers } 699821f1d3SAlan Somers 709821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 719821f1d3SAlan Somers { 729821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); 739821f1d3SAlan Somers } 749821f1d3SAlan Somers 759821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r) 769821f1d3SAlan Somers { 779821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 789821f1d3SAlan Somers ResultOf([=](auto in) { 7929edc611SAlan Somers return (in.header.opcode == FUSE_RELEASE && 8029edc611SAlan Somers in.header.nodeid == ino); 819821f1d3SAlan Somers }, Eq(true)), 829821f1d3SAlan Somers _) 839821f1d3SAlan Somers ).WillRepeatedly(Invoke(r)); 849821f1d3SAlan Somers } 859821f1d3SAlan Somers 86bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 87bda39894SAlan Somers uint64_t osize, const void *contents) 88bda39894SAlan Somers { 89bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents); 90bda39894SAlan Somers } 91bda39894SAlan Somers 9284879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */ 9384879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size, 9484879e46SAlan Somers const void *contents) 9584879e46SAlan Somers { 9684879e46SAlan Somers EXPECT_CALL(*m_mock, process( 9784879e46SAlan Somers ResultOf([=](auto in) { 9884879e46SAlan Somers const char *buf = (const char*)in.body.bytes + 9984879e46SAlan Somers sizeof(struct fuse_write_in); 10084879e46SAlan Somers 10184879e46SAlan Somers return (in.header.opcode == FUSE_WRITE && 10284879e46SAlan Somers in.header.nodeid == ino && 10384879e46SAlan Somers in.body.write.offset == offset && 10484879e46SAlan Somers in.body.write.size == size && 10584879e46SAlan Somers 0 == bcmp(buf, contents, size)); 10684879e46SAlan Somers }, Eq(true)), 10784879e46SAlan Somers _) 10884879e46SAlan Somers ).Times(AtMost(1)) 10984879e46SAlan Somers .WillRepeatedly(Invoke( 11084879e46SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 11184879e46SAlan Somers SET_OUT_HEADER_LEN(out, write); 11284879e46SAlan Somers out.body.write.size = size; 11384879e46SAlan Somers }) 11484879e46SAlan Somers )); 11584879e46SAlan Somers } 11684879e46SAlan Somers 1179821f1d3SAlan Somers }; 1189821f1d3SAlan Somers 119a639731bSAlan Somers sig_atomic_t Write::s_sigxfsz = 0; 120a639731bSAlan Somers 12116bd2d47SAlan Somers class Write_7_8: public FuseTest { 12216bd2d47SAlan Somers 12316bd2d47SAlan Somers public: 12416bd2d47SAlan Somers virtual void SetUp() { 12516bd2d47SAlan Somers m_kernel_minor_version = 8; 12616bd2d47SAlan Somers FuseTest::SetUp(); 12716bd2d47SAlan Somers } 12816bd2d47SAlan Somers 12916bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 13016bd2d47SAlan Somers { 13116bd2d47SAlan Somers FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1); 13216bd2d47SAlan Somers } 13316bd2d47SAlan Somers 13416bd2d47SAlan Somers }; 13516bd2d47SAlan Somers 1369821f1d3SAlan Somers class AioWrite: public Write { 1379821f1d3SAlan Somers virtual void SetUp() { 138*c2265ae7SAlan Somers if (!is_unsafe_aio_enabled()) 1399821f1d3SAlan Somers GTEST_SKIP() << 1409821f1d3SAlan Somers "vfs.aio.enable_unsafe must be set for this test"; 141*c2265ae7SAlan Somers FuseTest::SetUp(); 1429821f1d3SAlan Somers } 1439821f1d3SAlan Somers }; 1449821f1d3SAlan Somers 1459821f1d3SAlan Somers /* Tests for the writeback cache mode */ 1469821f1d3SAlan Somers class WriteBack: public Write { 147bda39894SAlan Somers public: 1489821f1d3SAlan Somers virtual void SetUp() { 149f8ebf1cdSAlan Somers m_init_flags |= FUSE_WRITEBACK_CACHE; 1509821f1d3SAlan Somers FuseTest::SetUp(); 1519821f1d3SAlan Somers if (IsSkipped()) 1529821f1d3SAlan Somers return; 1539821f1d3SAlan Somers } 1549821f1d3SAlan Somers 155bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 156bda39894SAlan Somers uint64_t osize, const void *contents) 157bda39894SAlan Somers { 158bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0, 159bda39894SAlan Somers contents); 160bda39894SAlan Somers } 1619821f1d3SAlan Somers }; 1629821f1d3SAlan Somers 16384879e46SAlan Somers class WriteBackAsync: public WriteBack { 16484879e46SAlan Somers public: 16584879e46SAlan Somers virtual void SetUp() { 16684879e46SAlan Somers m_async = true; 16784879e46SAlan Somers WriteBack::SetUp(); 16884879e46SAlan Somers } 16984879e46SAlan Somers }; 17084879e46SAlan Somers 171fef46454SAlan Somers class TimeGran: public WriteBackAsync, public WithParamInterface<unsigned> { 172fef46454SAlan Somers public: 173fef46454SAlan Somers virtual void SetUp() { 174fef46454SAlan Somers m_time_gran = 1 << GetParam(); 175fef46454SAlan Somers WriteBackAsync::SetUp(); 176fef46454SAlan Somers } 177fef46454SAlan Somers }; 178fef46454SAlan Somers 1798eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */ 1808eecd9ceSAlan Somers class WriteCluster: public WriteBack { 1818eecd9ceSAlan Somers public: 1828eecd9ceSAlan Somers virtual void SetUp() { 1838eecd9ceSAlan Somers m_async = true; 184f2704f05SAlan Somers m_maxwrite = m_maxphys; 1858eecd9ceSAlan Somers WriteBack::SetUp(); 186f8ebf1cdSAlan Somers if (m_maxphys < 2 * DFLTPHYS) 187f8ebf1cdSAlan Somers GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS" 188f8ebf1cdSAlan Somers << " for this test"; 189f2704f05SAlan Somers if (m_maxphys < 2 * m_maxbcachebuf) 1906ca3b02bSAlan Somers GTEST_SKIP() << "MAXPHYS must be at least twice maxbcachebuf" 1916ca3b02bSAlan Somers << " for this test"; 1928eecd9ceSAlan Somers } 1938eecd9ceSAlan Somers }; 1948eecd9ceSAlan Somers 195a639731bSAlan Somers void sigxfsz_handler(int __unused sig) { 196a639731bSAlan Somers Write::s_sigxfsz = 1; 197a639731bSAlan Somers } 198a639731bSAlan Somers 1999821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */ 2009821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ 2019821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write) 2029821f1d3SAlan Somers { 2039821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2049821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2059821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 2069821f1d3SAlan Somers uint64_t ino = 42; 2079821f1d3SAlan Somers uint64_t offset = 4096; 2089821f1d3SAlan Somers int fd; 2099821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 2109821f1d3SAlan Somers struct aiocb iocb, *piocb; 2119821f1d3SAlan Somers 2129821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 2139821f1d3SAlan Somers expect_open(ino, 0, 1); 214bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS); 2159821f1d3SAlan Somers 2169821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 2179821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 2189821f1d3SAlan Somers 2199821f1d3SAlan Somers iocb.aio_nbytes = bufsize; 2209821f1d3SAlan Somers iocb.aio_fildes = fd; 2215a0b9a27SAlan Somers iocb.aio_buf = __DECONST(void *, CONTENTS); 2229821f1d3SAlan Somers iocb.aio_offset = offset; 2239821f1d3SAlan Somers iocb.aio_sigevent.sigev_notify = SIGEV_NONE; 2249821f1d3SAlan Somers ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno); 2259821f1d3SAlan Somers ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); 2267fc0921dSAlan Somers leak(fd); 2279821f1d3SAlan Somers } 2289821f1d3SAlan Somers 2299821f1d3SAlan Somers /* 2309821f1d3SAlan Somers * When a file is opened with O_APPEND, we should forward that flag to 2319821f1d3SAlan Somers * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the 2329821f1d3SAlan Somers * offset internally. That way we'll work both with filesystems that 2339821f1d3SAlan Somers * understand O_APPEND (and ignore the offset) and filesystems that don't (and 2349821f1d3SAlan Somers * simply use the offset). 2359821f1d3SAlan Somers * 2369821f1d3SAlan Somers * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the 2379821f1d3SAlan Somers * Open.o_append test. 2389821f1d3SAlan Somers */ 2399821f1d3SAlan Somers TEST_F(Write, append) 2409821f1d3SAlan Somers { 2419821f1d3SAlan Somers const ssize_t BUFSIZE = 9; 2429821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2439821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2449821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 2459821f1d3SAlan Somers uint64_t ino = 42; 2469821f1d3SAlan Somers /* 2479821f1d3SAlan Somers * Set offset to a maxbcachebuf boundary so we don't need to RMW when 2489821f1d3SAlan Somers * using writeback caching 2499821f1d3SAlan Somers */ 2509821f1d3SAlan Somers uint64_t initial_offset = m_maxbcachebuf; 2519821f1d3SAlan Somers int fd; 2529821f1d3SAlan Somers 2539821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset); 2549821f1d3SAlan Somers expect_open(ino, 0, 1); 255bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS); 2569821f1d3SAlan Somers 2579821f1d3SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ 2589821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND); 2599821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 2609821f1d3SAlan Somers 2619821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 2627fc0921dSAlan Somers leak(fd); 2639821f1d3SAlan Somers } 2649821f1d3SAlan Somers 265a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */ 266a87e0831SAlan Somers TEST_F(Write, append_to_cached) 267a87e0831SAlan Somers { 268a87e0831SAlan Somers const ssize_t BUFSIZE = 9; 269a87e0831SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 270a87e0831SAlan Somers const char RELPATH[] = "some_file.txt"; 271a87e0831SAlan Somers char *oldcontents, *oldbuf; 272a87e0831SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 273a87e0831SAlan Somers uint64_t ino = 42; 274a87e0831SAlan Somers /* 275a87e0831SAlan Somers * Set offset in between maxbcachebuf boundary to test buffer handling 276a87e0831SAlan Somers */ 277a87e0831SAlan Somers uint64_t oldsize = m_maxbcachebuf / 2; 278a87e0831SAlan Somers int fd; 279a87e0831SAlan Somers 280a87e0831SAlan Somers oldcontents = (char*)calloc(1, oldsize); 2815a0b9a27SAlan Somers ASSERT_NE(nullptr, oldcontents) << strerror(errno); 282a87e0831SAlan Somers oldbuf = (char*)malloc(oldsize); 2835a0b9a27SAlan Somers ASSERT_NE(nullptr, oldbuf) << strerror(errno); 284a87e0831SAlan Somers 285a87e0831SAlan Somers expect_lookup(RELPATH, ino, oldsize); 286a87e0831SAlan Somers expect_open(ino, 0, 1); 287a87e0831SAlan Somers expect_read(ino, 0, oldsize, oldsize, oldcontents); 28884879e46SAlan Somers maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS); 289a87e0831SAlan Somers 290a87e0831SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ 291a87e0831SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND); 292a87e0831SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 293a87e0831SAlan Somers 294a87e0831SAlan Somers /* Read the old data into the cache */ 295a87e0831SAlan Somers ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize)) 296a87e0831SAlan Somers << strerror(errno); 297a87e0831SAlan Somers 298a87e0831SAlan Somers /* Write the new data. There should be no more read operations */ 299a87e0831SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 3007fc0921dSAlan Somers leak(fd); 301a87e0831SAlan Somers } 302a87e0831SAlan Somers 3039821f1d3SAlan Somers TEST_F(Write, append_direct_io) 3049821f1d3SAlan Somers { 3059821f1d3SAlan Somers const ssize_t BUFSIZE = 9; 3069821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3079821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3089821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 3099821f1d3SAlan Somers uint64_t ino = 42; 3109821f1d3SAlan Somers uint64_t initial_offset = 4096; 3119821f1d3SAlan Somers int fd; 3129821f1d3SAlan Somers 3139821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset); 3149821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 315bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS); 3169821f1d3SAlan Somers 3179821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY | O_APPEND); 3189821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 3199821f1d3SAlan Somers 3209821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 3217fc0921dSAlan Somers leak(fd); 3229821f1d3SAlan Somers } 3239821f1d3SAlan Somers 3249821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */ 3256af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache) 3269821f1d3SAlan Somers { 3279821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3289821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3299821f1d3SAlan Somers const char CONTENTS0[] = "abcdefgh"; 3309821f1d3SAlan Somers const char CONTENTS1[] = "ijklmnop"; 3319821f1d3SAlan Somers uint64_t ino = 42; 3329821f1d3SAlan Somers int fd; 3339821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1; 3349821f1d3SAlan Somers char readbuf[bufsize]; 3359821f1d3SAlan Somers 3369821f1d3SAlan Somers expect_lookup(RELPATH, ino, bufsize); 3379821f1d3SAlan Somers expect_open(ino, 0, 1); 3389821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS0); 339bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS1); 3409821f1d3SAlan Somers 3419821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 3429821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 3439821f1d3SAlan Somers 3449821f1d3SAlan Somers // Prime cache 3459821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 3469821f1d3SAlan Somers 3479821f1d3SAlan Somers // Write directly, evicting cache 3489821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 3499821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 3509821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno); 3519821f1d3SAlan Somers 3529821f1d3SAlan Somers // Read again. Cache should be bypassed 3539821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS1); 3549821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 3559821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 3569821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 3579821f1d3SAlan Somers ASSERT_STREQ(readbuf, CONTENTS1); 3589821f1d3SAlan Somers 3597fc0921dSAlan Somers leak(fd); 3609821f1d3SAlan Somers } 3619821f1d3SAlan Somers 3629821f1d3SAlan Somers /* 36312292a99SAlan Somers * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not 36412292a99SAlan Somers * allowed to return a short write for that file handle. However, if it does 36512292a99SAlan Somers * then we should still do our darndest to handle it by resending the unwritten 36612292a99SAlan Somers * portion. 3679821f1d3SAlan Somers */ 36812292a99SAlan Somers TEST_F(Write, indirect_io_short_write) 3699821f1d3SAlan Somers { 3709821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3719821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3729821f1d3SAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 3739821f1d3SAlan Somers uint64_t ino = 42; 3749821f1d3SAlan Somers int fd; 3759821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 37612292a99SAlan Somers ssize_t bufsize0 = 11; 37712292a99SAlan Somers ssize_t bufsize1 = strlen(CONTENTS) - bufsize0; 37812292a99SAlan Somers const char *contents1 = CONTENTS + bufsize0; 3799821f1d3SAlan Somers 3809821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 38112292a99SAlan Somers expect_open(ino, 0, 1); 382bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize0, CONTENTS); 383bda39894SAlan Somers expect_write(ino, bufsize0, bufsize1, bufsize1, contents1); 3849821f1d3SAlan Somers 3859821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 3869821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 3879821f1d3SAlan Somers 3889821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 3897fc0921dSAlan Somers leak(fd); 3909821f1d3SAlan Somers } 3919821f1d3SAlan Somers 3929821f1d3SAlan Somers /* 39312292a99SAlan Somers * When the direct_io option is used, filesystems are allowed to write less 39412292a99SAlan Somers * data than requested. We should return the short write to userland. 39512292a99SAlan Somers */ 39612292a99SAlan Somers TEST_F(Write, direct_io_short_write) 39712292a99SAlan Somers { 39812292a99SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 39912292a99SAlan Somers const char RELPATH[] = "some_file.txt"; 40012292a99SAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 40112292a99SAlan Somers uint64_t ino = 42; 40212292a99SAlan Somers int fd; 40312292a99SAlan Somers ssize_t bufsize = strlen(CONTENTS); 40412292a99SAlan Somers ssize_t halfbufsize = bufsize / 2; 40512292a99SAlan Somers 40612292a99SAlan Somers expect_lookup(RELPATH, ino, 0); 40712292a99SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 408bda39894SAlan Somers expect_write(ino, 0, bufsize, halfbufsize, CONTENTS); 40912292a99SAlan Somers 41012292a99SAlan Somers fd = open(FULLPATH, O_WRONLY); 41112292a99SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 41212292a99SAlan Somers 41312292a99SAlan Somers ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 4147fc0921dSAlan Somers leak(fd); 41512292a99SAlan Somers } 41612292a99SAlan Somers 41712292a99SAlan Somers /* 4189821f1d3SAlan Somers * An insidious edge case: the filesystem returns a short write, and the 4199821f1d3SAlan Somers * difference between what we requested and what it actually wrote crosses an 4209821f1d3SAlan Somers * iov element boundary 4219821f1d3SAlan Somers */ 42212292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov) 4239821f1d3SAlan Somers { 4249821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4259821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 4269821f1d3SAlan Somers const char *CONTENTS0 = "abcdefgh"; 4279821f1d3SAlan Somers const char *CONTENTS1 = "ijklmnop"; 4289821f1d3SAlan Somers const char *EXPECTED0 = "abcdefghijklmnop"; 4299821f1d3SAlan Somers uint64_t ino = 42; 4309821f1d3SAlan Somers int fd; 4319821f1d3SAlan Somers ssize_t size0 = strlen(CONTENTS0) - 1; 4329821f1d3SAlan Somers ssize_t size1 = strlen(CONTENTS1) + 1; 4339821f1d3SAlan Somers ssize_t totalsize = size0 + size1; 4349821f1d3SAlan Somers struct iovec iov[2]; 4359821f1d3SAlan Somers 4369821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 4379821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 438bda39894SAlan Somers expect_write(ino, 0, totalsize, size0, EXPECTED0); 4399821f1d3SAlan Somers 4409821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 4419821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 4429821f1d3SAlan Somers 4435a0b9a27SAlan Somers iov[0].iov_base = __DECONST(void*, CONTENTS0); 4449821f1d3SAlan Somers iov[0].iov_len = strlen(CONTENTS0); 4455a0b9a27SAlan Somers iov[1].iov_base = __DECONST(void*, CONTENTS1); 4469821f1d3SAlan Somers iov[1].iov_len = strlen(CONTENTS1); 44712292a99SAlan Somers ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno); 4487fc0921dSAlan Somers leak(fd); 4499821f1d3SAlan Somers } 4509821f1d3SAlan Somers 451a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */ 452a639731bSAlan Somers TEST_F(Write, rlimit_fsize) 453a639731bSAlan Somers { 454a639731bSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 455a639731bSAlan Somers const char RELPATH[] = "some_file.txt"; 456a639731bSAlan Somers const char *CONTENTS = "abcdefgh"; 457a639731bSAlan Somers struct rlimit rl; 458a639731bSAlan Somers ssize_t bufsize = strlen(CONTENTS); 459a639731bSAlan Somers off_t offset = 1'000'000'000; 460a639731bSAlan Somers uint64_t ino = 42; 461a639731bSAlan Somers int fd; 462a639731bSAlan Somers 463a639731bSAlan Somers expect_lookup(RELPATH, ino, 0); 464a639731bSAlan Somers expect_open(ino, 0, 1); 465a639731bSAlan Somers 466a639731bSAlan Somers rl.rlim_cur = offset; 467a639731bSAlan Somers rl.rlim_max = 10 * offset; 468a639731bSAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno); 469a639731bSAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno); 470a639731bSAlan Somers 471a639731bSAlan Somers fd = open(FULLPATH, O_WRONLY); 472a639731bSAlan Somers 473a639731bSAlan Somers EXPECT_LE(0, fd) << strerror(errno); 474a639731bSAlan Somers 475a639731bSAlan Somers ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset)); 476a639731bSAlan Somers EXPECT_EQ(EFBIG, errno); 477a639731bSAlan Somers EXPECT_EQ(1, s_sigxfsz); 4787fc0921dSAlan Somers leak(fd); 479a639731bSAlan Somers } 480a639731bSAlan Somers 4819821f1d3SAlan Somers /* 482b9e20197SAlan Somers * A short read indicates EOF. Test that nothing bad happens if we get EOF 483b9e20197SAlan Somers * during the R of a RMW operation. 484b9e20197SAlan Somers */ 485f8ebf1cdSAlan Somers TEST_F(Write, eof_during_rmw) 486b9e20197SAlan Somers { 487b9e20197SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 488b9e20197SAlan Somers const char RELPATH[] = "some_file.txt"; 489b9e20197SAlan Somers const char *CONTENTS = "abcdefgh"; 490b9e20197SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 491b9e20197SAlan Somers uint64_t ino = 42; 492b9e20197SAlan Somers uint64_t offset = 1; 493b9e20197SAlan Somers ssize_t bufsize = strlen(CONTENTS); 494b9e20197SAlan Somers off_t orig_fsize = 10; 495b9e20197SAlan Somers off_t truncated_fsize = 5; 496b9e20197SAlan Somers off_t final_fsize = bufsize; 497b9e20197SAlan Somers int fd; 498b9e20197SAlan Somers 499b9e20197SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1); 500b9e20197SAlan Somers expect_open(ino, 0, 1); 501b9e20197SAlan Somers expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR); 502b9e20197SAlan Somers expect_getattr(ino, truncated_fsize); 503b9e20197SAlan Somers expect_read(ino, 0, final_fsize, final_fsize, INITIAL, O_RDWR); 504b9e20197SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS); 505b9e20197SAlan Somers 506b9e20197SAlan Somers fd = open(FULLPATH, O_RDWR); 507b9e20197SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 508b9e20197SAlan Somers 509b9e20197SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 510b9e20197SAlan Somers << strerror(errno); 5117fc0921dSAlan Somers leak(fd); 512b9e20197SAlan Somers } 513b9e20197SAlan Somers 514b9e20197SAlan Somers /* 5159821f1d3SAlan Somers * If the kernel cannot be sure which uid, gid, or pid was responsible for a 5169821f1d3SAlan Somers * write, then it must set the FUSE_WRITE_CACHE bit 5179821f1d3SAlan Somers */ 5189821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */ 519f8ebf1cdSAlan Somers TEST_F(Write, mmap) 5209821f1d3SAlan Somers { 5219821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 5229821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 5239821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 5249821f1d3SAlan Somers uint64_t ino = 42; 5259821f1d3SAlan Somers int fd; 5269821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 5279821f1d3SAlan Somers void *p; 5289821f1d3SAlan Somers uint64_t offset = 10; 5299821f1d3SAlan Somers size_t len; 5309821f1d3SAlan Somers void *zeros, *expected; 5319821f1d3SAlan Somers 5329821f1d3SAlan Somers len = getpagesize(); 5339821f1d3SAlan Somers 5349821f1d3SAlan Somers zeros = calloc(1, len); 5355a0b9a27SAlan Somers ASSERT_NE(nullptr, zeros); 5369821f1d3SAlan Somers expected = calloc(1, len); 5375a0b9a27SAlan Somers ASSERT_NE(nullptr, expected); 5389821f1d3SAlan Somers memmove((uint8_t*)expected + offset, CONTENTS, bufsize); 5399821f1d3SAlan Somers 5409821f1d3SAlan Somers expect_lookup(RELPATH, ino, len); 5419821f1d3SAlan Somers expect_open(ino, 0, 1); 5429821f1d3SAlan Somers expect_read(ino, 0, len, len, zeros); 5439821f1d3SAlan Somers /* 5449821f1d3SAlan Somers * Writes from the pager may or may not be associated with the correct 545cf437e2aSAlan Somers * pid, so they must set FUSE_WRITE_CACHE. 5469821f1d3SAlan Somers */ 547bda39894SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected); 5489f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 5499821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 5509821f1d3SAlan Somers 5519821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 5529821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 5539821f1d3SAlan Somers 5549821f1d3SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 5559821f1d3SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno); 5569821f1d3SAlan Somers 5579821f1d3SAlan Somers memmove((uint8_t*)p + offset, CONTENTS, bufsize); 5589821f1d3SAlan Somers 5599821f1d3SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 5609821f1d3SAlan Somers close(fd); // Write mmap'd data on close 5619821f1d3SAlan Somers 5629821f1d3SAlan Somers free(expected); 5639821f1d3SAlan Somers free(zeros); 5649821f1d3SAlan Somers } 5659821f1d3SAlan Somers 566f8ebf1cdSAlan Somers TEST_F(Write, pwrite) 5679821f1d3SAlan Somers { 5689821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 5699821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 5709821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 5719821f1d3SAlan Somers uint64_t ino = 42; 5726ca3b02bSAlan Somers uint64_t offset = m_maxbcachebuf; 5739821f1d3SAlan Somers int fd; 5749821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 5759821f1d3SAlan Somers 5769821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 5779821f1d3SAlan Somers expect_open(ino, 0, 1); 578bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS); 5799821f1d3SAlan Somers 5809821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 5819821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 5829821f1d3SAlan Somers 5839821f1d3SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 5849821f1d3SAlan Somers << strerror(errno); 5857fc0921dSAlan Somers leak(fd); 5869821f1d3SAlan Somers } 5879821f1d3SAlan Somers 588788af953SAlan Somers /* Writing a file should update its cached mtime and ctime */ 589788af953SAlan Somers TEST_F(Write, timestamps) 590788af953SAlan Somers { 591788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 592788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 593788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 594788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 595788af953SAlan Somers uint64_t ino = 42; 596788af953SAlan Somers struct stat sb0, sb1; 597788af953SAlan Somers int fd; 598788af953SAlan Somers 599788af953SAlan Somers expect_lookup(RELPATH, ino, 0); 600788af953SAlan Somers expect_open(ino, 0, 1); 601788af953SAlan Somers maybe_expect_write(ino, 0, bufsize, CONTENTS); 602788af953SAlan Somers 603788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 604788af953SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 605788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb0)) << strerror(errno); 606788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 607788af953SAlan Somers 608788af953SAlan Somers nap(); 609788af953SAlan Somers 610788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb1)) << strerror(errno); 611788af953SAlan Somers 612788af953SAlan Somers EXPECT_EQ(sb0.st_atime, sb1.st_atime); 613788af953SAlan Somers EXPECT_NE(sb0.st_mtime, sb1.st_mtime); 614788af953SAlan Somers EXPECT_NE(sb0.st_ctime, sb1.st_ctime); 615788af953SAlan Somers } 616788af953SAlan Somers 6179821f1d3SAlan Somers TEST_F(Write, write) 6189821f1d3SAlan Somers { 6199821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6209821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6219821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 6229821f1d3SAlan Somers uint64_t ino = 42; 6239821f1d3SAlan Somers int fd; 6249821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 6259821f1d3SAlan Somers 6269821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6279821f1d3SAlan Somers expect_open(ino, 0, 1); 628bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 6299821f1d3SAlan Somers 6309821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 6319821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6329821f1d3SAlan Somers 6339821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 6347fc0921dSAlan Somers leak(fd); 6359821f1d3SAlan Somers } 6369821f1d3SAlan Somers 6379821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */ 6389821f1d3SAlan Somers TEST_F(Write, write_large) 6399821f1d3SAlan Somers { 6409821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6419821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6429821f1d3SAlan Somers int *contents; 6439821f1d3SAlan Somers uint64_t ino = 42; 6449821f1d3SAlan Somers int fd; 6459821f1d3SAlan Somers ssize_t halfbufsize, bufsize; 6469821f1d3SAlan Somers 6478eecd9ceSAlan Somers halfbufsize = m_mock->m_maxwrite; 6489821f1d3SAlan Somers bufsize = halfbufsize * 2; 6499821f1d3SAlan Somers contents = (int*)malloc(bufsize); 6505a0b9a27SAlan Somers ASSERT_NE(nullptr, contents); 6519821f1d3SAlan Somers for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) { 6529821f1d3SAlan Somers contents[i] = i; 6539821f1d3SAlan Somers } 6549821f1d3SAlan Somers 6559821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6569821f1d3SAlan Somers expect_open(ino, 0, 1); 657f2704f05SAlan Somers maybe_expect_write(ino, 0, halfbufsize, contents); 65884879e46SAlan Somers maybe_expect_write(ino, halfbufsize, halfbufsize, 6599821f1d3SAlan Somers &contents[halfbufsize / sizeof(int)]); 6609821f1d3SAlan Somers 6619821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 6629821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6639821f1d3SAlan Somers 6649821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno); 6657fc0921dSAlan Somers leak(fd); 6669821f1d3SAlan Somers 6679821f1d3SAlan Somers free(contents); 6689821f1d3SAlan Somers } 6699821f1d3SAlan Somers 6709821f1d3SAlan Somers TEST_F(Write, write_nothing) 6719821f1d3SAlan Somers { 6729821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6739821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6749821f1d3SAlan Somers const char *CONTENTS = ""; 6759821f1d3SAlan Somers uint64_t ino = 42; 6769821f1d3SAlan Somers int fd; 6779821f1d3SAlan Somers ssize_t bufsize = 0; 6789821f1d3SAlan Somers 6799821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6809821f1d3SAlan Somers expect_open(ino, 0, 1); 6819821f1d3SAlan Somers 6829821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 6839821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6849821f1d3SAlan Somers 6859821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 6867fc0921dSAlan Somers leak(fd); 6879821f1d3SAlan Somers } 6889821f1d3SAlan Somers 68916bd2d47SAlan Somers TEST_F(Write_7_8, write) 69016bd2d47SAlan Somers { 69116bd2d47SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 69216bd2d47SAlan Somers const char RELPATH[] = "some_file.txt"; 69316bd2d47SAlan Somers const char *CONTENTS = "abcdefgh"; 69416bd2d47SAlan Somers uint64_t ino = 42; 69516bd2d47SAlan Somers int fd; 69616bd2d47SAlan Somers ssize_t bufsize = strlen(CONTENTS); 69716bd2d47SAlan Somers 69816bd2d47SAlan Somers expect_lookup(RELPATH, ino, 0); 69916bd2d47SAlan Somers expect_open(ino, 0, 1); 700bda39894SAlan Somers expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS); 70116bd2d47SAlan Somers 70216bd2d47SAlan Somers fd = open(FULLPATH, O_WRONLY); 70316bd2d47SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 70416bd2d47SAlan Somers 70516bd2d47SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 7067fc0921dSAlan Somers leak(fd); 70716bd2d47SAlan Somers } 70816bd2d47SAlan Somers 7099821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */ 71084879e46SAlan Somers TEST_F(WriteBackAsync, close) 7119821f1d3SAlan Somers { 7129821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7139821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7149821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 7159821f1d3SAlan Somers uint64_t ino = 42; 7169821f1d3SAlan Somers int fd; 7179821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 7189821f1d3SAlan Somers 7199821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 7209821f1d3SAlan Somers expect_open(ino, 0, 1); 721bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 7229821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 7239821f1d3SAlan Somers ResultOf([=](auto in) { 72429edc611SAlan Somers return (in.header.opcode == FUSE_SETATTR); 7259821f1d3SAlan Somers }, Eq(true)), 7269821f1d3SAlan Somers _) 72729edc611SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 7289821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, attr); 72929edc611SAlan Somers out.body.attr.attr.ino = ino; // Must match nodeid 7309821f1d3SAlan Somers }))); 7319f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 7329821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 7339821f1d3SAlan Somers 7349821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 7359821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7369821f1d3SAlan Somers 7379821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 7389821f1d3SAlan Somers close(fd); 7399821f1d3SAlan Somers } 7409821f1d3SAlan Somers 7418eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */ 7428eecd9ceSAlan Somers TEST_F(WriteCluster, clustering) 7438eecd9ceSAlan Somers { 7448eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7458eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 7468eecd9ceSAlan Somers uint64_t ino = 42; 7478eecd9ceSAlan Somers int i, fd; 7488eecd9ceSAlan Somers void *wbuf, *wbuf2x; 7496ca3b02bSAlan Somers ssize_t bufsize = m_maxbcachebuf; 7506ca3b02bSAlan Somers off_t filesize = 5 * bufsize; 7518eecd9ceSAlan Somers 7528eecd9ceSAlan Somers wbuf = malloc(bufsize); 7535a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf) << strerror(errno); 7548eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 7558eecd9ceSAlan Somers wbuf2x = malloc(2 * bufsize); 7565a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf2x) << strerror(errno); 7578eecd9ceSAlan Somers memset(wbuf2x, 'X', 2 * bufsize); 7588eecd9ceSAlan Somers 7598eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 7608eecd9ceSAlan Somers expect_open(ino, 0, 1); 7618eecd9ceSAlan Somers /* 7628eecd9ceSAlan Somers * Writes of bufsize-bytes each should be clustered into greater sizes. 7638eecd9ceSAlan Somers * The amount of clustering is adaptive, so the first write actually 7648eecd9ceSAlan Somers * issued will be 2x bufsize and subsequent writes may be larger 7658eecd9ceSAlan Somers */ 7668eecd9ceSAlan Somers expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x); 7678eecd9ceSAlan Somers expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x); 7688eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 7698eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 7708eecd9ceSAlan Somers 7718eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 7728eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7738eecd9ceSAlan Somers 7748eecd9ceSAlan Somers for (i = 0; i < 4; i++) { 7758eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 7768eecd9ceSAlan Somers << strerror(errno); 7778eecd9ceSAlan Somers } 7788eecd9ceSAlan Somers close(fd); 7798eecd9ceSAlan Somers } 7808eecd9ceSAlan Somers 7818eecd9ceSAlan Somers /* 7828eecd9ceSAlan Somers * When clustering writes, an I/O error to any of the cluster's children should 7838eecd9ceSAlan Somers * not panic the system on unmount 7848eecd9ceSAlan Somers */ 7858eecd9ceSAlan Somers /* 7868eecd9ceSAlan Somers * Disabled because it panics. 7878eecd9ceSAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565 7888eecd9ceSAlan Somers */ 7898eecd9ceSAlan Somers TEST_F(WriteCluster, DISABLED_cluster_write_err) 7908eecd9ceSAlan Somers { 7918eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7928eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 7938eecd9ceSAlan Somers uint64_t ino = 42; 7948eecd9ceSAlan Somers int i, fd; 7958eecd9ceSAlan Somers void *wbuf; 7966ca3b02bSAlan Somers ssize_t bufsize = m_maxbcachebuf; 7976ca3b02bSAlan Somers off_t filesize = 4 * bufsize; 7988eecd9ceSAlan Somers 7998eecd9ceSAlan Somers wbuf = malloc(bufsize); 8005a0b9a27SAlan Somers ASSERT_NE(nullptr, wbuf) << strerror(errno); 8018eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 8028eecd9ceSAlan Somers 8038eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 8048eecd9ceSAlan Somers expect_open(ino, 0, 1); 8058eecd9ceSAlan Somers EXPECT_CALL(*m_mock, process( 8068eecd9ceSAlan Somers ResultOf([=](auto in) { 8078eecd9ceSAlan Somers return (in.header.opcode == FUSE_WRITE); 8088eecd9ceSAlan Somers }, Eq(true)), 8098eecd9ceSAlan Somers _) 8108eecd9ceSAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(EIO))); 8118eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 8128eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 8138eecd9ceSAlan Somers 8148eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 8158eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 8168eecd9ceSAlan Somers 8178eecd9ceSAlan Somers for (i = 0; i < 3; i++) { 8188eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 8198eecd9ceSAlan Somers << strerror(errno); 8208eecd9ceSAlan Somers } 8218eecd9ceSAlan Somers close(fd); 8228eecd9ceSAlan Somers } 8238eecd9ceSAlan Somers 8249821f1d3SAlan Somers /* 8255fccbf31SAlan Somers * In writeback mode, writes to an O_WRONLY file could trigger reads from the 8265fccbf31SAlan Somers * server. The FUSE protocol explicitly allows that. 8275fccbf31SAlan Somers */ 8285fccbf31SAlan Somers TEST_F(WriteBack, rmw) 8295fccbf31SAlan Somers { 8305fccbf31SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8315fccbf31SAlan Somers const char RELPATH[] = "some_file.txt"; 8325fccbf31SAlan Somers const char *CONTENTS = "abcdefgh"; 8335fccbf31SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 8345fccbf31SAlan Somers uint64_t ino = 42; 8355fccbf31SAlan Somers uint64_t offset = 1; 8365fccbf31SAlan Somers off_t fsize = 10; 8375fccbf31SAlan Somers int fd; 8385fccbf31SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8395fccbf31SAlan Somers 840cad67791SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1); 8415fccbf31SAlan Somers expect_open(ino, 0, 1); 842d4fd0c81SAlan Somers expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY); 84384879e46SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS); 8445fccbf31SAlan Somers 8455fccbf31SAlan Somers fd = open(FULLPATH, O_WRONLY); 8465fccbf31SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 8475fccbf31SAlan Somers 8485fccbf31SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 8495fccbf31SAlan Somers << strerror(errno); 8507fc0921dSAlan Somers leak(fd); 8515fccbf31SAlan Somers } 8525fccbf31SAlan Somers 8535fccbf31SAlan Somers /* 8549821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 8559821f1d3SAlan Somers */ 85684879e46SAlan Somers TEST_F(WriteBack, cache) 8579821f1d3SAlan Somers { 8589821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8599821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8609821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 8619821f1d3SAlan Somers uint64_t ino = 42; 8629821f1d3SAlan Somers int fd; 8639821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8649821f1d3SAlan Somers char readbuf[bufsize]; 8659821f1d3SAlan Somers 8669821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 8679821f1d3SAlan Somers expect_open(ino, 0, 1); 868bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 8699821f1d3SAlan Somers 8709821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 8719821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 8729821f1d3SAlan Somers 8739821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 8749821f1d3SAlan Somers /* 8759821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 8769821f1d3SAlan Somers * filesystem daemon 8779821f1d3SAlan Somers */ 8789821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 8799821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 8807fc0921dSAlan Somers leak(fd); 8819821f1d3SAlan Somers } 8829821f1d3SAlan Somers 8839821f1d3SAlan Somers /* 8849821f1d3SAlan Somers * With O_DIRECT, writes should be not committed to cache. Admittedly this is 8859821f1d3SAlan Somers * an odd test, because it would be unusual to use O_DIRECT for writes but not 8869821f1d3SAlan Somers * reads. 8879821f1d3SAlan Somers */ 8889821f1d3SAlan Somers TEST_F(WriteBack, o_direct) 8899821f1d3SAlan Somers { 8909821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8919821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8929821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 8939821f1d3SAlan Somers uint64_t ino = 42; 8949821f1d3SAlan Somers int fd; 8959821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8969821f1d3SAlan Somers char readbuf[bufsize]; 8979821f1d3SAlan Somers 8989821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 8999821f1d3SAlan Somers expect_open(ino, 0, 1); 900bda39894SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE, 901bda39894SAlan Somers CONTENTS); 9029821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS); 9039821f1d3SAlan Somers 9049821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_DIRECT); 9059821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 9069821f1d3SAlan Somers 9079821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 9089821f1d3SAlan Somers /* A subsequent read must query the daemon because cache is empty */ 9099821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 9109821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 9119821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 9127fc0921dSAlan Somers leak(fd); 9139821f1d3SAlan Somers } 9149821f1d3SAlan Somers 9159821f1d3SAlan Somers /* 91684879e46SAlan Somers * When mounted with -o async, the writeback cache mode should delay writes 91784879e46SAlan Somers */ 91884879e46SAlan Somers TEST_F(WriteBackAsync, delay) 91984879e46SAlan Somers { 92084879e46SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 92184879e46SAlan Somers const char RELPATH[] = "some_file.txt"; 92284879e46SAlan Somers const char *CONTENTS = "abcdefgh"; 92384879e46SAlan Somers uint64_t ino = 42; 92484879e46SAlan Somers int fd; 92584879e46SAlan Somers ssize_t bufsize = strlen(CONTENTS); 92684879e46SAlan Somers 92784879e46SAlan Somers expect_lookup(RELPATH, ino, 0); 92884879e46SAlan Somers expect_open(ino, 0, 1); 92984879e46SAlan Somers /* Write should be cached, but FUSE_WRITE shouldn't be sent */ 93084879e46SAlan Somers EXPECT_CALL(*m_mock, process( 93184879e46SAlan Somers ResultOf([=](auto in) { 93284879e46SAlan Somers return (in.header.opcode == FUSE_WRITE); 93384879e46SAlan Somers }, Eq(true)), 93484879e46SAlan Somers _) 93584879e46SAlan Somers ).Times(0); 93684879e46SAlan Somers 93784879e46SAlan Somers fd = open(FULLPATH, O_RDWR); 93884879e46SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 93984879e46SAlan Somers 94084879e46SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 94184879e46SAlan Somers 94284879e46SAlan Somers /* Don't close the file because that would flush the cache */ 94384879e46SAlan Somers } 94484879e46SAlan Somers 94584879e46SAlan Somers /* 946669a092aSAlan Somers * A direct write should not evict dirty cached data from outside of its own 947669a092aSAlan Somers * byte range. 948669a092aSAlan Somers */ 949669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_ignores_unrelated_cached) 950669a092aSAlan Somers { 951669a092aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 952669a092aSAlan Somers const char RELPATH[] = "some_file.txt"; 953669a092aSAlan Somers const char CONTENTS0[] = "abcdefgh"; 954669a092aSAlan Somers const char CONTENTS1[] = "ijklmnop"; 955669a092aSAlan Somers uint64_t ino = 42; 956669a092aSAlan Somers int fd; 957669a092aSAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1; 958669a092aSAlan Somers ssize_t fsize = 2 * m_maxbcachebuf; 959669a092aSAlan Somers char readbuf[bufsize]; 960669a092aSAlan Somers void *zeros; 961669a092aSAlan Somers 962669a092aSAlan Somers zeros = calloc(1, m_maxbcachebuf); 963669a092aSAlan Somers ASSERT_NE(nullptr, zeros); 964669a092aSAlan Somers 965669a092aSAlan Somers expect_lookup(RELPATH, ino, fsize); 966669a092aSAlan Somers expect_open(ino, 0, 1); 967669a092aSAlan Somers expect_read(ino, 0, m_maxbcachebuf, m_maxbcachebuf, zeros); 968669a092aSAlan Somers FuseTest::expect_write(ino, m_maxbcachebuf, bufsize, bufsize, 0, 0, 969669a092aSAlan Somers CONTENTS1); 970669a092aSAlan Somers 971669a092aSAlan Somers fd = open(FULLPATH, O_RDWR); 972669a092aSAlan Somers EXPECT_LE(0, fd) << strerror(errno); 973669a092aSAlan Somers 974669a092aSAlan Somers // Cache first block with dirty data. This will entail first reading 975669a092aSAlan Somers // the existing data. 976669a092aSAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS0, bufsize, 0)) 977669a092aSAlan Somers << strerror(errno); 978669a092aSAlan Somers 979669a092aSAlan Somers // Write directly to second block 980669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 981669a092aSAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS1, bufsize, m_maxbcachebuf)) 982669a092aSAlan Somers << strerror(errno); 983669a092aSAlan Somers 984669a092aSAlan Somers // Read from the first block again. Should be serviced by cache. 985669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 986669a092aSAlan Somers ASSERT_EQ(bufsize, pread(fd, readbuf, bufsize, 0)) << strerror(errno); 987669a092aSAlan Somers ASSERT_STREQ(readbuf, CONTENTS0); 988669a092aSAlan Somers 989669a092aSAlan Somers leak(fd); 990669a092aSAlan Somers free(zeros); 991669a092aSAlan Somers } 992669a092aSAlan Somers 993669a092aSAlan Somers /* 994669a092aSAlan Somers * If a direct io write partially overlaps one or two blocks of dirty cached 995669a092aSAlan Somers * data, No dirty data should be lost. Admittedly this is a weird test, 996669a092aSAlan Somers * because it would be unusual to use O_DIRECT and the writeback cache. 997669a092aSAlan Somers */ 998669a092aSAlan Somers TEST_F(WriteBackAsync, direct_io_partially_overlaps_cached_block) 999669a092aSAlan Somers { 1000669a092aSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1001669a092aSAlan Somers const char RELPATH[] = "some_file.txt"; 1002669a092aSAlan Somers uint64_t ino = 42; 1003669a092aSAlan Somers int fd; 1004669a092aSAlan Somers off_t bs = m_maxbcachebuf; 1005669a092aSAlan Somers ssize_t fsize = 3 * bs; 1006669a092aSAlan Somers void *readbuf, *zeros, *ones, *zeroones, *onezeros; 1007669a092aSAlan Somers 1008669a092aSAlan Somers readbuf = malloc(bs); 1009669a092aSAlan Somers ASSERT_NE(nullptr, readbuf) << strerror(errno); 1010669a092aSAlan Somers zeros = calloc(1, 3 * bs); 1011669a092aSAlan Somers ASSERT_NE(nullptr, zeros); 1012669a092aSAlan Somers ones = calloc(1, 2 * bs); 1013669a092aSAlan Somers ASSERT_NE(nullptr, ones); 1014669a092aSAlan Somers memset(ones, 1, 2 * bs); 1015669a092aSAlan Somers zeroones = calloc(1, bs); 1016669a092aSAlan Somers ASSERT_NE(nullptr, zeroones); 1017669a092aSAlan Somers memset((uint8_t*)zeroones + bs / 2, 1, bs / 2); 1018669a092aSAlan Somers onezeros = calloc(1, bs); 1019669a092aSAlan Somers ASSERT_NE(nullptr, onezeros); 1020669a092aSAlan Somers memset(onezeros, 1, bs / 2); 1021669a092aSAlan Somers 1022669a092aSAlan Somers expect_lookup(RELPATH, ino, fsize); 1023669a092aSAlan Somers expect_open(ino, 0, 1); 1024669a092aSAlan Somers 1025669a092aSAlan Somers fd = open(FULLPATH, O_RDWR); 1026669a092aSAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1027669a092aSAlan Somers 1028669a092aSAlan Somers /* Cache first and third blocks with dirty data. */ 1029669a092aSAlan Somers ASSERT_EQ(3 * bs, pwrite(fd, zeros, 3 * bs, 0)) << strerror(errno); 1030669a092aSAlan Somers 1031669a092aSAlan Somers /* 1032669a092aSAlan Somers * Write directly to all three blocks. The partially written blocks 1033669a092aSAlan Somers * will be flushed because they're dirty. 1034669a092aSAlan Somers */ 1035669a092aSAlan Somers FuseTest::expect_write(ino, 0, bs, bs, 0, 0, zeros); 1036669a092aSAlan Somers FuseTest::expect_write(ino, 2 * bs, bs, bs, 0, 0, zeros); 1037669a092aSAlan Somers /* The direct write is split in two because of the m_maxwrite value */ 1038669a092aSAlan Somers FuseTest::expect_write(ino, bs / 2, bs, bs, 0, 0, ones); 1039669a092aSAlan Somers FuseTest::expect_write(ino, 3 * bs / 2, bs, bs, 0, 0, ones); 1040669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 1041669a092aSAlan Somers ASSERT_EQ(2 * bs, pwrite(fd, ones, 2 * bs, bs / 2)) << strerror(errno); 1042669a092aSAlan Somers 1043669a092aSAlan Somers /* 1044669a092aSAlan Somers * Read from both the valid and invalid portions of the first and third 1045669a092aSAlan Somers * blocks again. This will entail FUSE_READ operations because these 1046669a092aSAlan Somers * blocks were invalidated by the direct write. 1047669a092aSAlan Somers */ 1048669a092aSAlan Somers expect_read(ino, 0, bs, bs, zeroones); 1049669a092aSAlan Somers expect_read(ino, 2 * bs, bs, bs, onezeros); 1050669a092aSAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 1051669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 0)) << strerror(errno); 1052669a092aSAlan Somers EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2)); 1053669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 5 * bs / 2)) 1054669a092aSAlan Somers << strerror(errno); 1055669a092aSAlan Somers EXPECT_EQ(0, memcmp(zeros, readbuf, bs / 2)); 1056669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, bs / 2)) 1057669a092aSAlan Somers << strerror(errno); 1058669a092aSAlan Somers EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2)); 1059669a092aSAlan Somers ASSERT_EQ(bs / 2, pread(fd, readbuf, bs / 2, 2 * bs)) 1060669a092aSAlan Somers << strerror(errno); 1061669a092aSAlan Somers EXPECT_EQ(0, memcmp(ones, readbuf, bs / 2)); 1062669a092aSAlan Somers 1063669a092aSAlan Somers leak(fd); 1064669a092aSAlan Somers free(zeroones); 1065669a092aSAlan Somers free(onezeros); 1066669a092aSAlan Somers free(ones); 1067669a092aSAlan Somers free(zeros); 1068669a092aSAlan Somers free(readbuf); 1069669a092aSAlan Somers } 1070669a092aSAlan Somers 1071669a092aSAlan Somers /* 1072aef22f2dSAlan Somers * In WriteBack mode, writes may be cached beyond what the server thinks is the 1073aef22f2dSAlan Somers * EOF. In this case, a short read at EOF should _not_ cause fusefs to update 1074aef22f2dSAlan Somers * the file's size. 1075aef22f2dSAlan Somers */ 1076aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof) 1077aef22f2dSAlan Somers { 1078aef22f2dSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1079aef22f2dSAlan Somers const char RELPATH[] = "some_file.txt"; 1080aef22f2dSAlan Somers const char *CONTENTS0 = "abcdefgh"; 1081aef22f2dSAlan Somers const char *CONTENTS1 = "ijklmnop"; 1082aef22f2dSAlan Somers uint64_t ino = 42; 1083aef22f2dSAlan Somers int fd; 1084aef22f2dSAlan Somers off_t offset = m_maxbcachebuf; 1085aef22f2dSAlan Somers ssize_t wbufsize = strlen(CONTENTS1); 1086aef22f2dSAlan Somers off_t old_filesize = (off_t)strlen(CONTENTS0); 1087aef22f2dSAlan Somers ssize_t rbufsize = 2 * old_filesize; 1088aef22f2dSAlan Somers char readbuf[rbufsize]; 1089aef22f2dSAlan Somers size_t holesize = rbufsize - old_filesize; 1090aef22f2dSAlan Somers char hole[holesize]; 1091aef22f2dSAlan Somers struct stat sb; 1092aef22f2dSAlan Somers ssize_t r; 1093aef22f2dSAlan Somers 1094aef22f2dSAlan Somers expect_lookup(RELPATH, ino, 0); 1095aef22f2dSAlan Somers expect_open(ino, 0, 1); 1096aef22f2dSAlan Somers expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0); 1097aef22f2dSAlan Somers 1098aef22f2dSAlan Somers fd = open(FULLPATH, O_RDWR); 1099aef22f2dSAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1100aef22f2dSAlan Somers 1101aef22f2dSAlan Somers /* Write and cache data beyond EOF */ 1102aef22f2dSAlan Somers ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset)) 1103aef22f2dSAlan Somers << strerror(errno); 1104aef22f2dSAlan Somers 1105aef22f2dSAlan Somers /* Read from the old EOF */ 1106aef22f2dSAlan Somers r = pread(fd, readbuf, rbufsize, 0); 1107aef22f2dSAlan Somers ASSERT_LE(0, r) << strerror(errno); 1108aef22f2dSAlan Somers EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole"; 1109aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize)); 1110aef22f2dSAlan Somers bzero(hole, holesize); 1111aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize)); 1112aef22f2dSAlan Somers 1113aef22f2dSAlan Somers /* The file's size should still be what was established by pwrite */ 1114aef22f2dSAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1115aef22f2dSAlan Somers EXPECT_EQ(offset + wbufsize, sb.st_size); 11167fc0921dSAlan Somers leak(fd); 1117aef22f2dSAlan Somers } 1118aef22f2dSAlan Somers 1119aef22f2dSAlan Somers /* 1120788af953SAlan Somers * When a file has dirty writes that haven't been flushed, the server's notion 1121788af953SAlan Somers * of its mtime and ctime will be wrong. The kernel should ignore those if it 1122788af953SAlan Somers * gets them from a FUSE_GETATTR before flushing. 1123788af953SAlan Somers */ 1124788af953SAlan Somers TEST_F(WriteBackAsync, timestamps) 1125788af953SAlan Somers { 1126788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1127788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 1128788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 1129788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1130788af953SAlan Somers uint64_t ino = 42; 1131788af953SAlan Somers uint64_t attr_valid = 0; 1132788af953SAlan Somers uint64_t attr_valid_nsec = 0; 1133788af953SAlan Somers uint64_t server_time = 12345; 1134788af953SAlan Somers mode_t mode = S_IFREG | 0644; 1135788af953SAlan Somers int fd; 1136788af953SAlan Somers 1137788af953SAlan Somers struct stat sb; 1138788af953SAlan Somers 1139788af953SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 1140788af953SAlan Somers .WillRepeatedly(Invoke( 1141788af953SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 1142788af953SAlan Somers SET_OUT_HEADER_LEN(out, entry); 1143788af953SAlan Somers out.body.entry.attr.mode = mode; 1144788af953SAlan Somers out.body.entry.nodeid = ino; 1145788af953SAlan Somers out.body.entry.attr.nlink = 1; 1146788af953SAlan Somers out.body.entry.attr_valid = attr_valid; 1147788af953SAlan Somers out.body.entry.attr_valid_nsec = attr_valid_nsec; 1148788af953SAlan Somers }))); 1149788af953SAlan Somers expect_open(ino, 0, 1); 1150788af953SAlan Somers EXPECT_CALL(*m_mock, process( 1151788af953SAlan Somers ResultOf([=](auto in) { 1152788af953SAlan Somers return (in.header.opcode == FUSE_GETATTR && 1153788af953SAlan Somers in.header.nodeid == ino); 1154788af953SAlan Somers }, Eq(true)), 1155788af953SAlan Somers _) 1156788af953SAlan Somers ).WillRepeatedly(Invoke( 1157788af953SAlan Somers ReturnImmediate([=](auto i __unused, auto& out) { 1158788af953SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1159788af953SAlan Somers out.body.attr.attr.ino = ino; 1160788af953SAlan Somers out.body.attr.attr.mode = mode; 1161788af953SAlan Somers out.body.attr.attr_valid = attr_valid; 1162788af953SAlan Somers out.body.attr.attr_valid_nsec = attr_valid_nsec; 1163788af953SAlan Somers out.body.attr.attr.atime = server_time; 1164788af953SAlan Somers out.body.attr.attr.mtime = server_time; 1165788af953SAlan Somers out.body.attr.attr.ctime = server_time; 1166788af953SAlan Somers }))); 1167788af953SAlan Somers 1168788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 1169788af953SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1170788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1171788af953SAlan Somers 1172788af953SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1173788af953SAlan Somers EXPECT_EQ((time_t)server_time, sb.st_atime); 1174788af953SAlan Somers EXPECT_NE((time_t)server_time, sb.st_mtime); 1175788af953SAlan Somers EXPECT_NE((time_t)server_time, sb.st_ctime); 1176788af953SAlan Somers } 1177788af953SAlan Somers 1178788af953SAlan Somers /* Any dirty timestamp fields should be flushed during a SETATTR */ 1179788af953SAlan Somers TEST_F(WriteBackAsync, timestamps_during_setattr) 1180788af953SAlan Somers { 1181788af953SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1182788af953SAlan Somers const char RELPATH[] = "some_file.txt"; 1183788af953SAlan Somers const char *CONTENTS = "abcdefgh"; 1184788af953SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1185788af953SAlan Somers uint64_t ino = 42; 1186788af953SAlan Somers const mode_t newmode = 0755; 1187788af953SAlan Somers int fd; 1188788af953SAlan Somers 1189788af953SAlan Somers expect_lookup(RELPATH, ino, 0); 1190788af953SAlan Somers expect_open(ino, 0, 1); 1191788af953SAlan Somers EXPECT_CALL(*m_mock, process( 1192788af953SAlan Somers ResultOf([=](auto in) { 11930a8fe2d3SAlan Somers uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME; 1194788af953SAlan Somers return (in.header.opcode == FUSE_SETATTR && 1195788af953SAlan Somers in.header.nodeid == ino && 1196788af953SAlan Somers in.body.setattr.valid == valid); 1197788af953SAlan Somers }, Eq(true)), 1198788af953SAlan Somers _) 1199788af953SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1200788af953SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1201788af953SAlan Somers out.body.attr.attr.ino = ino; 1202788af953SAlan Somers out.body.attr.attr.mode = S_IFREG | newmode; 1203788af953SAlan Somers }))); 1204788af953SAlan Somers 1205788af953SAlan Somers fd = open(FULLPATH, O_RDWR); 1206788af953SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1207788af953SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1208788af953SAlan Somers ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 1209788af953SAlan Somers } 1210788af953SAlan Somers 1211fef46454SAlan Somers /* fuse_init_out.time_gran controls the granularity of timestamps */ 1212fef46454SAlan Somers TEST_P(TimeGran, timestamps_during_setattr) 1213fef46454SAlan Somers { 1214fef46454SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1215fef46454SAlan Somers const char RELPATH[] = "some_file.txt"; 1216fef46454SAlan Somers const char *CONTENTS = "abcdefgh"; 1217fef46454SAlan Somers ssize_t bufsize = strlen(CONTENTS); 1218fef46454SAlan Somers uint64_t ino = 42; 1219fef46454SAlan Somers const mode_t newmode = 0755; 1220fef46454SAlan Somers int fd; 1221fef46454SAlan Somers 1222fef46454SAlan Somers expect_lookup(RELPATH, ino, 0); 1223fef46454SAlan Somers expect_open(ino, 0, 1); 1224fef46454SAlan Somers EXPECT_CALL(*m_mock, process( 1225fef46454SAlan Somers ResultOf([=](auto in) { 1226fef46454SAlan Somers uint32_t valid = FATTR_MODE | FATTR_MTIME | FATTR_CTIME; 1227fef46454SAlan Somers return (in.header.opcode == FUSE_SETATTR && 1228fef46454SAlan Somers in.header.nodeid == ino && 1229fef46454SAlan Somers in.body.setattr.valid == valid && 1230fef46454SAlan Somers in.body.setattr.mtimensec % m_time_gran == 0 && 1231fef46454SAlan Somers in.body.setattr.ctimensec % m_time_gran == 0); 1232fef46454SAlan Somers }, Eq(true)), 1233fef46454SAlan Somers _) 1234fef46454SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 1235fef46454SAlan Somers SET_OUT_HEADER_LEN(out, attr); 1236fef46454SAlan Somers out.body.attr.attr.ino = ino; 1237fef46454SAlan Somers out.body.attr.attr.mode = S_IFREG | newmode; 1238fef46454SAlan Somers }))); 1239fef46454SAlan Somers 1240fef46454SAlan Somers fd = open(FULLPATH, O_RDWR); 1241fef46454SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 1242fef46454SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 1243fef46454SAlan Somers ASSERT_EQ(0, fchmod(fd, newmode)) << strerror(errno); 1244fef46454SAlan Somers } 1245fef46454SAlan Somers 1246fef46454SAlan Somers INSTANTIATE_TEST_CASE_P(RA, TimeGran, Range(0u, 10u)); 1247fef46454SAlan Somers 1248788af953SAlan Somers /* 12499821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 12509821f1d3SAlan Somers */ 1251f8ebf1cdSAlan Somers TEST_F(Write, writethrough) 12529821f1d3SAlan Somers { 12539821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 12549821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 12559821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 12569821f1d3SAlan Somers uint64_t ino = 42; 12579821f1d3SAlan Somers int fd; 12589821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 12599821f1d3SAlan Somers char readbuf[bufsize]; 12609821f1d3SAlan Somers 12619821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 12629821f1d3SAlan Somers expect_open(ino, 0, 1); 1263bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 12649821f1d3SAlan Somers 12659821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 12669821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 12679821f1d3SAlan Somers 12689821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 12699821f1d3SAlan Somers /* 12709821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 12719821f1d3SAlan Somers * filesystem daemon 12729821f1d3SAlan Somers */ 1273b5aaf286SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 12749821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 12757fc0921dSAlan Somers leak(fd); 12769821f1d3SAlan Somers } 12779821f1d3SAlan Somers 12780d3a88d7SAlan Somers /* Writes that extend a file should update the cached file size */ 12790d3a88d7SAlan Somers TEST_F(Write, update_file_size) 12809821f1d3SAlan Somers { 12819821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 12829821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 12839821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 12849821f1d3SAlan Somers struct stat sb; 12859821f1d3SAlan Somers uint64_t ino = 42; 12869821f1d3SAlan Somers int fd; 12879821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 12889821f1d3SAlan Somers 12899821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 12909821f1d3SAlan Somers expect_open(ino, 0, 1); 1291bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 12929821f1d3SAlan Somers 12939821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 12949821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 12959821f1d3SAlan Somers 12969821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 12979821f1d3SAlan Somers /* Get cached attributes */ 12989821f1d3SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 12999821f1d3SAlan Somers ASSERT_EQ(bufsize, sb.st_size); 13007fc0921dSAlan Somers leak(fd); 13019821f1d3SAlan Somers } 1302