1*a87e0831SAlan Somers /*- 2*a87e0831SAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*a87e0831SAlan Somers * 4*a87e0831SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 5*a87e0831SAlan Somers * 6*a87e0831SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 7*a87e0831SAlan Somers * from the FreeBSD Foundation. 8*a87e0831SAlan Somers * 9*a87e0831SAlan Somers * Redistribution and use in source and binary forms, with or without 10*a87e0831SAlan Somers * modification, are permitted provided that the following conditions 11*a87e0831SAlan Somers * are met: 12*a87e0831SAlan Somers * 1. Redistributions of source code must retain the above copyright 13*a87e0831SAlan Somers * notice, this list of conditions and the following disclaimer. 14*a87e0831SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 15*a87e0831SAlan Somers * notice, this list of conditions and the following disclaimer in the 16*a87e0831SAlan Somers * documentation and/or other materials provided with the distribution. 17*a87e0831SAlan Somers * 18*a87e0831SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19*a87e0831SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*a87e0831SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*a87e0831SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22*a87e0831SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23*a87e0831SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24*a87e0831SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25*a87e0831SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26*a87e0831SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27*a87e0831SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28*a87e0831SAlan Somers * SUCH DAMAGE. 29*a87e0831SAlan Somers */ 30*a87e0831SAlan Somers 31*a87e0831SAlan Somers extern "C" { 32*a87e0831SAlan Somers #include <fcntl.h> 33*a87e0831SAlan Somers #include <stdlib.h> 34*a87e0831SAlan Somers #include <unistd.h> 35*a87e0831SAlan Somers } 36*a87e0831SAlan Somers 37*a87e0831SAlan Somers #include "mockfs.hh" 38*a87e0831SAlan Somers #include "utils.hh" 39*a87e0831SAlan Somers 40*a87e0831SAlan Somers /* 41*a87e0831SAlan Somers * For testing I/O like fsx does, but deterministically and without a real 42*a87e0831SAlan Somers * underlying file system 43*a87e0831SAlan Somers * 44*a87e0831SAlan Somers * TODO: after fusefs gains the options to select cache mode for each mount 45*a87e0831SAlan Somers * point, run each of these tests for all cache modes. 46*a87e0831SAlan Somers */ 47*a87e0831SAlan Somers 48*a87e0831SAlan Somers using namespace testing; 49*a87e0831SAlan Somers 50*a87e0831SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 51*a87e0831SAlan Somers const char RELPATH[] = "some_file.txt"; 52*a87e0831SAlan Somers const uint64_t ino = 42; 53*a87e0831SAlan Somers 54*a87e0831SAlan Somers class Io: public FuseTest { 55*a87e0831SAlan Somers public: 56*a87e0831SAlan Somers int m_backing_fd, m_control_fd, m_test_fd; 57*a87e0831SAlan Somers 58*a87e0831SAlan Somers Io(): m_backing_fd(-1), m_control_fd(-1) {}; 59*a87e0831SAlan Somers 60*a87e0831SAlan Somers void SetUp() 61*a87e0831SAlan Somers { 62*a87e0831SAlan Somers m_backing_fd = open("backing_file", O_RDWR | O_CREAT | O_TRUNC); 63*a87e0831SAlan Somers if (m_backing_fd < 0) 64*a87e0831SAlan Somers FAIL() << strerror(errno); 65*a87e0831SAlan Somers m_control_fd = open("control", O_RDWR | O_CREAT | O_TRUNC); 66*a87e0831SAlan Somers if (m_control_fd < 0) 67*a87e0831SAlan Somers FAIL() << strerror(errno); 68*a87e0831SAlan Somers srandom(22'9'1982); // Seed with my birthday 69*a87e0831SAlan Somers FuseTest::SetUp(); 70*a87e0831SAlan Somers if (IsSkipped()) 71*a87e0831SAlan Somers return; 72*a87e0831SAlan Somers 73*a87e0831SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 74*a87e0831SAlan Somers expect_open(ino, 0, 1); 75*a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 76*a87e0831SAlan Somers ResultOf([=](auto in) { 77*a87e0831SAlan Somers return (in.header.opcode == FUSE_WRITE && 78*a87e0831SAlan Somers in.header.nodeid == ino); 79*a87e0831SAlan Somers }, Eq(true)), 80*a87e0831SAlan Somers _) 81*a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 82*a87e0831SAlan Somers const char *buf = (const char*)in.body.bytes + 83*a87e0831SAlan Somers sizeof(struct fuse_write_in); 84*a87e0831SAlan Somers ssize_t isize = in.body.write.size; 85*a87e0831SAlan Somers off_t iofs = in.body.write.offset; 86*a87e0831SAlan Somers 87*a87e0831SAlan Somers ASSERT_EQ(isize, pwrite(m_backing_fd, buf, isize, iofs)) 88*a87e0831SAlan Somers << strerror(errno); 89*a87e0831SAlan Somers SET_OUT_HEADER_LEN(out, write); 90*a87e0831SAlan Somers out.body.write.size = isize; 91*a87e0831SAlan Somers }))); 92*a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 93*a87e0831SAlan Somers ResultOf([=](auto in) { 94*a87e0831SAlan Somers return (in.header.opcode == FUSE_READ && 95*a87e0831SAlan Somers in.header.nodeid == ino); 96*a87e0831SAlan Somers }, Eq(true)), 97*a87e0831SAlan Somers _) 98*a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 99*a87e0831SAlan Somers ssize_t isize = in.body.write.size; 100*a87e0831SAlan Somers off_t iofs = in.body.write.offset; 101*a87e0831SAlan Somers void *buf = out.body.bytes; 102*a87e0831SAlan Somers 103*a87e0831SAlan Somers ASSERT_LE(0, pread(m_backing_fd, buf, isize, iofs)) 104*a87e0831SAlan Somers << strerror(errno); 105*a87e0831SAlan Somers out.header.len = sizeof(struct fuse_out_header) + isize; 106*a87e0831SAlan Somers }))); 107*a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 108*a87e0831SAlan Somers ResultOf([=](auto in) { 109*a87e0831SAlan Somers uint32_t valid = FATTR_SIZE | FATTR_FH; 110*a87e0831SAlan Somers return (in.header.opcode == FUSE_SETATTR && 111*a87e0831SAlan Somers in.header.nodeid == ino && 112*a87e0831SAlan Somers in.body.setattr.valid == valid); 113*a87e0831SAlan Somers }, Eq(true)), 114*a87e0831SAlan Somers _) 115*a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnImmediate([=](auto in, auto& out) { 116*a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_backing_fd, in.body.setattr.size)) 117*a87e0831SAlan Somers << strerror(errno); 118*a87e0831SAlan Somers SET_OUT_HEADER_LEN(out, attr); 119*a87e0831SAlan Somers out.body.attr.attr.ino = ino; 120*a87e0831SAlan Somers out.body.attr.attr.mode = S_IFREG | 0755; 121*a87e0831SAlan Somers out.body.attr.attr.size = in.body.setattr.size; 122*a87e0831SAlan Somers out.body.attr.attr_valid = UINT64_MAX; 123*a87e0831SAlan Somers }))); 124*a87e0831SAlan Somers /* Any test that close()s will send FUSE_FLUSH and FUSE_RELEASE */ 125*a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 126*a87e0831SAlan Somers ResultOf([=](auto in) { 127*a87e0831SAlan Somers return (in.header.opcode == FUSE_FLUSH && 128*a87e0831SAlan Somers in.header.nodeid == ino); 129*a87e0831SAlan Somers }, Eq(true)), 130*a87e0831SAlan Somers _) 131*a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(0))); 132*a87e0831SAlan Somers EXPECT_CALL(*m_mock, process( 133*a87e0831SAlan Somers ResultOf([=](auto in) { 134*a87e0831SAlan Somers return (in.header.opcode == FUSE_RELEASE && 135*a87e0831SAlan Somers in.header.nodeid == ino); 136*a87e0831SAlan Somers }, Eq(true)), 137*a87e0831SAlan Somers _) 138*a87e0831SAlan Somers ).WillRepeatedly(Invoke(ReturnErrno(0))); 139*a87e0831SAlan Somers 140*a87e0831SAlan Somers m_test_fd = open(FULLPATH, O_RDWR ); 141*a87e0831SAlan Somers EXPECT_LE(0, m_test_fd) << strerror(errno); 142*a87e0831SAlan Somers } 143*a87e0831SAlan Somers 144*a87e0831SAlan Somers void TearDown() 145*a87e0831SAlan Somers { 146*a87e0831SAlan Somers if (m_backing_fd >= 0) 147*a87e0831SAlan Somers close(m_backing_fd); 148*a87e0831SAlan Somers if (m_control_fd >= 0) 149*a87e0831SAlan Somers close(m_control_fd); 150*a87e0831SAlan Somers FuseTest::TearDown(); 151*a87e0831SAlan Somers /* Deliberately leak test_fd */ 152*a87e0831SAlan Somers } 153*a87e0831SAlan Somers 154*a87e0831SAlan Somers void do_ftruncate(off_t offs) 155*a87e0831SAlan Somers { 156*a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_test_fd, offs)) << strerror(errno); 157*a87e0831SAlan Somers ASSERT_EQ(0, ftruncate(m_control_fd, offs)) << strerror(errno); 158*a87e0831SAlan Somers } 159*a87e0831SAlan Somers 160*a87e0831SAlan Somers void do_read(ssize_t size, off_t offs) 161*a87e0831SAlan Somers { 162*a87e0831SAlan Somers void *test_buf, *control_buf; 163*a87e0831SAlan Somers 164*a87e0831SAlan Somers test_buf = malloc(size); 165*a87e0831SAlan Somers ASSERT_NE(NULL, test_buf) << strerror(errno); 166*a87e0831SAlan Somers control_buf = malloc(size); 167*a87e0831SAlan Somers ASSERT_NE(NULL, control_buf) << strerror(errno); 168*a87e0831SAlan Somers 169*a87e0831SAlan Somers ASSERT_EQ(size, pread(m_test_fd, test_buf, size, offs)) 170*a87e0831SAlan Somers << strerror(errno); 171*a87e0831SAlan Somers ASSERT_EQ(size, pread(m_control_fd, control_buf, size, offs)) 172*a87e0831SAlan Somers << strerror(errno); 173*a87e0831SAlan Somers 174*a87e0831SAlan Somers ASSERT_EQ(0, memcmp(test_buf, control_buf, size)); 175*a87e0831SAlan Somers 176*a87e0831SAlan Somers free(control_buf); 177*a87e0831SAlan Somers free(test_buf); 178*a87e0831SAlan Somers } 179*a87e0831SAlan Somers 180*a87e0831SAlan Somers void do_write(ssize_t size, off_t offs) 181*a87e0831SAlan Somers { 182*a87e0831SAlan Somers char *buf; 183*a87e0831SAlan Somers long i; 184*a87e0831SAlan Somers 185*a87e0831SAlan Somers buf = (char*)malloc(size); 186*a87e0831SAlan Somers ASSERT_NE(NULL, buf) << strerror(errno); 187*a87e0831SAlan Somers for (i=0; i < size; i++) 188*a87e0831SAlan Somers buf[i] = random(); 189*a87e0831SAlan Somers 190*a87e0831SAlan Somers ASSERT_EQ(size, pwrite(m_test_fd, buf, size, offs )) 191*a87e0831SAlan Somers << strerror(errno); 192*a87e0831SAlan Somers ASSERT_EQ(size, pwrite(m_control_fd, buf, size, offs)) 193*a87e0831SAlan Somers << strerror(errno); 194*a87e0831SAlan Somers } 195*a87e0831SAlan Somers 196*a87e0831SAlan Somers }; 197*a87e0831SAlan Somers 198*a87e0831SAlan Somers /* 199*a87e0831SAlan Somers * Extend a file with dirty data in the last page of the last block. 200*a87e0831SAlan Somers * 201*a87e0831SAlan Somers * fsx -WR -P /tmp -S8 -N3 fsx.bin 202*a87e0831SAlan Somers */ 203*a87e0831SAlan Somers TEST_F(Io, extend_from_dirty_page) 204*a87e0831SAlan Somers { 205*a87e0831SAlan Somers off_t wofs = 0x21a0; 206*a87e0831SAlan Somers ssize_t wsize = 0xf0a8; 207*a87e0831SAlan Somers off_t rofs = 0xb284; 208*a87e0831SAlan Somers ssize_t rsize = 0x9b22; 209*a87e0831SAlan Somers off_t truncsize = 0x28702; 210*a87e0831SAlan Somers 211*a87e0831SAlan Somers do_write(wsize, wofs); 212*a87e0831SAlan Somers do_ftruncate(truncsize); 213*a87e0831SAlan Somers do_read(rsize, rofs); 214*a87e0831SAlan Somers } 215*a87e0831SAlan Somers 216*a87e0831SAlan Somers /* 217*a87e0831SAlan Somers * When writing the last page of a file, it must be written synchronously. 218*a87e0831SAlan Somers * Otherwise the cached page can become invalid by a subsequent extend 219*a87e0831SAlan Somers * operation. 220*a87e0831SAlan Somers * 221*a87e0831SAlan Somers * fsx -WR -P /tmp -S642 -N3 fsx.bin 222*a87e0831SAlan Somers */ 223*a87e0831SAlan Somers TEST_F(Io, last_page) 224*a87e0831SAlan Somers { 225*a87e0831SAlan Somers off_t wofs0 = 0x1134f; 226*a87e0831SAlan Somers ssize_t wsize0 = 0xcc77; 227*a87e0831SAlan Somers off_t wofs1 = 0x2096a; 228*a87e0831SAlan Somers ssize_t wsize1 = 0xdfa7; 229*a87e0831SAlan Somers off_t rofs = 0x1a3aa; 230*a87e0831SAlan Somers ssize_t rsize = 0xb5b7; 231*a87e0831SAlan Somers 232*a87e0831SAlan Somers do_write(wsize0, wofs0); 233*a87e0831SAlan Somers do_write(wsize1, wofs1); 234*a87e0831SAlan Somers do_read(rsize, rofs); 235*a87e0831SAlan Somers } 236*a87e0831SAlan Somers 237*a87e0831SAlan Somers /* 238*a87e0831SAlan Somers * Read a hole from a block that contains some cached data. 239*a87e0831SAlan Somers * 240*a87e0831SAlan Somers * fsx -WR -P /tmp -S55 fsx.bin 241*a87e0831SAlan Somers */ 242*a87e0831SAlan Somers TEST_F(Io, read_hole_from_cached_block) 243*a87e0831SAlan Somers { 244*a87e0831SAlan Somers off_t wofs = 0x160c5; 245*a87e0831SAlan Somers ssize_t wsize = 0xa996; 246*a87e0831SAlan Somers off_t rofs = 0x472e; 247*a87e0831SAlan Somers ssize_t rsize = 0xd8d5; 248*a87e0831SAlan Somers 249*a87e0831SAlan Somers do_write(wsize, wofs); 250*a87e0831SAlan Somers do_read(rsize, rofs); 251*a87e0831SAlan Somers } 252*a87e0831SAlan Somers 253*a87e0831SAlan Somers /* 254*a87e0831SAlan Somers * Reliable panic; I don't yet know why. 255*a87e0831SAlan Somers * Disabled because it panics. 256*a87e0831SAlan Somers * 257*a87e0831SAlan Somers * fsx -WR -P /tmp -S839 -d -N6 fsx.bin 258*a87e0831SAlan Somers */ 259*a87e0831SAlan Somers TEST_F(Io, DISABLED_fault_on_nofault_entry) 260*a87e0831SAlan Somers { 261*a87e0831SAlan Somers off_t wofs0 = 0x3bad7; 262*a87e0831SAlan Somers ssize_t wsize0 = 0x4529; 263*a87e0831SAlan Somers off_t wofs1 = 0xc30d; 264*a87e0831SAlan Somers ssize_t wsize1 = 0x5f77; 265*a87e0831SAlan Somers off_t truncsize0 = 0x10916; 266*a87e0831SAlan Somers off_t rofs = 0xdf17; 267*a87e0831SAlan Somers ssize_t rsize = 0x29ff; 268*a87e0831SAlan Somers off_t truncsize1 = 0x152b4; 269*a87e0831SAlan Somers 270*a87e0831SAlan Somers do_write(wsize0, wofs0); 271*a87e0831SAlan Somers do_write(wsize1, wofs1); 272*a87e0831SAlan Somers do_ftruncate(truncsize0); 273*a87e0831SAlan Somers do_read(rsize, rofs); 274*a87e0831SAlan Somers do_ftruncate(truncsize1); 275*a87e0831SAlan Somers close(m_test_fd); 276*a87e0831SAlan Somers } 277