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> 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 9384879e46SAlan Somers /* Expect a write that may or may not come, depending on the cache mode */ 9484879e46SAlan Somers void maybe_expect_write(uint64_t ino, uint64_t offset, uint64_t size, 9584879e46SAlan Somers const void *contents) 9684879e46SAlan Somers { 9784879e46SAlan Somers EXPECT_CALL(*m_mock, process( 9884879e46SAlan Somers ResultOf([=](auto in) { 9984879e46SAlan Somers const char *buf = (const char*)in.body.bytes + 10084879e46SAlan Somers sizeof(struct fuse_write_in); 10184879e46SAlan Somers 10284879e46SAlan Somers return (in.header.opcode == FUSE_WRITE && 10384879e46SAlan Somers in.header.nodeid == ino && 10484879e46SAlan Somers in.body.write.offset == offset && 10584879e46SAlan Somers in.body.write.size == size && 10684879e46SAlan Somers 0 == bcmp(buf, contents, size)); 10784879e46SAlan Somers }, Eq(true)), 10884879e46SAlan Somers _) 10984879e46SAlan Somers ).Times(AtMost(1)) 11084879e46SAlan Somers .WillRepeatedly(Invoke( 11184879e46SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 11284879e46SAlan Somers SET_OUT_HEADER_LEN(out, write); 11384879e46SAlan Somers out.body.write.size = size; 11484879e46SAlan Somers }) 11584879e46SAlan Somers )); 11684879e46SAlan Somers } 11784879e46SAlan Somers 1189821f1d3SAlan Somers }; 1199821f1d3SAlan Somers 1206fa772a8SAlan Somers class WriteCacheable: public Write { 1216fa772a8SAlan Somers public: 1226fa772a8SAlan Somers virtual void SetUp() { 1236fa772a8SAlan Somers const char *node = "vfs.fusefs.data_cache_mode"; 1246fa772a8SAlan Somers int val = 0; 1256fa772a8SAlan Somers size_t size = sizeof(val); 1266fa772a8SAlan Somers 1276fa772a8SAlan Somers FuseTest::SetUp(); 1286fa772a8SAlan Somers 1296fa772a8SAlan Somers ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) 1306fa772a8SAlan Somers << strerror(errno); 1316fa772a8SAlan Somers if (val == 0) 1326fa772a8SAlan Somers GTEST_SKIP() << 1336fa772a8SAlan Somers "fusefs data caching must be enabled for this test"; 1346fa772a8SAlan Somers } 1356fa772a8SAlan Somers }; 1366fa772a8SAlan Somers 137a639731bSAlan Somers sig_atomic_t Write::s_sigxfsz = 0; 138a639731bSAlan Somers 13916bd2d47SAlan Somers class Write_7_8: public FuseTest { 14016bd2d47SAlan Somers 14116bd2d47SAlan Somers public: 14216bd2d47SAlan Somers virtual void SetUp() { 14316bd2d47SAlan Somers m_kernel_minor_version = 8; 14416bd2d47SAlan Somers FuseTest::SetUp(); 14516bd2d47SAlan Somers } 14616bd2d47SAlan Somers 14716bd2d47SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, uint64_t size) 14816bd2d47SAlan Somers { 14916bd2d47SAlan Somers FuseTest::expect_lookup_7_8(relpath, ino, S_IFREG | 0644, size, 1); 15016bd2d47SAlan Somers } 15116bd2d47SAlan Somers 15216bd2d47SAlan Somers }; 15316bd2d47SAlan Somers 1549821f1d3SAlan Somers class AioWrite: public Write { 1559821f1d3SAlan Somers virtual void SetUp() { 1569821f1d3SAlan Somers const char *node = "vfs.aio.enable_unsafe"; 1579821f1d3SAlan Somers int val = 0; 1589821f1d3SAlan Somers size_t size = sizeof(val); 1599821f1d3SAlan Somers 1609821f1d3SAlan Somers FuseTest::SetUp(); 1619821f1d3SAlan Somers 1629821f1d3SAlan Somers ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) 1639821f1d3SAlan Somers << strerror(errno); 1649821f1d3SAlan Somers if (!val) 1659821f1d3SAlan Somers GTEST_SKIP() << 1669821f1d3SAlan Somers "vfs.aio.enable_unsafe must be set for this test"; 1679821f1d3SAlan Somers } 1689821f1d3SAlan Somers }; 1699821f1d3SAlan Somers 1709821f1d3SAlan Somers /* Tests for the write-through cache mode */ 1719821f1d3SAlan Somers class WriteThrough: public Write { 172bda39894SAlan Somers public: 1739821f1d3SAlan Somers virtual void SetUp() { 1749821f1d3SAlan Somers const char *cache_mode_node = "vfs.fusefs.data_cache_mode"; 1759821f1d3SAlan Somers int val = 0; 1769821f1d3SAlan Somers size_t size = sizeof(val); 1779821f1d3SAlan Somers 1789821f1d3SAlan Somers FuseTest::SetUp(); 1799821f1d3SAlan Somers if (IsSkipped()) 1809821f1d3SAlan Somers return; 1819821f1d3SAlan Somers 1829821f1d3SAlan Somers ASSERT_EQ(0, sysctlbyname(cache_mode_node, &val, &size, NULL, 0)) 1839821f1d3SAlan Somers << strerror(errno); 1849821f1d3SAlan Somers if (val != 1) 1859821f1d3SAlan Somers GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 1 " 1869821f1d3SAlan Somers "(writethrough) for this test"; 1879821f1d3SAlan Somers } 1889821f1d3SAlan Somers 189bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 190bda39894SAlan Somers uint64_t osize, const void *contents) 191bda39894SAlan Somers { 192bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, 0, FUSE_WRITE_CACHE, 193bda39894SAlan Somers contents); 194bda39894SAlan Somers } 1959821f1d3SAlan Somers }; 1969821f1d3SAlan Somers 1979821f1d3SAlan Somers /* Tests for the writeback cache mode */ 1989821f1d3SAlan Somers class WriteBack: public Write { 199bda39894SAlan Somers public: 2009821f1d3SAlan Somers virtual void SetUp() { 2019821f1d3SAlan Somers const char *node = "vfs.fusefs.data_cache_mode"; 2029821f1d3SAlan Somers int val = 0; 2039821f1d3SAlan Somers size_t size = sizeof(val); 2049821f1d3SAlan Somers 2059821f1d3SAlan Somers FuseTest::SetUp(); 2069821f1d3SAlan Somers if (IsSkipped()) 2079821f1d3SAlan Somers return; 2089821f1d3SAlan Somers 2099821f1d3SAlan Somers ASSERT_EQ(0, sysctlbyname(node, &val, &size, NULL, 0)) 2109821f1d3SAlan Somers << strerror(errno); 2119821f1d3SAlan Somers if (val != 2) 2129821f1d3SAlan Somers GTEST_SKIP() << "vfs.fusefs.data_cache_mode must be set to 2 " 2139821f1d3SAlan Somers "(writeback) for this test"; 2149821f1d3SAlan Somers } 2159821f1d3SAlan Somers 216bda39894SAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 217bda39894SAlan Somers uint64_t osize, const void *contents) 218bda39894SAlan Somers { 219bda39894SAlan Somers FuseTest::expect_write(ino, offset, isize, osize, FUSE_WRITE_CACHE, 0, 220bda39894SAlan Somers contents); 221bda39894SAlan Somers } 2229821f1d3SAlan Somers }; 2239821f1d3SAlan Somers 22484879e46SAlan Somers class WriteBackAsync: public WriteBack { 22584879e46SAlan Somers public: 22684879e46SAlan Somers virtual void SetUp() { 22784879e46SAlan Somers m_async = true; 22884879e46SAlan Somers WriteBack::SetUp(); 22984879e46SAlan Somers } 23084879e46SAlan Somers }; 23184879e46SAlan Somers 2328eecd9ceSAlan Somers /* Tests for clustered writes with WriteBack cacheing */ 2338eecd9ceSAlan Somers class WriteCluster: public WriteBack { 2348eecd9ceSAlan Somers public: 2358eecd9ceSAlan Somers virtual void SetUp() { 2368eecd9ceSAlan Somers if (MAXPHYS < 2 * DFLTPHYS) 2378eecd9ceSAlan Somers GTEST_SKIP() << "MAXPHYS must be at least twice DFLTPHYS" 2388eecd9ceSAlan Somers << "for this test"; 2398eecd9ceSAlan Somers m_async = true; 2408eecd9ceSAlan Somers m_maxwrite = MAXPHYS; 2418eecd9ceSAlan Somers WriteBack::SetUp(); 2428eecd9ceSAlan Somers } 2438eecd9ceSAlan Somers }; 2448eecd9ceSAlan Somers 245a639731bSAlan Somers void sigxfsz_handler(int __unused sig) { 246a639731bSAlan Somers Write::s_sigxfsz = 1; 247a639731bSAlan Somers } 248a639731bSAlan Somers 2499821f1d3SAlan Somers /* AIO writes need to set the header's pid field correctly */ 2509821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ 2519821f1d3SAlan Somers TEST_F(AioWrite, DISABLED_aio_write) 2529821f1d3SAlan Somers { 2539821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2549821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2559821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 2569821f1d3SAlan Somers uint64_t ino = 42; 2579821f1d3SAlan Somers uint64_t offset = 4096; 2589821f1d3SAlan Somers int fd; 2599821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 2609821f1d3SAlan Somers struct aiocb iocb, *piocb; 2619821f1d3SAlan Somers 2629821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 2639821f1d3SAlan Somers expect_open(ino, 0, 1); 264bda39894SAlan Somers expect_write(ino, offset, bufsize, bufsize, CONTENTS); 2659821f1d3SAlan Somers 2669821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 2679821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 2689821f1d3SAlan Somers 2699821f1d3SAlan Somers iocb.aio_nbytes = bufsize; 2709821f1d3SAlan Somers iocb.aio_fildes = fd; 2719821f1d3SAlan Somers iocb.aio_buf = (void *)CONTENTS; 2729821f1d3SAlan Somers iocb.aio_offset = offset; 2739821f1d3SAlan Somers iocb.aio_sigevent.sigev_notify = SIGEV_NONE; 2749821f1d3SAlan Somers ASSERT_EQ(0, aio_write(&iocb)) << strerror(errno); 2759821f1d3SAlan Somers ASSERT_EQ(bufsize, aio_waitcomplete(&piocb, NULL)) << strerror(errno); 2769821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 2779821f1d3SAlan Somers } 2789821f1d3SAlan Somers 2799821f1d3SAlan Somers /* 2809821f1d3SAlan Somers * When a file is opened with O_APPEND, we should forward that flag to 2819821f1d3SAlan Somers * FUSE_OPEN (tested by Open.o_append) but still attempt to calculate the 2829821f1d3SAlan Somers * offset internally. That way we'll work both with filesystems that 2839821f1d3SAlan Somers * understand O_APPEND (and ignore the offset) and filesystems that don't (and 2849821f1d3SAlan Somers * simply use the offset). 2859821f1d3SAlan Somers * 2869821f1d3SAlan Somers * Note that verifying the O_APPEND flag in FUSE_OPEN is done in the 2879821f1d3SAlan Somers * Open.o_append test. 2889821f1d3SAlan Somers */ 2899821f1d3SAlan Somers TEST_F(Write, append) 2909821f1d3SAlan Somers { 2919821f1d3SAlan Somers const ssize_t BUFSIZE = 9; 2929821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2939821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2949821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 2959821f1d3SAlan Somers uint64_t ino = 42; 2969821f1d3SAlan Somers /* 2979821f1d3SAlan Somers * Set offset to a maxbcachebuf boundary so we don't need to RMW when 2989821f1d3SAlan Somers * using writeback caching 2999821f1d3SAlan Somers */ 3009821f1d3SAlan Somers uint64_t initial_offset = m_maxbcachebuf; 3019821f1d3SAlan Somers int fd; 3029821f1d3SAlan Somers 3039821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset); 3049821f1d3SAlan Somers expect_open(ino, 0, 1); 305bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS); 3069821f1d3SAlan Somers 3079821f1d3SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ 3089821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND); 3099821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 3109821f1d3SAlan Somers 3119821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 3129821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 3139821f1d3SAlan Somers } 3149821f1d3SAlan Somers 315a87e0831SAlan Somers /* If a file is cached, then appending to the end should not cause a read */ 316a87e0831SAlan Somers TEST_F(Write, append_to_cached) 317a87e0831SAlan Somers { 318a87e0831SAlan Somers const ssize_t BUFSIZE = 9; 319a87e0831SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 320a87e0831SAlan Somers const char RELPATH[] = "some_file.txt"; 321a87e0831SAlan Somers char *oldcontents, *oldbuf; 322a87e0831SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 323a87e0831SAlan Somers uint64_t ino = 42; 324a87e0831SAlan Somers /* 325a87e0831SAlan Somers * Set offset in between maxbcachebuf boundary to test buffer handling 326a87e0831SAlan Somers */ 327a87e0831SAlan Somers uint64_t oldsize = m_maxbcachebuf / 2; 328a87e0831SAlan Somers int fd; 329a87e0831SAlan Somers 330a87e0831SAlan Somers oldcontents = (char*)calloc(1, oldsize); 331a87e0831SAlan Somers ASSERT_NE(NULL, oldcontents) << strerror(errno); 332a87e0831SAlan Somers oldbuf = (char*)malloc(oldsize); 333a87e0831SAlan Somers ASSERT_NE(NULL, oldbuf) << strerror(errno); 334a87e0831SAlan Somers 335a87e0831SAlan Somers expect_lookup(RELPATH, ino, oldsize); 336a87e0831SAlan Somers expect_open(ino, 0, 1); 337a87e0831SAlan Somers expect_read(ino, 0, oldsize, oldsize, oldcontents); 33884879e46SAlan Somers maybe_expect_write(ino, oldsize, BUFSIZE, CONTENTS); 339a87e0831SAlan Somers 340a87e0831SAlan Somers /* Must open O_RDWR or fuse(4) implicitly sets direct_io */ 341a87e0831SAlan Somers fd = open(FULLPATH, O_RDWR | O_APPEND); 342a87e0831SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 343a87e0831SAlan Somers 344a87e0831SAlan Somers /* Read the old data into the cache */ 345a87e0831SAlan Somers ASSERT_EQ((ssize_t)oldsize, read(fd, oldbuf, oldsize)) 346a87e0831SAlan Somers << strerror(errno); 347a87e0831SAlan Somers 348a87e0831SAlan Somers /* Write the new data. There should be no more read operations */ 349a87e0831SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 350a87e0831SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 351a87e0831SAlan Somers } 352a87e0831SAlan Somers 3539821f1d3SAlan Somers TEST_F(Write, append_direct_io) 3549821f1d3SAlan Somers { 3559821f1d3SAlan Somers const ssize_t BUFSIZE = 9; 3569821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3579821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3589821f1d3SAlan Somers const char CONTENTS[BUFSIZE] = "abcdefgh"; 3599821f1d3SAlan Somers uint64_t ino = 42; 3609821f1d3SAlan Somers uint64_t initial_offset = 4096; 3619821f1d3SAlan Somers int fd; 3629821f1d3SAlan Somers 3639821f1d3SAlan Somers expect_lookup(RELPATH, ino, initial_offset); 3649821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 365bda39894SAlan Somers expect_write(ino, initial_offset, BUFSIZE, BUFSIZE, CONTENTS); 3669821f1d3SAlan Somers 3679821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY | O_APPEND); 3689821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 3699821f1d3SAlan Somers 3709821f1d3SAlan Somers ASSERT_EQ(BUFSIZE, write(fd, CONTENTS, BUFSIZE)) << strerror(errno); 3719821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 3729821f1d3SAlan Somers } 3739821f1d3SAlan Somers 3749821f1d3SAlan Somers /* A direct write should evict any overlapping cached data */ 3756af6fdceSAlan Somers TEST_F(Write, direct_io_evicts_cache) 3769821f1d3SAlan Somers { 3779821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3789821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 3799821f1d3SAlan Somers const char CONTENTS0[] = "abcdefgh"; 3809821f1d3SAlan Somers const char CONTENTS1[] = "ijklmnop"; 3819821f1d3SAlan Somers uint64_t ino = 42; 3829821f1d3SAlan Somers int fd; 3839821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS0) + 1; 3849821f1d3SAlan Somers char readbuf[bufsize]; 3859821f1d3SAlan Somers 3869821f1d3SAlan Somers expect_lookup(RELPATH, ino, bufsize); 3879821f1d3SAlan Somers expect_open(ino, 0, 1); 3889821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS0); 389bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS1); 3909821f1d3SAlan Somers 3919821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 3929821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 3939821f1d3SAlan Somers 3949821f1d3SAlan Somers // Prime cache 3959821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 3969821f1d3SAlan Somers 3979821f1d3SAlan Somers // Write directly, evicting cache 3989821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, O_DIRECT)) << strerror(errno); 3999821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 4009821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno); 4019821f1d3SAlan Somers 4029821f1d3SAlan Somers // Read again. Cache should be bypassed 4039821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS1); 4049821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 4059821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 4069821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 4079821f1d3SAlan Somers ASSERT_STREQ(readbuf, CONTENTS1); 4089821f1d3SAlan Somers 4099821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 4109821f1d3SAlan Somers } 4119821f1d3SAlan Somers 4129821f1d3SAlan Somers /* 41312292a99SAlan Somers * If the server doesn't return FOPEN_DIRECT_IO during FUSE_OPEN, then it's not 41412292a99SAlan Somers * allowed to return a short write for that file handle. However, if it does 41512292a99SAlan Somers * then we should still do our darndest to handle it by resending the unwritten 41612292a99SAlan Somers * portion. 4179821f1d3SAlan Somers */ 41812292a99SAlan Somers TEST_F(Write, indirect_io_short_write) 4199821f1d3SAlan Somers { 4209821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4219821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 4229821f1d3SAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 4239821f1d3SAlan Somers uint64_t ino = 42; 4249821f1d3SAlan Somers int fd; 4259821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 42612292a99SAlan Somers ssize_t bufsize0 = 11; 42712292a99SAlan Somers ssize_t bufsize1 = strlen(CONTENTS) - bufsize0; 42812292a99SAlan Somers const char *contents1 = CONTENTS + bufsize0; 4299821f1d3SAlan Somers 4309821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 43112292a99SAlan Somers expect_open(ino, 0, 1); 432bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize0, CONTENTS); 433bda39894SAlan Somers expect_write(ino, bufsize0, bufsize1, bufsize1, contents1); 4349821f1d3SAlan Somers 4359821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 4369821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 4379821f1d3SAlan Somers 4389821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 4399821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 4409821f1d3SAlan Somers } 4419821f1d3SAlan Somers 4429821f1d3SAlan Somers /* 44312292a99SAlan Somers * When the direct_io option is used, filesystems are allowed to write less 44412292a99SAlan Somers * data than requested. We should return the short write to userland. 44512292a99SAlan Somers */ 44612292a99SAlan Somers TEST_F(Write, direct_io_short_write) 44712292a99SAlan Somers { 44812292a99SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 44912292a99SAlan Somers const char RELPATH[] = "some_file.txt"; 45012292a99SAlan Somers const char *CONTENTS = "abcdefghijklmnop"; 45112292a99SAlan Somers uint64_t ino = 42; 45212292a99SAlan Somers int fd; 45312292a99SAlan Somers ssize_t bufsize = strlen(CONTENTS); 45412292a99SAlan Somers ssize_t halfbufsize = bufsize / 2; 45512292a99SAlan Somers 45612292a99SAlan Somers expect_lookup(RELPATH, ino, 0); 45712292a99SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 458bda39894SAlan Somers expect_write(ino, 0, bufsize, halfbufsize, CONTENTS); 45912292a99SAlan Somers 46012292a99SAlan Somers fd = open(FULLPATH, O_WRONLY); 46112292a99SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 46212292a99SAlan Somers 46312292a99SAlan Somers ASSERT_EQ(halfbufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 46412292a99SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 46512292a99SAlan Somers } 46612292a99SAlan Somers 46712292a99SAlan Somers /* 4689821f1d3SAlan Somers * An insidious edge case: the filesystem returns a short write, and the 4699821f1d3SAlan Somers * difference between what we requested and what it actually wrote crosses an 4709821f1d3SAlan Somers * iov element boundary 4719821f1d3SAlan Somers */ 47212292a99SAlan Somers TEST_F(Write, direct_io_short_write_iov) 4739821f1d3SAlan Somers { 4749821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4759821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 4769821f1d3SAlan Somers const char *CONTENTS0 = "abcdefgh"; 4779821f1d3SAlan Somers const char *CONTENTS1 = "ijklmnop"; 4789821f1d3SAlan Somers const char *EXPECTED0 = "abcdefghijklmnop"; 4799821f1d3SAlan Somers uint64_t ino = 42; 4809821f1d3SAlan Somers int fd; 4819821f1d3SAlan Somers ssize_t size0 = strlen(CONTENTS0) - 1; 4829821f1d3SAlan Somers ssize_t size1 = strlen(CONTENTS1) + 1; 4839821f1d3SAlan Somers ssize_t totalsize = size0 + size1; 4849821f1d3SAlan Somers struct iovec iov[2]; 4859821f1d3SAlan Somers 4869821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 4879821f1d3SAlan Somers expect_open(ino, FOPEN_DIRECT_IO, 1); 488bda39894SAlan Somers expect_write(ino, 0, totalsize, size0, EXPECTED0); 4899821f1d3SAlan Somers 4909821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 4919821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 4929821f1d3SAlan Somers 4939821f1d3SAlan Somers iov[0].iov_base = (void*)CONTENTS0; 4949821f1d3SAlan Somers iov[0].iov_len = strlen(CONTENTS0); 4959821f1d3SAlan Somers iov[1].iov_base = (void*)CONTENTS1; 4969821f1d3SAlan Somers iov[1].iov_len = strlen(CONTENTS1); 49712292a99SAlan Somers ASSERT_EQ(size0, writev(fd, iov, 2)) << strerror(errno); 4989821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 4999821f1d3SAlan Somers } 5009821f1d3SAlan Somers 501a639731bSAlan Somers /* fusefs should respect RLIMIT_FSIZE */ 502a639731bSAlan Somers TEST_F(Write, rlimit_fsize) 503a639731bSAlan Somers { 504a639731bSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 505a639731bSAlan Somers const char RELPATH[] = "some_file.txt"; 506a639731bSAlan Somers const char *CONTENTS = "abcdefgh"; 507a639731bSAlan Somers struct rlimit rl; 508a639731bSAlan Somers ssize_t bufsize = strlen(CONTENTS); 509a639731bSAlan Somers off_t offset = 1'000'000'000; 510a639731bSAlan Somers uint64_t ino = 42; 511a639731bSAlan Somers int fd; 512a639731bSAlan Somers 513a639731bSAlan Somers expect_lookup(RELPATH, ino, 0); 514a639731bSAlan Somers expect_open(ino, 0, 1); 515a639731bSAlan Somers 516a639731bSAlan Somers rl.rlim_cur = offset; 517a639731bSAlan Somers rl.rlim_max = 10 * offset; 518a639731bSAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno); 519a639731bSAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno); 520a639731bSAlan Somers 521a639731bSAlan Somers fd = open(FULLPATH, O_WRONLY); 522a639731bSAlan Somers 523a639731bSAlan Somers EXPECT_LE(0, fd) << strerror(errno); 524a639731bSAlan Somers 525a639731bSAlan Somers ASSERT_EQ(-1, pwrite(fd, CONTENTS, bufsize, offset)); 526a639731bSAlan Somers EXPECT_EQ(EFBIG, errno); 527a639731bSAlan Somers EXPECT_EQ(1, s_sigxfsz); 528a639731bSAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 529a639731bSAlan Somers } 530a639731bSAlan Somers 5319821f1d3SAlan Somers /* 532*b9e20197SAlan Somers * A short read indicates EOF. Test that nothing bad happens if we get EOF 533*b9e20197SAlan Somers * during the R of a RMW operation. 534*b9e20197SAlan Somers */ 535*b9e20197SAlan Somers TEST_F(WriteCacheable, eof_during_rmw) 536*b9e20197SAlan Somers { 537*b9e20197SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 538*b9e20197SAlan Somers const char RELPATH[] = "some_file.txt"; 539*b9e20197SAlan Somers const char *CONTENTS = "abcdefgh"; 540*b9e20197SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 541*b9e20197SAlan Somers uint64_t ino = 42; 542*b9e20197SAlan Somers uint64_t offset = 1; 543*b9e20197SAlan Somers ssize_t bufsize = strlen(CONTENTS); 544*b9e20197SAlan Somers off_t orig_fsize = 10; 545*b9e20197SAlan Somers off_t truncated_fsize = 5; 546*b9e20197SAlan Somers off_t final_fsize = bufsize; 547*b9e20197SAlan Somers int fd; 548*b9e20197SAlan Somers 549*b9e20197SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, orig_fsize, 1); 550*b9e20197SAlan Somers expect_open(ino, 0, 1); 551*b9e20197SAlan Somers expect_read(ino, 0, orig_fsize, truncated_fsize, INITIAL, O_RDWR); 552*b9e20197SAlan Somers expect_getattr(ino, truncated_fsize); 553*b9e20197SAlan Somers expect_read(ino, 0, final_fsize, final_fsize, INITIAL, O_RDWR); 554*b9e20197SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS); 555*b9e20197SAlan Somers 556*b9e20197SAlan Somers fd = open(FULLPATH, O_RDWR); 557*b9e20197SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 558*b9e20197SAlan Somers 559*b9e20197SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 560*b9e20197SAlan Somers << strerror(errno); 561*b9e20197SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 562*b9e20197SAlan Somers } 563*b9e20197SAlan Somers 564*b9e20197SAlan Somers /* 5659821f1d3SAlan Somers * If the kernel cannot be sure which uid, gid, or pid was responsible for a 5669821f1d3SAlan Somers * write, then it must set the FUSE_WRITE_CACHE bit 5679821f1d3SAlan Somers */ 5689821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236378 */ 5696fa772a8SAlan Somers TEST_F(WriteCacheable, mmap) 5709821f1d3SAlan Somers { 5719821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 5729821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 5739821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 5749821f1d3SAlan Somers uint64_t ino = 42; 5759821f1d3SAlan Somers int fd; 5769821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 5779821f1d3SAlan Somers void *p; 5789821f1d3SAlan Somers uint64_t offset = 10; 5799821f1d3SAlan Somers size_t len; 5809821f1d3SAlan Somers void *zeros, *expected; 5819821f1d3SAlan Somers 5829821f1d3SAlan Somers len = getpagesize(); 5839821f1d3SAlan Somers 5849821f1d3SAlan Somers zeros = calloc(1, len); 5859821f1d3SAlan Somers ASSERT_NE(NULL, zeros); 5869821f1d3SAlan Somers expected = calloc(1, len); 5879821f1d3SAlan Somers ASSERT_NE(NULL, expected); 5889821f1d3SAlan Somers memmove((uint8_t*)expected + offset, CONTENTS, bufsize); 5899821f1d3SAlan Somers 5909821f1d3SAlan Somers expect_lookup(RELPATH, ino, len); 5919821f1d3SAlan Somers expect_open(ino, 0, 1); 5929821f1d3SAlan Somers expect_read(ino, 0, len, len, zeros); 5939821f1d3SAlan Somers /* 5949821f1d3SAlan Somers * Writes from the pager may or may not be associated with the correct 595cf437e2aSAlan Somers * pid, so they must set FUSE_WRITE_CACHE. 5969821f1d3SAlan Somers */ 597bda39894SAlan Somers FuseTest::expect_write(ino, 0, len, len, FUSE_WRITE_CACHE, 0, expected); 5989f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 5999821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 6009821f1d3SAlan Somers 6019821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 6029821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6039821f1d3SAlan Somers 6049821f1d3SAlan Somers p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 6059821f1d3SAlan Somers ASSERT_NE(MAP_FAILED, p) << strerror(errno); 6069821f1d3SAlan Somers 6079821f1d3SAlan Somers memmove((uint8_t*)p + offset, CONTENTS, bufsize); 6089821f1d3SAlan Somers 6099821f1d3SAlan Somers ASSERT_EQ(0, munmap(p, len)) << strerror(errno); 6109821f1d3SAlan Somers close(fd); // Write mmap'd data on close 6119821f1d3SAlan Somers 6129821f1d3SAlan Somers free(expected); 6139821f1d3SAlan Somers free(zeros); 6149821f1d3SAlan Somers } 6159821f1d3SAlan Somers 6165fccbf31SAlan Somers TEST_F(WriteThrough, pwrite) 6179821f1d3SAlan Somers { 6189821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6199821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6209821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 6219821f1d3SAlan Somers uint64_t ino = 42; 622b5aaf286SAlan Somers uint64_t offset = 65536; 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, offset, 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, pwrite(fd, CONTENTS, bufsize, offset)) 6349821f1d3SAlan Somers << strerror(errno); 6359821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6369821f1d3SAlan Somers } 6379821f1d3SAlan Somers 6389821f1d3SAlan Somers TEST_F(Write, write) 6399821f1d3SAlan Somers { 6409821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6419821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6429821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 6439821f1d3SAlan Somers uint64_t ino = 42; 6449821f1d3SAlan Somers int fd; 6459821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 6469821f1d3SAlan Somers 6479821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6489821f1d3SAlan Somers expect_open(ino, 0, 1); 649bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 6509821f1d3SAlan Somers 6519821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 6529821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6539821f1d3SAlan Somers 6549821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 6559821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6569821f1d3SAlan Somers } 6579821f1d3SAlan Somers 6589821f1d3SAlan Somers /* fuse(4) should not issue writes of greater size than the daemon requests */ 6599821f1d3SAlan Somers TEST_F(Write, write_large) 6609821f1d3SAlan Somers { 6619821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6629821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6639821f1d3SAlan Somers int *contents; 6649821f1d3SAlan Somers uint64_t ino = 42; 6659821f1d3SAlan Somers int fd; 6669821f1d3SAlan Somers ssize_t halfbufsize, bufsize; 6679821f1d3SAlan Somers 6688eecd9ceSAlan Somers halfbufsize = m_mock->m_maxwrite; 6699821f1d3SAlan Somers bufsize = halfbufsize * 2; 6709821f1d3SAlan Somers contents = (int*)malloc(bufsize); 6719821f1d3SAlan Somers ASSERT_NE(NULL, contents); 6729821f1d3SAlan Somers for (int i = 0; i < (int)bufsize / (int)sizeof(i); i++) { 6739821f1d3SAlan Somers contents[i] = i; 6749821f1d3SAlan Somers } 6759821f1d3SAlan Somers 6769821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 6779821f1d3SAlan Somers expect_open(ino, 0, 1); 678bda39894SAlan Somers expect_write(ino, 0, halfbufsize, halfbufsize, contents); 67984879e46SAlan Somers maybe_expect_write(ino, halfbufsize, halfbufsize, 6809821f1d3SAlan Somers &contents[halfbufsize / sizeof(int)]); 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); 6869821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6879821f1d3SAlan Somers 6889821f1d3SAlan Somers free(contents); 6899821f1d3SAlan Somers } 6909821f1d3SAlan Somers 6919821f1d3SAlan Somers TEST_F(Write, write_nothing) 6929821f1d3SAlan Somers { 6939821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6949821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6959821f1d3SAlan Somers const char *CONTENTS = ""; 6969821f1d3SAlan Somers uint64_t ino = 42; 6979821f1d3SAlan Somers int fd; 6989821f1d3SAlan Somers ssize_t bufsize = 0; 6999821f1d3SAlan Somers 7009821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 7019821f1d3SAlan Somers expect_open(ino, 0, 1); 7029821f1d3SAlan Somers 7039821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 7049821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 7059821f1d3SAlan Somers 7069821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 7079821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 7089821f1d3SAlan Somers } 7099821f1d3SAlan Somers 71016bd2d47SAlan Somers TEST_F(Write_7_8, write) 71116bd2d47SAlan Somers { 71216bd2d47SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 71316bd2d47SAlan Somers const char RELPATH[] = "some_file.txt"; 71416bd2d47SAlan Somers const char *CONTENTS = "abcdefgh"; 71516bd2d47SAlan Somers uint64_t ino = 42; 71616bd2d47SAlan Somers int fd; 71716bd2d47SAlan Somers ssize_t bufsize = strlen(CONTENTS); 71816bd2d47SAlan Somers 71916bd2d47SAlan Somers expect_lookup(RELPATH, ino, 0); 72016bd2d47SAlan Somers expect_open(ino, 0, 1); 721bda39894SAlan Somers expect_write_7_8(ino, 0, bufsize, bufsize, CONTENTS); 72216bd2d47SAlan Somers 72316bd2d47SAlan Somers fd = open(FULLPATH, O_WRONLY); 72416bd2d47SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 72516bd2d47SAlan Somers 72616bd2d47SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 72716bd2d47SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 72816bd2d47SAlan Somers } 72916bd2d47SAlan Somers 7309821f1d3SAlan Somers /* In writeback mode, dirty data should be written on close */ 73184879e46SAlan Somers TEST_F(WriteBackAsync, close) 7329821f1d3SAlan Somers { 7339821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7349821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7359821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 7369821f1d3SAlan Somers uint64_t ino = 42; 7379821f1d3SAlan Somers int fd; 7389821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 7399821f1d3SAlan Somers 7409821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 7419821f1d3SAlan Somers expect_open(ino, 0, 1); 742bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 7439821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 7449821f1d3SAlan Somers ResultOf([=](auto in) { 74529edc611SAlan Somers return (in.header.opcode == FUSE_SETATTR); 7469821f1d3SAlan Somers }, Eq(true)), 7479821f1d3SAlan Somers _) 74829edc611SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 7499821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, attr); 75029edc611SAlan Somers out.body.attr.attr.ino = ino; // Must match nodeid 7519821f1d3SAlan Somers }))); 7529f10f423SAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 7539821f1d3SAlan Somers expect_release(ino, ReturnErrno(0)); 7549821f1d3SAlan Somers 7559821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 7569821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7579821f1d3SAlan Somers 7589821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 7599821f1d3SAlan Somers close(fd); 7609821f1d3SAlan Somers } 7619821f1d3SAlan Somers 7628eecd9ceSAlan Somers /* In writeback mode, adjacent writes will be clustered together */ 7638eecd9ceSAlan Somers TEST_F(WriteCluster, clustering) 7648eecd9ceSAlan Somers { 7658eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7668eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 7678eecd9ceSAlan Somers uint64_t ino = 42; 7688eecd9ceSAlan Somers int i, fd; 7698eecd9ceSAlan Somers void *wbuf, *wbuf2x; 7708eecd9ceSAlan Somers ssize_t bufsize = 65536; 7718eecd9ceSAlan Somers off_t filesize = 327680; 7728eecd9ceSAlan Somers 7738eecd9ceSAlan Somers wbuf = malloc(bufsize); 7748eecd9ceSAlan Somers ASSERT_NE(NULL, wbuf) << strerror(errno); 7758eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 7768eecd9ceSAlan Somers wbuf2x = malloc(2 * bufsize); 7778eecd9ceSAlan Somers ASSERT_NE(NULL, wbuf2x) << strerror(errno); 7788eecd9ceSAlan Somers memset(wbuf2x, 'X', 2 * bufsize); 7798eecd9ceSAlan Somers 7808eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 7818eecd9ceSAlan Somers expect_open(ino, 0, 1); 7828eecd9ceSAlan Somers /* 7838eecd9ceSAlan Somers * Writes of bufsize-bytes each should be clustered into greater sizes. 7848eecd9ceSAlan Somers * The amount of clustering is adaptive, so the first write actually 7858eecd9ceSAlan Somers * issued will be 2x bufsize and subsequent writes may be larger 7868eecd9ceSAlan Somers */ 7878eecd9ceSAlan Somers expect_write(ino, 0, 2 * bufsize, 2 * bufsize, wbuf2x); 7888eecd9ceSAlan Somers expect_write(ino, 2 * bufsize, 2 * bufsize, 2 * bufsize, wbuf2x); 7898eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 7908eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 7918eecd9ceSAlan Somers 7928eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 7938eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 7948eecd9ceSAlan Somers 7958eecd9ceSAlan Somers for (i = 0; i < 4; i++) { 7968eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 7978eecd9ceSAlan Somers << strerror(errno); 7988eecd9ceSAlan Somers } 7998eecd9ceSAlan Somers close(fd); 8008eecd9ceSAlan Somers } 8018eecd9ceSAlan Somers 8028eecd9ceSAlan Somers /* 8038eecd9ceSAlan Somers * When clustering writes, an I/O error to any of the cluster's children should 8048eecd9ceSAlan Somers * not panic the system on unmount 8058eecd9ceSAlan Somers */ 8068eecd9ceSAlan Somers /* 8078eecd9ceSAlan Somers * Disabled because it panics. 8088eecd9ceSAlan Somers * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=238565 8098eecd9ceSAlan Somers */ 8108eecd9ceSAlan Somers TEST_F(WriteCluster, DISABLED_cluster_write_err) 8118eecd9ceSAlan Somers { 8128eecd9ceSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8138eecd9ceSAlan Somers const char RELPATH[] = "some_file.txt"; 8148eecd9ceSAlan Somers uint64_t ino = 42; 8158eecd9ceSAlan Somers int i, fd; 8168eecd9ceSAlan Somers void *wbuf; 8178eecd9ceSAlan Somers ssize_t bufsize = 65536; 8188eecd9ceSAlan Somers off_t filesize = 262144; 8198eecd9ceSAlan Somers 8208eecd9ceSAlan Somers wbuf = malloc(bufsize); 8218eecd9ceSAlan Somers ASSERT_NE(NULL, wbuf) << strerror(errno); 8228eecd9ceSAlan Somers memset(wbuf, 'X', bufsize); 8238eecd9ceSAlan Somers 8248eecd9ceSAlan Somers expect_lookup(RELPATH, ino, filesize); 8258eecd9ceSAlan Somers expect_open(ino, 0, 1); 8268eecd9ceSAlan Somers EXPECT_CALL(*m_mock, process( 8278eecd9ceSAlan Somers ResultOf([=](auto in) { 8288eecd9ceSAlan Somers return (in.header.opcode == FUSE_WRITE); 8298eecd9ceSAlan Somers }, Eq(true)), 8308eecd9ceSAlan Somers _) 8318eecd9ceSAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(EIO))); 8328eecd9ceSAlan Somers expect_flush(ino, 1, ReturnErrno(0)); 8338eecd9ceSAlan Somers expect_release(ino, ReturnErrno(0)); 8348eecd9ceSAlan Somers 8358eecd9ceSAlan Somers fd = open(FULLPATH, O_RDWR); 8368eecd9ceSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 8378eecd9ceSAlan Somers 8388eecd9ceSAlan Somers for (i = 0; i < 3; i++) { 8398eecd9ceSAlan Somers ASSERT_EQ(bufsize, write(fd, wbuf, bufsize)) 8408eecd9ceSAlan Somers << strerror(errno); 8418eecd9ceSAlan Somers } 8428eecd9ceSAlan Somers close(fd); 8438eecd9ceSAlan Somers } 8448eecd9ceSAlan Somers 8459821f1d3SAlan Somers /* 8465fccbf31SAlan Somers * In writeback mode, writes to an O_WRONLY file could trigger reads from the 8475fccbf31SAlan Somers * server. The FUSE protocol explicitly allows that. 8485fccbf31SAlan Somers */ 8495fccbf31SAlan Somers TEST_F(WriteBack, rmw) 8505fccbf31SAlan Somers { 8515fccbf31SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8525fccbf31SAlan Somers const char RELPATH[] = "some_file.txt"; 8535fccbf31SAlan Somers const char *CONTENTS = "abcdefgh"; 8545fccbf31SAlan Somers const char *INITIAL = "XXXXXXXXXX"; 8555fccbf31SAlan Somers uint64_t ino = 42; 8565fccbf31SAlan Somers uint64_t offset = 1; 8575fccbf31SAlan Somers off_t fsize = 10; 8585fccbf31SAlan Somers int fd; 8595fccbf31SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8605fccbf31SAlan Somers 861cad67791SAlan Somers FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1); 8625fccbf31SAlan Somers expect_open(ino, 0, 1); 863d4fd0c81SAlan Somers expect_read(ino, 0, fsize, fsize, INITIAL, O_WRONLY); 86484879e46SAlan Somers maybe_expect_write(ino, offset, bufsize, CONTENTS); 8655fccbf31SAlan Somers 8665fccbf31SAlan Somers fd = open(FULLPATH, O_WRONLY); 8675fccbf31SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 8685fccbf31SAlan Somers 8695fccbf31SAlan Somers ASSERT_EQ(bufsize, pwrite(fd, CONTENTS, bufsize, offset)) 8705fccbf31SAlan Somers << strerror(errno); 8715fccbf31SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 8725fccbf31SAlan Somers } 8735fccbf31SAlan Somers 8745fccbf31SAlan Somers /* 8759821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 8769821f1d3SAlan Somers */ 87784879e46SAlan Somers TEST_F(WriteBack, cache) 8789821f1d3SAlan Somers { 8799821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8809821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 8819821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 8829821f1d3SAlan Somers uint64_t ino = 42; 8839821f1d3SAlan Somers int fd; 8849821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 8859821f1d3SAlan Somers char readbuf[bufsize]; 8869821f1d3SAlan Somers 8879821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 8889821f1d3SAlan Somers expect_open(ino, 0, 1); 889bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 8909821f1d3SAlan Somers 8919821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 8929821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 8939821f1d3SAlan Somers 8949821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 8959821f1d3SAlan Somers /* 8969821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 8979821f1d3SAlan Somers * filesystem daemon 8989821f1d3SAlan Somers */ 8999821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 9009821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 9019821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 9029821f1d3SAlan Somers } 9039821f1d3SAlan Somers 9049821f1d3SAlan Somers /* 9059821f1d3SAlan Somers * With O_DIRECT, writes should be not committed to cache. Admittedly this is 9069821f1d3SAlan Somers * an odd test, because it would be unusual to use O_DIRECT for writes but not 9079821f1d3SAlan Somers * reads. 9089821f1d3SAlan Somers */ 9099821f1d3SAlan Somers TEST_F(WriteBack, o_direct) 9109821f1d3SAlan Somers { 9119821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9129821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 9139821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 9149821f1d3SAlan Somers uint64_t ino = 42; 9159821f1d3SAlan Somers int fd; 9169821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 9179821f1d3SAlan Somers char readbuf[bufsize]; 9189821f1d3SAlan Somers 9199821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 9209821f1d3SAlan Somers expect_open(ino, 0, 1); 921bda39894SAlan Somers FuseTest::expect_write(ino, 0, bufsize, bufsize, 0, FUSE_WRITE_CACHE, 922bda39894SAlan Somers CONTENTS); 9239821f1d3SAlan Somers expect_read(ino, 0, bufsize, bufsize, CONTENTS); 9249821f1d3SAlan Somers 9259821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR | O_DIRECT); 9269821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 9279821f1d3SAlan Somers 9289821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 9299821f1d3SAlan Somers /* A subsequent read must query the daemon because cache is empty */ 9309821f1d3SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 9319821f1d3SAlan Somers ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno); 9329821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 9339821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 9349821f1d3SAlan Somers } 9359821f1d3SAlan Somers 9369821f1d3SAlan Somers /* 93784879e46SAlan Somers * When mounted with -o async, the writeback cache mode should delay writes 93884879e46SAlan Somers */ 93984879e46SAlan Somers TEST_F(WriteBackAsync, delay) 94084879e46SAlan Somers { 94184879e46SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 94284879e46SAlan Somers const char RELPATH[] = "some_file.txt"; 94384879e46SAlan Somers const char *CONTENTS = "abcdefgh"; 94484879e46SAlan Somers uint64_t ino = 42; 94584879e46SAlan Somers int fd; 94684879e46SAlan Somers ssize_t bufsize = strlen(CONTENTS); 94784879e46SAlan Somers 94884879e46SAlan Somers expect_lookup(RELPATH, ino, 0); 94984879e46SAlan Somers expect_open(ino, 0, 1); 95084879e46SAlan Somers /* Write should be cached, but FUSE_WRITE shouldn't be sent */ 95184879e46SAlan Somers EXPECT_CALL(*m_mock, process( 95284879e46SAlan Somers ResultOf([=](auto in) { 95384879e46SAlan Somers return (in.header.opcode == FUSE_WRITE); 95484879e46SAlan Somers }, Eq(true)), 95584879e46SAlan Somers _) 95684879e46SAlan Somers ).Times(0); 95784879e46SAlan Somers 95884879e46SAlan Somers fd = open(FULLPATH, O_RDWR); 95984879e46SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 96084879e46SAlan Somers 96184879e46SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 96284879e46SAlan Somers 96384879e46SAlan Somers /* Don't close the file because that would flush the cache */ 96484879e46SAlan Somers } 96584879e46SAlan Somers 96684879e46SAlan Somers /* 967aef22f2dSAlan Somers * In WriteBack mode, writes may be cached beyond what the server thinks is the 968aef22f2dSAlan Somers * EOF. In this case, a short read at EOF should _not_ cause fusefs to update 969aef22f2dSAlan Somers * the file's size. 970aef22f2dSAlan Somers */ 971aef22f2dSAlan Somers TEST_F(WriteBackAsync, eof) 972aef22f2dSAlan Somers { 973aef22f2dSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 974aef22f2dSAlan Somers const char RELPATH[] = "some_file.txt"; 975aef22f2dSAlan Somers const char *CONTENTS0 = "abcdefgh"; 976aef22f2dSAlan Somers const char *CONTENTS1 = "ijklmnop"; 977aef22f2dSAlan Somers uint64_t ino = 42; 978aef22f2dSAlan Somers int fd; 979aef22f2dSAlan Somers off_t offset = m_maxbcachebuf; 980aef22f2dSAlan Somers ssize_t wbufsize = strlen(CONTENTS1); 981aef22f2dSAlan Somers off_t old_filesize = (off_t)strlen(CONTENTS0); 982aef22f2dSAlan Somers ssize_t rbufsize = 2 * old_filesize; 983aef22f2dSAlan Somers char readbuf[rbufsize]; 984aef22f2dSAlan Somers size_t holesize = rbufsize - old_filesize; 985aef22f2dSAlan Somers char hole[holesize]; 986aef22f2dSAlan Somers struct stat sb; 987aef22f2dSAlan Somers ssize_t r; 988aef22f2dSAlan Somers 989aef22f2dSAlan Somers expect_lookup(RELPATH, ino, 0); 990aef22f2dSAlan Somers expect_open(ino, 0, 1); 991aef22f2dSAlan Somers expect_read(ino, 0, m_maxbcachebuf, old_filesize, CONTENTS0); 992aef22f2dSAlan Somers 993aef22f2dSAlan Somers fd = open(FULLPATH, O_RDWR); 994aef22f2dSAlan Somers EXPECT_LE(0, fd) << strerror(errno); 995aef22f2dSAlan Somers 996aef22f2dSAlan Somers /* Write and cache data beyond EOF */ 997aef22f2dSAlan Somers ASSERT_EQ(wbufsize, pwrite(fd, CONTENTS1, wbufsize, offset)) 998aef22f2dSAlan Somers << strerror(errno); 999aef22f2dSAlan Somers 1000aef22f2dSAlan Somers /* Read from the old EOF */ 1001aef22f2dSAlan Somers r = pread(fd, readbuf, rbufsize, 0); 1002aef22f2dSAlan Somers ASSERT_LE(0, r) << strerror(errno); 1003aef22f2dSAlan Somers EXPECT_EQ(rbufsize, r) << "read should've synthesized a hole"; 1004aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(CONTENTS0, readbuf, old_filesize)); 1005aef22f2dSAlan Somers bzero(hole, holesize); 1006aef22f2dSAlan Somers EXPECT_EQ(0, memcmp(hole, readbuf + old_filesize, holesize)); 1007aef22f2dSAlan Somers 1008aef22f2dSAlan Somers /* The file's size should still be what was established by pwrite */ 1009aef22f2dSAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1010aef22f2dSAlan Somers EXPECT_EQ(offset + wbufsize, sb.st_size); 1011aef22f2dSAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 1012aef22f2dSAlan Somers } 1013aef22f2dSAlan Somers 1014aef22f2dSAlan Somers /* 10159821f1d3SAlan Somers * Without direct_io, writes should be committed to cache 10169821f1d3SAlan Somers */ 1017b5aaf286SAlan Somers TEST_F(WriteThrough, writethrough) 10189821f1d3SAlan Somers { 10199821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 10209821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 10219821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 10229821f1d3SAlan Somers uint64_t ino = 42; 10239821f1d3SAlan Somers int fd; 10249821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 10259821f1d3SAlan Somers char readbuf[bufsize]; 10269821f1d3SAlan Somers 10279821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 10289821f1d3SAlan Somers expect_open(ino, 0, 1); 1029bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 10309821f1d3SAlan Somers 10319821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 10329821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 10339821f1d3SAlan Somers 10349821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 10359821f1d3SAlan Somers /* 10369821f1d3SAlan Somers * A subsequent read should be serviced by cache, without querying the 10379821f1d3SAlan Somers * filesystem daemon 10389821f1d3SAlan Somers */ 1039b5aaf286SAlan Somers ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno); 10409821f1d3SAlan Somers ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno); 10419821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 10429821f1d3SAlan Somers } 10439821f1d3SAlan Somers 10449821f1d3SAlan Somers /* With writethrough caching, writes update the cached file size */ 1045cad67791SAlan Somers TEST_F(WriteThrough, update_file_size) 10469821f1d3SAlan Somers { 10479821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 10489821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 10499821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 10509821f1d3SAlan Somers struct stat sb; 10519821f1d3SAlan Somers uint64_t ino = 42; 10529821f1d3SAlan Somers int fd; 10539821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 10549821f1d3SAlan Somers 10559821f1d3SAlan Somers expect_lookup(RELPATH, ino, 0); 10569821f1d3SAlan Somers expect_open(ino, 0, 1); 1057bda39894SAlan Somers expect_write(ino, 0, bufsize, bufsize, CONTENTS); 10589821f1d3SAlan Somers 10599821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 10609821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 10619821f1d3SAlan Somers 10629821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 10639821f1d3SAlan Somers /* Get cached attributes */ 10649821f1d3SAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 10659821f1d3SAlan Somers ASSERT_EQ(bufsize, sb.st_size); 10669821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 10679821f1d3SAlan Somers } 1068