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" { 32*8eecd9ceSAlan Somers #include <sys/param.h> 339821f1d3SAlan Somers #include <sys/mman.h> 34a639731bSAlan Somers #include <sys/resource.h> 359821f1d3SAlan Somers #include <sys/stat.h> 369821f1d3SAlan Somers #include <sys/sysctl.h> 37a639731bSAlan Somers #include <sys/time.h> 389821f1d3SAlan Somers #include <sys/uio.h> 399821f1d3SAlan Somers 409821f1d3SAlan Somers #include <aio.h> 419821f1d3SAlan Somers #include <fcntl.h> 42a639731bSAlan Somers #include <signal.h> 439821f1d3SAlan Somers #include <unistd.h> 449821f1d3SAlan Somers } 459821f1d3SAlan Somers 469821f1d3SAlan Somers #include "mockfs.hh" 479821f1d3SAlan Somers #include "utils.hh" 489821f1d3SAlan Somers 499821f1d3SAlan Somers using namespace testing; 509821f1d3SAlan Somers 519821f1d3SAlan Somers class Write: public FuseTest { 529821f1d3SAlan Somers 539821f1d3SAlan Somers public: 54a639731bSAlan Somers static sig_atomic_t s_sigxfsz; 55a639731bSAlan Somers 56a639731bSAlan Somers void SetUp() { 57a639731bSAlan Somers s_sigxfsz = 0; 58a639731bSAlan Somers FuseTest::SetUp(); 59a639731bSAlan Somers } 60a639731bSAlan Somers 61a639731bSAlan Somers void TearDown() { 62a639731bSAlan Somers struct sigaction sa; 63a639731bSAlan Somers 64a639731bSAlan Somers bzero(&sa, sizeof(sa)); 65a639731bSAlan Somers sa.sa_handler = SIG_DFL; 66a639731bSAlan Somers sigaction(SIGXFSZ, &sa, NULL); 67a639731bSAlan Somers 68a639731bSAlan Somers FuseTest::TearDown(); 69a639731bSAlan Somers } 709821f1d3SAlan Somers 719821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 729821f1d3SAlan Somers { 739821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); 749821f1d3SAlan Somers } 759821f1d3SAlan Somers 769821f1d3SAlan Somers void expect_release(uint64_t ino, ProcessMockerT r) 779821f1d3SAlan Somers { 789821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 799821f1d3SAlan Somers ResultOf([=](auto in) { 8029edc611SAlan Somers return (in.header.opcode == FUSE_RELEASE && 8129edc611SAlan Somers in.header.nodeid == ino); 829821f1d3SAlan Somers }, Eq(true)), 839821f1d3SAlan Somers _) 849821f1d3SAlan Somers ).WillRepeatedly(Invoke(r)); 859821f1d3SAlan Somers } 869821f1d3SAlan Somers 87bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 88bda39894SAlan Somers uint64_t osize, const void *contents) 89bda39894SAlan Somers { 90bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, 0, 0, contents); 91bda39894SAlan Somers } 92bda39894SAlan Somers 939821f1d3SAlan Somers }; 949821f1d3SAlan Somers 95a639731bSAlan Somers sig_atomic_t Write::s_sigxfsz = 0; 96a639731bSAlan Somers 9716bd2d47SAlan Somers class Write_7_8: public FuseTest { 9816bd2d47SAlan Somers 9916bd2d47SAlan Somers public: 10016bd2d47SAlan Somers virtual void SetUp() { 10116bd2d47SAlan Somers m_kernel_minor_version = 8; 10216bd2d47SAlan Somers FuseTest::SetUp(); 10316bd2d47SAlan Somers } 10416bd2d47SAlan Somers 10516bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 10616bd2d47SAlan Somers { 10716bd2d47SAlan Somers FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1); 10816bd2d47SAlan Somers } 10916bd2d47SAlan Somers 11016bd2d47SAlan Somers }; 11116bd2d47SAlan Somers 1129821f1d3SAlan Somers class AioWrite: public Write { 1139821f1d3SAlan Somers virtual void SetUp() { 1149821f1d3SAlan Somers const char *node = "vfs.aio.enable_unsafe"; 1159821f1d3SAlan Somers int val = 0; 1169821f1d3SAlan Somers size_t size = sizeof(val); 1179821f1d3SAlan Somers 1189821f1d3SAlan Somers FuseTest::SetUp(); 1199821f1d3SAlan Somers 1209821f1d3SAlan Somers ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) 1219821f1d3SAlan Somers << strerror(errno); 1229821f1d3SAlan Somers if (!val) 1239821f1d3SAlan Somers GTEST_SKIP() << 1249821f1d3SAlan Somers "vfs.aio.enable_unsafe must be set for this test"; 1259821f1d3SAlan Somers } 1269821f1d3SAlan Somers }; 1279821f1d3SAlan Somers 1289821f1d3SAlan Somers /* Tests for the write-through cache mode */ 1299821f1d3SAlan Somers class WriteThrough: public Write { 130bda39894SAlan Somers public: 1319821f1d3SAlan Somers virtual void SetUp() { 1329821f1d3SAlan Somers const char *cache_mode_node = "vfs.fusefs.data_cache_mode"; 1339821f1d3SAlan Somers int val = 0; 1349821f1d3SAlan Somers size_t size = sizeof(val); 1359821f1d3SAlan Somers 1369821f1d3SAlan Somers FuseTest::SetUp(); 1379821f1d3SAlan Somers if (IsSkipped()) 1389821f1d3SAlan Somers return; 1399821f1d3SAlan Somers 1409821f1d3SAlan Somers ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0)) 1419821f1d3SAlan Somers << strerror(errno); 1429821f1d3SAlan Somers if (val != 1) 1439821f1d3SAlan Somers GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 " 1449821f1d3SAlan Somers "(writethrough) for this test"; 1459821f1d3SAlan Somers } 1469821f1d3SAlan Somers 147bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 148bda39894SAlan Somers uint64_t osize, const void *contents) 149bda39894SAlan Somers { 150bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, 0, FUSE_WRITE_CACHE, 151bda39894SAlan Somers contents); 152bda39894SAlan Somers } 1539821f1d3SAlan Somers }; 1549821f1d3SAlan Somers 1559821f1d3SAlan Somers /* Tests for the writeback cache mode */ 1569821f1d3SAlan Somers class WriteBack: public Write { 157bda39894SAlan Somers public: 1589821f1d3SAlan Somers virtual void SetUp() { 1599821f1d3SAlan Somers const char *node = "vfs.fusefs.data_cache_mode"; 1609821f1d3SAlan Somers int val = 0; 1619821f1d3SAlan Somers size_t size = sizeof(val); 1629821f1d3SAlan Somers 1639821f1d3SAlan Somers FuseTest::SetUp(); 1649821f1d3SAlan Somers if (IsSkipped()) 1659821f1d3SAlan Somers return; 1669821f1d3SAlan Somers 1679821f1d3SAlan Somers ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) 1689821f1d3SAlan Somers << strerror(errno); 1699821f1d3SAlan Somers if (val != 2) 1709821f1d3SAlan Somers GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 " 1719821f1d3SAlan Somers "(writeback) for this test"; 1729821f1d3SAlan Somers } 1739821f1d3SAlan Somers 174bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 175bda39894SAlan Somers uint64_t osize, const void *contents) 176bda39894SAlan Somers { 177bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0, 178bda39894SAlan Somers contents); 179bda39894SAlan Somers } 1809821f1d3SAlan Somers }; 1819821f1d3SAlan Somers 182*8eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */ 183*8eecd9ceSAlan Somers class WriteCluster: public WriteBack { 184*8eecd9ceSAlan Somers public: 185*8eecd9ceSAlan Somers virtual void SetUp() { 186*8eecd9ceSAlan Somers if (MAXPHYS < 2 * DFLTPHYS) 187*8eecd9ceSAlan Somers GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS" 188*8eecd9ceSAlan Somers << "for this test"; 189*8eecd9ceSAlan Somers m_async = true; 190*8eecd9ceSAlan Somers m_maxwrite = MAXPHYS; 191*8eecd9ceSAlan Somers WriteBack::SetUp(); 192*8eecd9ceSAlan Somers } 193*8eecd9ceSAlan Somers }; 194*8eecd9ceSAlan 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; 2219821f1d3SAlan Somers iocb.aio_buf = (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); 2269821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 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); 2629821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 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); 281a87e0831SAlan Somers ASSERT_NE(NULL, oldcontents) << strerror(errno); 282a87e0831SAlan Somers oldbuf = (char*)malloc(oldsize); 283a87e0831SAlan Somers ASSERT_NE(NULL, 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); 288a87e0831SAlan Somers expect_write(ino, oldsize, BUFSIZE, 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); 300a87e0831SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 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); 3219821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 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 3599821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 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); 3899821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 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); 41412292a99SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 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 4439821f1d3SAlan Somers iov[0].iov_base = (void*)CONTENTS0; 4449821f1d3SAlan Somers iov[0].iov_len = strlen(CONTENTS0); 4459821f1d3SAlan Somers iov[1].iov_base = (void*)CONTENTS1; 4469821f1d3SAlan Somers iov[1].iov_len = strlen(CONTENTS1); 44712292a99SAlan Somers ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno); 4489821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 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); 478a639731bSAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 479a639731bSAlan Somers } 480a639731bSAlan Somers 4819821f1d3SAlan Somers /* 4829821f1d3SAlan Somers * If the kernel cannot be sure which uid, gid, or pid was responsible for a 4839821f1d3SAlan Somers * write, then it must set the FUSE_WRITE_CACHE bit 4849821f1d3SAlan Somers */ 4859821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */ 4869821f1d3SAlan Somers // TODO: check vfs.fusefs.mmap_enable 487cf437e2aSAlan Somers TEST_F(Write, mmap) 4889821f1d3SAlan Somers { 4899821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4909821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 4919821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 4929821f1d3SAlan Somers uint64_t ino = 42; 4939821f1d3SAlan Somers int fd; 4949821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 4959821f1d3SAlan Somers void *p; 4969821f1d3SAlan Somers uint64_t offset = 10; 4979821f1d3SAlan Somers size_t len; 4989821f1d3SAlan Somers void *zeros, *expected; 4999821f1d3SAlan Somers 5009821f1d3SAlan Somers len = getpagesize(); 5019821f1d3SAlan Somers 5029821f1d3SAlan Somers zeros = calloc(1, len); 5039821f1d3SAlan Somers ASSERT_NE(NULL, zeros); 5049821f1d3SAlan Somers expected = calloc(1, len); 5059821f1d3SAlan Somers ASSERT_NE(NULL, expected); 5069821f1d3SAlan Somers memmove((uint8_t*)expected + offset, CONTENTS, bufsize); 5079821f1d3SAlan Somers 5089821f1d3SAlan Somers expect_lookup(RELPATH, ino, len); 5099821f1d3SAlan Somers expect_open(ino, 0, 1); 5109821f1d3SAlan Somers expect_read(ino, 0, len, len, zeros); 5119821f1d3SAlan Somers /* 5129821f1d3SAlan Somers * Writes from the pager may or may not be associated with the correct 513cf437e2aSAlan Somers * pid, so they must set FUSE_WRITE_CACHE. 5149821f1d3SAlan Somers */ 515bda39894SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected); 5169f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 5179821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 5189821f1d3SAlan Somers 5199821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 5209821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 5219821f1d3SAlan Somers 5229821f1d3SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 5239821f1d3SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno); 5249821f1d3SAlan Somers 5259821f1d3SAlan Somers memmove((uint8_t*)p + offset, CONTENTS, bufsize); 5269821f1d3SAlan Somers 5279821f1d3SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 5289821f1d3SAlan Somers close(fd); // Write mmap'd data on close 5299821f1d3SAlan Somers 5309821f1d3SAlan Somers free(expected); 5319821f1d3SAlan Somers free(zeros); 5329821f1d3SAlan Somers } 5339821f1d3SAlan Somers 5346af6fdceSAlan Somers /* In WriteThrough mode, a write should evict overlapping cached data */ 5356af6fdceSAlan Somers TEST_F(WriteThrough, evicts_read_cache) 5366af6fdceSAlan Somers { 5376af6fdceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 5386af6fdceSAlan Somers const char RELPATH[] = "some_file.txt"; 53975d5cb29SAlan Somers ssize_t bufsize = 65536; 54075d5cb29SAlan Somers /* End the write in the middle of a page */ 54175d5cb29SAlan Somers ssize_t wrsize = bufsize - 1000; 54275d5cb29SAlan Somers char *contents0, *contents1, *readbuf, *expected; 5436af6fdceSAlan Somers uint64_t ino = 42; 5446af6fdceSAlan Somers int fd; 54575d5cb29SAlan Somers 54675d5cb29SAlan Somers contents0 = (char*)malloc(bufsize); 54775d5cb29SAlan Somers memset(contents0, 'X', bufsize); 54875d5cb29SAlan Somers contents0[bufsize - 1] = '\0'; // Null-terminate 54975d5cb29SAlan Somers contents1 = (char*)malloc(wrsize); 55075d5cb29SAlan Somers memset(contents1, 'Y', wrsize); 55175d5cb29SAlan Somers readbuf = (char*)calloc(bufsize, 1); 55275d5cb29SAlan Somers expected = (char*)malloc(bufsize); 55375d5cb29SAlan Somers memset(expected, 'Y', wrsize); 55475d5cb29SAlan Somers memset(expected + wrsize, 'X', bufsize - wrsize); 55575d5cb29SAlan Somers expected[bufsize - 1] = '\0'; // Null-terminate 5566af6fdceSAlan Somers 5576af6fdceSAlan Somers expect_lookup(RELPATH, ino, bufsize); 5586af6fdceSAlan Somers expect_open(ino, 0, 1); 55975d5cb29SAlan Somers expect_read(ino, 0, bufsize, bufsize, contents0); 560bda39894SAlan Somers expect_write(ino, 0, wrsize, wrsize, contents1); 5616af6fdceSAlan Somers 5626af6fdceSAlan Somers fd = open(FULLPATH, O_RDWR); 5636af6fdceSAlan Somers EXPECT_LE(0, fd) << strerror(errno); 5646af6fdceSAlan Somers 5656af6fdceSAlan Somers // Prime cache 5666af6fdceSAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 5676af6fdceSAlan Somers 5686af6fdceSAlan Somers // Write directly, evicting cache 5696af6fdceSAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 57075d5cb29SAlan Somers ASSERT_EQ(wrsize, write(fd, contents1, wrsize)) << strerror(errno); 5716af6fdceSAlan Somers 5726af6fdceSAlan Somers // Read again. Cache should be bypassed 57375d5cb29SAlan Somers expect_read(ino, 0, bufsize, bufsize, expected); 5746af6fdceSAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 5756af6fdceSAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 57675d5cb29SAlan Somers ASSERT_STREQ(readbuf, expected); 5776af6fdceSAlan Somers 5786af6fdceSAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 5796af6fdceSAlan Somers } 5806af6fdceSAlan Somers 5815fccbf31SAlan Somers TEST_F(WriteThrough, pwrite) 5829821f1d3SAlan Somers { 5839821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 5849821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 5859821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 5869821f1d3SAlan Somers uint64_t ino = 42; 5879821f1d3SAlan Somers uint64_t offset = 4096; 5889821f1d3SAlan Somers int fd; 5899821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 5909821f1d3SAlan Somers 5919821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 5929821f1d3SAlan Somers expect_open(ino, 0, 1); 593bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS); 5949821f1d3SAlan Somers 5959821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 5969821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 5979821f1d3SAlan Somers 5989821f1d3SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 5999821f1d3SAlan Somers << strerror(errno); 6009821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6019821f1d3SAlan Somers } 6029821f1d3SAlan Somers 6039821f1d3SAlan Somers TEST_F(Write, write) 6049821f1d3SAlan Somers { 6059821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6069821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6079821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 6089821f1d3SAlan Somers uint64_t ino = 42; 6099821f1d3SAlan Somers int fd; 6109821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 6119821f1d3SAlan Somers 6129821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6139821f1d3SAlan Somers expect_open(ino, 0, 1); 614bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 6159821f1d3SAlan Somers 6169821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 6179821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6189821f1d3SAlan Somers 6199821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 6209821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6219821f1d3SAlan Somers } 6229821f1d3SAlan Somers 6239821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */ 6249821f1d3SAlan Somers TEST_F(Write, write_large) 6259821f1d3SAlan Somers { 6269821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6279821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6289821f1d3SAlan Somers int *contents; 6299821f1d3SAlan Somers uint64_t ino = 42; 6309821f1d3SAlan Somers int fd; 6319821f1d3SAlan Somers ssize_t halfbufsize, bufsize; 6329821f1d3SAlan Somers 633*8eecd9ceSAlan Somers halfbufsize = m_mock->m_maxwrite; 6349821f1d3SAlan Somers bufsize = halfbufsize * 2; 6359821f1d3SAlan Somers contents = (int*)malloc(bufsize); 6369821f1d3SAlan Somers ASSERT_NE(NULL, contents); 6379821f1d3SAlan Somers for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) { 6389821f1d3SAlan Somers contents[i] = i; 6399821f1d3SAlan Somers } 6409821f1d3SAlan Somers 6419821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6429821f1d3SAlan Somers expect_open(ino, 0, 1); 643bda39894SAlan Somers expect_write(ino, 0, halfbufsize, halfbufsize, contents); 644bda39894SAlan Somers expect_write(ino, halfbufsize, halfbufsize, halfbufsize, 6459821f1d3SAlan Somers &contents[halfbufsize / sizeof(int)]); 6469821f1d3SAlan Somers 6479821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 6489821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6499821f1d3SAlan Somers 6509821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, contents, bufsize)) << strerror(errno); 6519821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6529821f1d3SAlan Somers 6539821f1d3SAlan Somers free(contents); 6549821f1d3SAlan Somers } 6559821f1d3SAlan Somers 6569821f1d3SAlan Somers TEST_F(Write, write_nothing) 6579821f1d3SAlan Somers { 6589821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6599821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6609821f1d3SAlan Somers const char *CONTENTS = ""; 6619821f1d3SAlan Somers uint64_t ino = 42; 6629821f1d3SAlan Somers int fd; 6639821f1d3SAlan Somers ssize_t bufsize = 0; 6649821f1d3SAlan Somers 6659821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6669821f1d3SAlan Somers expect_open(ino, 0, 1); 6679821f1d3SAlan Somers 6689821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 6699821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6709821f1d3SAlan Somers 6719821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 6729821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6739821f1d3SAlan Somers } 6749821f1d3SAlan Somers 67516bd2d47SAlan Somers TEST_F(Write_7_8, write) 67616bd2d47SAlan Somers { 67716bd2d47SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 67816bd2d47SAlan Somers const char RELPATH[] = "some_file.txt"; 67916bd2d47SAlan Somers const char *CONTENTS = "abcdefgh"; 68016bd2d47SAlan Somers uint64_t ino = 42; 68116bd2d47SAlan Somers int fd; 68216bd2d47SAlan Somers ssize_t bufsize = strlen(CONTENTS); 68316bd2d47SAlan Somers 68416bd2d47SAlan Somers expect_lookup(RELPATH, ino, 0); 68516bd2d47SAlan Somers expect_open(ino, 0, 1); 686bda39894SAlan Somers expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS); 68716bd2d47SAlan Somers 68816bd2d47SAlan Somers fd = open(FULLPATH, O_WRONLY); 68916bd2d47SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 69016bd2d47SAlan Somers 69116bd2d47SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 69216bd2d47SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 69316bd2d47SAlan Somers } 69416bd2d47SAlan Somers 6959821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */ 6969821f1d3SAlan Somers TEST_F(WriteBack, close) 6979821f1d3SAlan Somers { 6989821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6999821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7009821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 7019821f1d3SAlan Somers uint64_t ino = 42; 7029821f1d3SAlan Somers int fd; 7039821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 7049821f1d3SAlan Somers 7059821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 7069821f1d3SAlan Somers expect_open(ino, 0, 1); 707bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 7089821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 7099821f1d3SAlan Somers ResultOf([=](auto in) { 71029edc611SAlan Somers return (in.header.opcode == FUSE_SETATTR); 7119821f1d3SAlan Somers }, Eq(true)), 7129821f1d3SAlan Somers _) 71329edc611SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 7149821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, attr); 71529edc611SAlan Somers out.body.attr.attr.ino = ino; // Must match nodeid 7169821f1d3SAlan Somers }))); 7179f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 7189821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 7199821f1d3SAlan Somers 7209821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 7219821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7229821f1d3SAlan Somers 7239821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 7249821f1d3SAlan Somers close(fd); 7259821f1d3SAlan Somers } 7269821f1d3SAlan Somers 727*8eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */ 728*8eecd9ceSAlan Somers TEST_F(WriteCluster, clustering) 729*8eecd9ceSAlan Somers { 730*8eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 731*8eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 732*8eecd9ceSAlan Somers uint64_t ino = 42; 733*8eecd9ceSAlan Somers int i, fd; 734*8eecd9ceSAlan Somers void *wbuf, *wbuf2x; 735*8eecd9ceSAlan Somers ssize_t bufsize = 65536; 736*8eecd9ceSAlan Somers off_t filesize = 327680; 737*8eecd9ceSAlan Somers 738*8eecd9ceSAlan Somers wbuf = malloc(bufsize); 739*8eecd9ceSAlan Somers ASSERT_NE(NULL, wbuf) << strerror(errno); 740*8eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 741*8eecd9ceSAlan Somers wbuf2x = malloc(2 * bufsize); 742*8eecd9ceSAlan Somers ASSERT_NE(NULL, wbuf2x) << strerror(errno); 743*8eecd9ceSAlan Somers memset(wbuf2x, 'X', 2 * bufsize); 744*8eecd9ceSAlan Somers 745*8eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 746*8eecd9ceSAlan Somers expect_open(ino, 0, 1); 747*8eecd9ceSAlan Somers /* 748*8eecd9ceSAlan Somers * Writes of bufsize-bytes each should be clustered into greater sizes. 749*8eecd9ceSAlan Somers * The amount of clustering is adaptive, so the first write actually 750*8eecd9ceSAlan Somers * issued will be 2x bufsize and subsequent writes may be larger 751*8eecd9ceSAlan Somers */ 752*8eecd9ceSAlan Somers expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x); 753*8eecd9ceSAlan Somers expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x); 754*8eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 755*8eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 756*8eecd9ceSAlan Somers 757*8eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 758*8eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 759*8eecd9ceSAlan Somers 760*8eecd9ceSAlan Somers for (i = 0; i < 4; i++) { 761*8eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 762*8eecd9ceSAlan Somers << strerror(errno); 763*8eecd9ceSAlan Somers } 764*8eecd9ceSAlan Somers close(fd); 765*8eecd9ceSAlan Somers } 766*8eecd9ceSAlan Somers 767*8eecd9ceSAlan Somers /* 768*8eecd9ceSAlan Somers * When clustering writes, an I/O error to any of the cluster's children should 769*8eecd9ceSAlan Somers * not panic the system on unmount 770*8eecd9ceSAlan Somers */ 771*8eecd9ceSAlan Somers /* 772*8eecd9ceSAlan Somers * Disabled because it panics. 773*8eecd9ceSAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565 774*8eecd9ceSAlan Somers */ 775*8eecd9ceSAlan Somers TEST_F(WriteCluster, DISABLED_cluster_write_err) 776*8eecd9ceSAlan Somers { 777*8eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 778*8eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 779*8eecd9ceSAlan Somers uint64_t ino = 42; 780*8eecd9ceSAlan Somers int i, fd; 781*8eecd9ceSAlan Somers void *wbuf; 782*8eecd9ceSAlan Somers ssize_t bufsize = 65536; 783*8eecd9ceSAlan Somers off_t filesize = 262144; 784*8eecd9ceSAlan Somers 785*8eecd9ceSAlan Somers wbuf = malloc(bufsize); 786*8eecd9ceSAlan Somers ASSERT_NE(NULL, wbuf) << strerror(errno); 787*8eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 788*8eecd9ceSAlan Somers 789*8eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 790*8eecd9ceSAlan Somers expect_open(ino, 0, 1); 791*8eecd9ceSAlan Somers EXPECT_CALL(*m_mock, process( 792*8eecd9ceSAlan Somers ResultOf([=](auto in) { 793*8eecd9ceSAlan Somers return (in.header.opcode == FUSE_WRITE); 794*8eecd9ceSAlan Somers }, Eq(true)), 795*8eecd9ceSAlan Somers _) 796*8eecd9ceSAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(EIO))); 797*8eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 798*8eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 799*8eecd9ceSAlan Somers 800*8eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 801*8eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 802*8eecd9ceSAlan Somers 803*8eecd9ceSAlan Somers for (i = 0; i < 3; i++) { 804*8eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 805*8eecd9ceSAlan Somers << strerror(errno); 806*8eecd9ceSAlan Somers } 807*8eecd9ceSAlan Somers close(fd); 808*8eecd9ceSAlan Somers } 809*8eecd9ceSAlan Somers 8109821f1d3SAlan Somers /* 8115fccbf31SAlan Somers * In writeback mode, writes to an O_WRONLY file could trigger reads from the 8125fccbf31SAlan Somers * server. The FUSE protocol explicitly allows that. 8135fccbf31SAlan Somers */ 8145fccbf31SAlan Somers TEST_F(WriteBack, rmw) 8155fccbf31SAlan Somers { 8165fccbf31SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8175fccbf31SAlan Somers const char RELPATH[] = "some_file.txt"; 8185fccbf31SAlan Somers const char *CONTENTS = "abcdefgh"; 8195fccbf31SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 8205fccbf31SAlan Somers uint64_t ino = 42; 8215fccbf31SAlan Somers uint64_t offset = 1; 8225fccbf31SAlan Somers off_t fsize = 10; 8235fccbf31SAlan Somers int fd; 8245fccbf31SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8255fccbf31SAlan Somers 826cad67791SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1); 8275fccbf31SAlan Somers expect_open(ino, 0, 1); 828d4fd0c81SAlan Somers expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY); 829bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS); 8305fccbf31SAlan Somers 8315fccbf31SAlan Somers fd = open(FULLPATH, O_WRONLY); 8325fccbf31SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 8335fccbf31SAlan Somers 8345fccbf31SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 8355fccbf31SAlan Somers << strerror(errno); 8365fccbf31SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 8375fccbf31SAlan Somers } 8385fccbf31SAlan Somers 8395fccbf31SAlan Somers /* 8409821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 8419821f1d3SAlan Somers */ 8429821f1d3SAlan Somers TEST_F(WriteBack, writeback) 8439821f1d3SAlan Somers { 8449821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8459821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8469821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 8479821f1d3SAlan Somers uint64_t ino = 42; 8489821f1d3SAlan Somers int fd; 8499821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8509821f1d3SAlan Somers char readbuf[bufsize]; 8519821f1d3SAlan Somers 8529821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 8539821f1d3SAlan Somers expect_open(ino, 0, 1); 854bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 8559821f1d3SAlan Somers 8569821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 8579821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 8589821f1d3SAlan Somers 8599821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 8609821f1d3SAlan Somers /* 8619821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 8629821f1d3SAlan Somers * filesystem daemon 8639821f1d3SAlan Somers */ 8649821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 8659821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 8669821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 8679821f1d3SAlan Somers } 8689821f1d3SAlan Somers 8699821f1d3SAlan Somers /* 8709821f1d3SAlan Somers * With O_DIRECT, writes should be not committed to cache. Admittedly this is 8719821f1d3SAlan Somers * an odd test, because it would be unusual to use O_DIRECT for writes but not 8729821f1d3SAlan Somers * reads. 8739821f1d3SAlan Somers */ 8749821f1d3SAlan Somers TEST_F(WriteBack, o_direct) 8759821f1d3SAlan Somers { 8769821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8779821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8789821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 8799821f1d3SAlan Somers uint64_t ino = 42; 8809821f1d3SAlan Somers int fd; 8819821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8829821f1d3SAlan Somers char readbuf[bufsize]; 8839821f1d3SAlan Somers 8849821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 8859821f1d3SAlan Somers expect_open(ino, 0, 1); 886bda39894SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE, 887bda39894SAlan Somers CONTENTS); 8889821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS); 8899821f1d3SAlan Somers 8909821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_DIRECT); 8919821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 8929821f1d3SAlan Somers 8939821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 8949821f1d3SAlan Somers /* A subsequent read must query the daemon because cache is empty */ 8959821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 8969821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 8979821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 8989821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 8999821f1d3SAlan Somers } 9009821f1d3SAlan Somers 9019821f1d3SAlan Somers /* 9029821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 9039821f1d3SAlan Somers */ 9049821f1d3SAlan Somers /* 9059821f1d3SAlan Somers * Disabled because we don't yet implement write-through caching. No bugzilla 9069821f1d3SAlan Somers * entry, because that's a feature request, not a bug. 9079821f1d3SAlan Somers */ 9089821f1d3SAlan Somers TEST_F(WriteThrough, DISABLED_writethrough) 9099821f1d3SAlan Somers { 9109821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9119821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 9129821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 9139821f1d3SAlan Somers uint64_t ino = 42; 9149821f1d3SAlan Somers int fd; 9159821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 9169821f1d3SAlan Somers char readbuf[bufsize]; 9179821f1d3SAlan Somers 9189821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 9199821f1d3SAlan Somers expect_open(ino, 0, 1); 920bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 9219821f1d3SAlan Somers 9229821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 9239821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 9249821f1d3SAlan Somers 9259821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 9269821f1d3SAlan Somers /* 9279821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 9289821f1d3SAlan Somers * filesystem daemon 9299821f1d3SAlan Somers */ 9309821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 9319821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 9329821f1d3SAlan Somers } 9339821f1d3SAlan Somers 9349821f1d3SAlan Somers /* With writethrough caching, writes update the cached file size */ 935cad67791SAlan Somers TEST_F(WriteThrough, update_file_size) 9369821f1d3SAlan Somers { 9379821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9389821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 9399821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 9409821f1d3SAlan Somers struct stat sb; 9419821f1d3SAlan Somers uint64_t ino = 42; 9429821f1d3SAlan Somers int fd; 9439821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 9449821f1d3SAlan Somers 9459821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 9469821f1d3SAlan Somers expect_open(ino, 0, 1); 947bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 9489821f1d3SAlan Somers 9499821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 9509821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 9519821f1d3SAlan Somers 9529821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 9539821f1d3SAlan Somers /* Get cached attributes */ 9549821f1d3SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 9559821f1d3SAlan Somers ASSERT_EQ(bufsize, sb.st_size); 9569821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 9579821f1d3SAlan Somers } 958