1*9821f1d3SAlan Somers /*- 2*9821f1d3SAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*9821f1d3SAlan Somers * 4*9821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 5*9821f1d3SAlan Somers * 6*9821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 7*9821f1d3SAlan Somers * from the FreeBSD Foundation. 8*9821f1d3SAlan Somers * 9*9821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without 10*9821f1d3SAlan Somers * modification, are permitted provided that the following conditions 11*9821f1d3SAlan Somers * are met: 12*9821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright 13*9821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer. 14*9821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 15*9821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the 16*9821f1d3SAlan Somers * documentation and/or other materials provided with the distribution. 17*9821f1d3SAlan Somers * 18*9821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19*9821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*9821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*9821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22*9821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23*9821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24*9821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25*9821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26*9821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27*9821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28*9821f1d3SAlan Somers * SUCH DAMAGE. 29*9821f1d3SAlan Somers */ 30*9821f1d3SAlan Somers 31*9821f1d3SAlan Somers extern "C" { 32*9821f1d3SAlan Somers #include <aio.h> 33*9821f1d3SAlan Somers #include <fcntl.h> 34*9821f1d3SAlan Somers #include <unistd.h> 35*9821f1d3SAlan Somers } 36*9821f1d3SAlan Somers 37*9821f1d3SAlan Somers #include "mockfs.hh" 38*9821f1d3SAlan Somers #include "utils.hh" 39*9821f1d3SAlan Somers 40*9821f1d3SAlan Somers using namespace testing; 41*9821f1d3SAlan Somers 42*9821f1d3SAlan Somers /* 43*9821f1d3SAlan Somers * TODO: remove FUSE_FSYNC_FDATASYNC definition when upgrading to protocol 7.28. 44*9821f1d3SAlan Somers * This bit was actually part of kernel protocol version 5.2, but never 45*9821f1d3SAlan Somers * documented until after 7.28 46*9821f1d3SAlan Somers */ 47*9821f1d3SAlan Somers #ifndef FUSE_FSYNC_FDATASYNC 48*9821f1d3SAlan Somers #define FUSE_FSYNC_FDATASYNC 1 49*9821f1d3SAlan Somers #endif 50*9821f1d3SAlan Somers 51*9821f1d3SAlan Somers class Fsync: public FuseTest { 52*9821f1d3SAlan Somers public: 53*9821f1d3SAlan Somers void expect_fsync(uint64_t ino, uint32_t flags, int error) 54*9821f1d3SAlan Somers { 55*9821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 56*9821f1d3SAlan Somers ResultOf([=](auto in) { 57*9821f1d3SAlan Somers return (in->header.opcode == FUSE_FSYNC && 58*9821f1d3SAlan Somers in->header.nodeid == ino && 59*9821f1d3SAlan Somers in->body.fsync.fh == FH && 60*9821f1d3SAlan Somers in->body.fsync.fsync_flags == flags); 61*9821f1d3SAlan Somers }, Eq(true)), 62*9821f1d3SAlan Somers _) 63*9821f1d3SAlan Somers ).WillOnce(Invoke(ReturnErrno(error))); 64*9821f1d3SAlan Somers } 65*9821f1d3SAlan Somers 66*9821f1d3SAlan Somers void expect_lookup(const char *relpath, uint64_t ino) 67*9821f1d3SAlan Somers { 68*9821f1d3SAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 0, 1); 69*9821f1d3SAlan Somers } 70*9821f1d3SAlan Somers 71*9821f1d3SAlan Somers void expect_write(uint64_t ino, uint64_t size, const void *contents) 72*9821f1d3SAlan Somers { 73*9821f1d3SAlan Somers FuseTest::expect_write(ino, 0, size, size, 0, contents); 74*9821f1d3SAlan Somers } 75*9821f1d3SAlan Somers 76*9821f1d3SAlan Somers }; 77*9821f1d3SAlan Somers 78*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236379 */ 79*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ 80*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ 81*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_aio_fsync) 82*9821f1d3SAlan Somers { 83*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 84*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 85*9821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 86*9821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 87*9821f1d3SAlan Somers uint64_t ino = 42; 88*9821f1d3SAlan Somers struct aiocb iocb, *piocb; 89*9821f1d3SAlan Somers int fd; 90*9821f1d3SAlan Somers 91*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 92*9821f1d3SAlan Somers expect_open(ino, 0, 1); 93*9821f1d3SAlan Somers expect_getattr(ino, 0); 94*9821f1d3SAlan Somers expect_write(ino, bufsize, CONTENTS); 95*9821f1d3SAlan Somers expect_fsync(ino, 0, 0); 96*9821f1d3SAlan Somers 97*9821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 98*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 99*9821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 100*9821f1d3SAlan Somers 101*9821f1d3SAlan Somers bzero(&iocb, sizeof(iocb)); 102*9821f1d3SAlan Somers iocb.aio_fildes = fd; 103*9821f1d3SAlan Somers 104*9821f1d3SAlan Somers ASSERT_EQ(0, aio_fsync(O_SYNC, &iocb)) << strerror(errno); 105*9821f1d3SAlan Somers ASSERT_EQ(0, aio_waitcomplete(&piocb, NULL)) << strerror(errno); 106*9821f1d3SAlan Somers 107*9821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 108*9821f1d3SAlan Somers } 109*9821f1d3SAlan Somers 110*9821f1d3SAlan Somers /* 111*9821f1d3SAlan Somers * fuse(4) should NOT fsync during VOP_RELEASE or VOP_INACTIVE 112*9821f1d3SAlan Somers * 113*9821f1d3SAlan Somers * This test only really make sense in writeback caching mode, but it should 114*9821f1d3SAlan Somers * still pass in any cache mode. 115*9821f1d3SAlan Somers */ 116*9821f1d3SAlan Somers TEST_F(Fsync, close) 117*9821f1d3SAlan Somers { 118*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 119*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 120*9821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 121*9821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 122*9821f1d3SAlan Somers uint64_t ino = 42; 123*9821f1d3SAlan Somers int fd; 124*9821f1d3SAlan Somers 125*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 126*9821f1d3SAlan Somers expect_open(ino, 0, 1); 127*9821f1d3SAlan Somers expect_getattr(ino, 0); 128*9821f1d3SAlan Somers expect_write(ino, bufsize, CONTENTS); 129*9821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 130*9821f1d3SAlan Somers ResultOf([=](auto in) { 131*9821f1d3SAlan Somers return (in->header.opcode == FUSE_SETATTR); 132*9821f1d3SAlan Somers }, Eq(true)), 133*9821f1d3SAlan Somers _) 134*9821f1d3SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { 135*9821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, attr); 136*9821f1d3SAlan Somers out->body.attr.attr.ino = ino; // Must match nodeid 137*9821f1d3SAlan Somers }))); 138*9821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 139*9821f1d3SAlan Somers ResultOf([=](auto in) { 140*9821f1d3SAlan Somers return (in->header.opcode == FUSE_FSYNC); 141*9821f1d3SAlan Somers }, Eq(true)), 142*9821f1d3SAlan Somers _) 143*9821f1d3SAlan Somers ).Times(0); 144*9821f1d3SAlan Somers expect_release(ino, 1, 0, 0); 145*9821f1d3SAlan Somers 146*9821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 147*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 148*9821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 149*9821f1d3SAlan Somers close(fd); 150*9821f1d3SAlan Somers } 151*9821f1d3SAlan Somers 152*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ 153*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_eio) 154*9821f1d3SAlan Somers { 155*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 156*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 157*9821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 158*9821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 159*9821f1d3SAlan Somers uint64_t ino = 42; 160*9821f1d3SAlan Somers int fd; 161*9821f1d3SAlan Somers 162*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 163*9821f1d3SAlan Somers expect_open(ino, 0, 1); 164*9821f1d3SAlan Somers expect_getattr(ino, 0); 165*9821f1d3SAlan Somers expect_write(ino, bufsize, CONTENTS); 166*9821f1d3SAlan Somers expect_fsync(ino, FUSE_FSYNC_FDATASYNC, EIO); 167*9821f1d3SAlan Somers 168*9821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 169*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 170*9821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 171*9821f1d3SAlan Somers ASSERT_NE(0, fdatasync(fd)); 172*9821f1d3SAlan Somers ASSERT_EQ(EIO, errno); 173*9821f1d3SAlan Somers 174*9821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 175*9821f1d3SAlan Somers } 176*9821f1d3SAlan Somers 177*9821f1d3SAlan Somers /* 178*9821f1d3SAlan Somers * If the filesystem returns ENOSYS, it will be treated as success and 179*9821f1d3SAlan Somers * subsequent calls to VOP_FSYNC will succeed automatically without being sent 180*9821f1d3SAlan Somers * to the filesystem daemon 181*9821f1d3SAlan Somers */ 182*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ 183*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236557 */ 184*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_enosys) 185*9821f1d3SAlan Somers { 186*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 187*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 188*9821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 189*9821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 190*9821f1d3SAlan Somers uint64_t ino = 42; 191*9821f1d3SAlan Somers int fd; 192*9821f1d3SAlan Somers 193*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 194*9821f1d3SAlan Somers expect_open(ino, 0, 1); 195*9821f1d3SAlan Somers expect_getattr(ino, 0); 196*9821f1d3SAlan Somers expect_write(ino, bufsize, CONTENTS); 197*9821f1d3SAlan Somers expect_fsync(ino, FUSE_FSYNC_FDATASYNC, ENOSYS); 198*9821f1d3SAlan Somers 199*9821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 200*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 201*9821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 202*9821f1d3SAlan Somers EXPECT_EQ(0, fdatasync(fd)); 203*9821f1d3SAlan Somers 204*9821f1d3SAlan Somers /* Subsequent calls shouldn't query the daemon*/ 205*9821f1d3SAlan Somers EXPECT_EQ(0, fdatasync(fd)); 206*9821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 207*9821f1d3SAlan Somers } 208*9821f1d3SAlan Somers 209*9821f1d3SAlan Somers 210*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ 211*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_fdatasync) 212*9821f1d3SAlan Somers { 213*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 214*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 215*9821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 216*9821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 217*9821f1d3SAlan Somers uint64_t ino = 42; 218*9821f1d3SAlan Somers int fd; 219*9821f1d3SAlan Somers 220*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 221*9821f1d3SAlan Somers expect_open(ino, 0, 1); 222*9821f1d3SAlan Somers expect_getattr(ino, 0); 223*9821f1d3SAlan Somers expect_write(ino, bufsize, CONTENTS); 224*9821f1d3SAlan Somers expect_fsync(ino, FUSE_FSYNC_FDATASYNC, 0); 225*9821f1d3SAlan Somers 226*9821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 227*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 228*9821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 229*9821f1d3SAlan Somers ASSERT_EQ(0, fdatasync(fd)) << strerror(errno); 230*9821f1d3SAlan Somers 231*9821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 232*9821f1d3SAlan Somers } 233*9821f1d3SAlan Somers 234*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ 235*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ 236*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_fsync) 237*9821f1d3SAlan Somers { 238*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 239*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 240*9821f1d3SAlan Somers const char *CONTENTS = "abcdefgh"; 241*9821f1d3SAlan Somers ssize_t bufsize = strlen(CONTENTS); 242*9821f1d3SAlan Somers uint64_t ino = 42; 243*9821f1d3SAlan Somers int fd; 244*9821f1d3SAlan Somers 245*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 246*9821f1d3SAlan Somers expect_open(ino, 0, 1); 247*9821f1d3SAlan Somers expect_getattr(ino, 0); 248*9821f1d3SAlan Somers expect_write(ino, bufsize, CONTENTS); 249*9821f1d3SAlan Somers expect_fsync(ino, 0, 0); 250*9821f1d3SAlan Somers 251*9821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 252*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 253*9821f1d3SAlan Somers ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno); 254*9821f1d3SAlan Somers ASSERT_EQ(0, fsync(fd)) << strerror(errno); 255*9821f1d3SAlan Somers 256*9821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 257*9821f1d3SAlan Somers } 258*9821f1d3SAlan Somers 259*9821f1d3SAlan Somers /* Fsync should sync a file with dirty metadata but clean data */ 260*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236473 */ 261*9821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=236474 */ 262*9821f1d3SAlan Somers TEST_F(Fsync, DISABLED_fsync_metadata_only) 263*9821f1d3SAlan Somers { 264*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 265*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 266*9821f1d3SAlan Somers uint64_t ino = 42; 267*9821f1d3SAlan Somers int fd; 268*9821f1d3SAlan Somers mode_t mode = 0755; 269*9821f1d3SAlan Somers 270*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 271*9821f1d3SAlan Somers expect_open(ino, 0, 1); 272*9821f1d3SAlan Somers expect_getattr(ino, 0); 273*9821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 274*9821f1d3SAlan Somers ResultOf([=](auto in) { 275*9821f1d3SAlan Somers return (in->header.opcode == FUSE_SETATTR); 276*9821f1d3SAlan Somers }, Eq(true)), 277*9821f1d3SAlan Somers _) 278*9821f1d3SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto out) { 279*9821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, attr); 280*9821f1d3SAlan Somers out->body.attr.attr.ino = ino; // Must match nodeid 281*9821f1d3SAlan Somers out->body.attr.attr.mode = S_IFREG | mode; 282*9821f1d3SAlan Somers }))); 283*9821f1d3SAlan Somers 284*9821f1d3SAlan Somers expect_fsync(ino, 0, 0); 285*9821f1d3SAlan Somers 286*9821f1d3SAlan Somers fd = open(FULLPATH, O_RDWR); 287*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 288*9821f1d3SAlan Somers ASSERT_EQ(0, fchmod(fd, mode)) << strerror(errno); 289*9821f1d3SAlan Somers ASSERT_EQ(0, fsync(fd)) << strerror(errno); 290*9821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 291*9821f1d3SAlan Somers } 292*9821f1d3SAlan Somers 293*9821f1d3SAlan Somers // fsync()ing a file that isn't dirty should be a no-op 294*9821f1d3SAlan Somers TEST_F(Fsync, nop) 295*9821f1d3SAlan Somers { 296*9821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 297*9821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 298*9821f1d3SAlan Somers uint64_t ino = 42; 299*9821f1d3SAlan Somers int fd; 300*9821f1d3SAlan Somers 301*9821f1d3SAlan Somers expect_lookup(RELPATH, ino); 302*9821f1d3SAlan Somers expect_open(ino, 0, 1); 303*9821f1d3SAlan Somers expect_getattr(ino, 0); 304*9821f1d3SAlan Somers 305*9821f1d3SAlan Somers fd = open(FULLPATH, O_WRONLY); 306*9821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 307*9821f1d3SAlan Somers 308*9821f1d3SAlan Somers ASSERT_EQ(0, fsync(fd)) << strerror(errno); 309*9821f1d3SAlan Somers 310*9821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 311*9821f1d3SAlan Somers } 312*9821f1d3SAlan Somers 313*9821f1d3SAlan Somers // TODO: ENOSYS test 314