1*92bbfe1fSAlan Somers /*- 2*92bbfe1fSAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*92bbfe1fSAlan Somers * 4*92bbfe1fSAlan Somers * Copyright (c) 2020 Alan Somers 5*92bbfe1fSAlan Somers * 6*92bbfe1fSAlan Somers * Redistribution and use in source and binary forms, with or without 7*92bbfe1fSAlan Somers * modification, are permitted provided that the following conditions 8*92bbfe1fSAlan Somers * are met: 9*92bbfe1fSAlan Somers * 1. Redistributions of source code must retain the above copyright 10*92bbfe1fSAlan Somers * notice, this list of conditions and the following disclaimer. 11*92bbfe1fSAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 12*92bbfe1fSAlan Somers * notice, this list of conditions and the following disclaimer in the 13*92bbfe1fSAlan Somers * documentation and/or other materials provided with the distribution. 14*92bbfe1fSAlan Somers * 15*92bbfe1fSAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*92bbfe1fSAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*92bbfe1fSAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*92bbfe1fSAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*92bbfe1fSAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*92bbfe1fSAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*92bbfe1fSAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*92bbfe1fSAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*92bbfe1fSAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*92bbfe1fSAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*92bbfe1fSAlan Somers * SUCH DAMAGE. 26*92bbfe1fSAlan Somers * 27*92bbfe1fSAlan Somers * $FreeBSD$ 28*92bbfe1fSAlan Somers */ 29*92bbfe1fSAlan Somers 30*92bbfe1fSAlan Somers extern "C" { 31*92bbfe1fSAlan Somers #include <sys/param.h> 32*92bbfe1fSAlan Somers #include <sys/time.h> 33*92bbfe1fSAlan Somers #include <sys/resource.h> 34*92bbfe1fSAlan Somers 35*92bbfe1fSAlan Somers #include <fcntl.h> 36*92bbfe1fSAlan Somers #include <signal.h> 37*92bbfe1fSAlan Somers #include <unistd.h> 38*92bbfe1fSAlan Somers } 39*92bbfe1fSAlan Somers 40*92bbfe1fSAlan Somers #include "mockfs.hh" 41*92bbfe1fSAlan Somers #include "utils.hh" 42*92bbfe1fSAlan Somers 43*92bbfe1fSAlan Somers using namespace testing; 44*92bbfe1fSAlan Somers 45*92bbfe1fSAlan Somers class CopyFileRange: public FuseTest { 46*92bbfe1fSAlan Somers public: 47*92bbfe1fSAlan Somers static sig_atomic_t s_sigxfsz; 48*92bbfe1fSAlan Somers 49*92bbfe1fSAlan Somers void SetUp() { 50*92bbfe1fSAlan Somers s_sigxfsz = 0; 51*92bbfe1fSAlan Somers FuseTest::SetUp(); 52*92bbfe1fSAlan Somers } 53*92bbfe1fSAlan Somers 54*92bbfe1fSAlan Somers void TearDown() { 55*92bbfe1fSAlan Somers struct sigaction sa; 56*92bbfe1fSAlan Somers 57*92bbfe1fSAlan Somers bzero(&sa, sizeof(sa)); 58*92bbfe1fSAlan Somers sa.sa_handler = SIG_DFL; 59*92bbfe1fSAlan Somers sigaction(SIGXFSZ, &sa, NULL); 60*92bbfe1fSAlan Somers 61*92bbfe1fSAlan Somers FuseTest::TearDown(); 62*92bbfe1fSAlan Somers } 63*92bbfe1fSAlan Somers 64*92bbfe1fSAlan Somers void expect_maybe_lseek(uint64_t ino) 65*92bbfe1fSAlan Somers { 66*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 67*92bbfe1fSAlan Somers ResultOf([=](auto in) { 68*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_LSEEK && 69*92bbfe1fSAlan Somers in.header.nodeid == ino); 70*92bbfe1fSAlan Somers }, Eq(true)), 71*92bbfe1fSAlan Somers _) 72*92bbfe1fSAlan Somers ).Times(AtMost(1)) 73*92bbfe1fSAlan Somers .WillRepeatedly(Invoke(ReturnErrno(ENOSYS))); 74*92bbfe1fSAlan Somers } 75*92bbfe1fSAlan Somers 76*92bbfe1fSAlan Somers void expect_open(uint64_t ino, uint32_t flags, int times, uint64_t fh) 77*92bbfe1fSAlan Somers { 78*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 79*92bbfe1fSAlan Somers ResultOf([=](auto in) { 80*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_OPEN && 81*92bbfe1fSAlan Somers in.header.nodeid == ino); 82*92bbfe1fSAlan Somers }, Eq(true)), 83*92bbfe1fSAlan Somers _) 84*92bbfe1fSAlan Somers ).Times(times) 85*92bbfe1fSAlan Somers .WillRepeatedly(Invoke( 86*92bbfe1fSAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 87*92bbfe1fSAlan Somers out.header.len = sizeof(out.header); 88*92bbfe1fSAlan Somers SET_OUT_HEADER_LEN(out, open); 89*92bbfe1fSAlan Somers out.body.open.fh = fh; 90*92bbfe1fSAlan Somers out.body.open.open_flags = flags; 91*92bbfe1fSAlan Somers }))); 92*92bbfe1fSAlan Somers } 93*92bbfe1fSAlan Somers 94*92bbfe1fSAlan Somers void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, 95*92bbfe1fSAlan Somers uint64_t osize, const void *contents) 96*92bbfe1fSAlan Somers { 97*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 98*92bbfe1fSAlan Somers ResultOf([=](auto in) { 99*92bbfe1fSAlan Somers const char *buf = (const char*)in.body.bytes + 100*92bbfe1fSAlan Somers sizeof(struct fuse_write_in); 101*92bbfe1fSAlan Somers 102*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_WRITE && 103*92bbfe1fSAlan Somers in.header.nodeid == ino && 104*92bbfe1fSAlan Somers in.body.write.offset == offset && 105*92bbfe1fSAlan Somers in.body.write.size == isize && 106*92bbfe1fSAlan Somers 0 == bcmp(buf, contents, isize)); 107*92bbfe1fSAlan Somers }, Eq(true)), 108*92bbfe1fSAlan Somers _) 109*92bbfe1fSAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 110*92bbfe1fSAlan Somers SET_OUT_HEADER_LEN(out, write); 111*92bbfe1fSAlan Somers out.body.write.size = osize; 112*92bbfe1fSAlan Somers }))); 113*92bbfe1fSAlan Somers } 114*92bbfe1fSAlan Somers 115*92bbfe1fSAlan Somers }; 116*92bbfe1fSAlan Somers 117*92bbfe1fSAlan Somers sig_atomic_t CopyFileRange::s_sigxfsz = 0; 118*92bbfe1fSAlan Somers 119*92bbfe1fSAlan Somers void sigxfsz_handler(int __unused sig) { 120*92bbfe1fSAlan Somers CopyFileRange::s_sigxfsz = 1; 121*92bbfe1fSAlan Somers } 122*92bbfe1fSAlan Somers 123*92bbfe1fSAlan Somers 124*92bbfe1fSAlan Somers class CopyFileRange_7_27: public CopyFileRange { 125*92bbfe1fSAlan Somers public: 126*92bbfe1fSAlan Somers virtual void SetUp() { 127*92bbfe1fSAlan Somers m_kernel_minor_version = 27; 128*92bbfe1fSAlan Somers CopyFileRange::SetUp(); 129*92bbfe1fSAlan Somers } 130*92bbfe1fSAlan Somers }; 131*92bbfe1fSAlan Somers 132*92bbfe1fSAlan Somers TEST_F(CopyFileRange, eio) 133*92bbfe1fSAlan Somers { 134*92bbfe1fSAlan Somers const char FULLPATH1[] = "mountpoint/src.txt"; 135*92bbfe1fSAlan Somers const char RELPATH1[] = "src.txt"; 136*92bbfe1fSAlan Somers const char FULLPATH2[] = "mountpoint/dst.txt"; 137*92bbfe1fSAlan Somers const char RELPATH2[] = "dst.txt"; 138*92bbfe1fSAlan Somers const uint64_t ino1 = 42; 139*92bbfe1fSAlan Somers const uint64_t ino2 = 43; 140*92bbfe1fSAlan Somers const uint64_t fh1 = 0xdeadbeef1a7ebabe; 141*92bbfe1fSAlan Somers const uint64_t fh2 = 0xdeadc0de88c0ffee; 142*92bbfe1fSAlan Somers off_t fsize1 = 1 << 20; /* 1 MiB */ 143*92bbfe1fSAlan Somers off_t fsize2 = 1 << 19; /* 512 KiB */ 144*92bbfe1fSAlan Somers off_t start1 = 1 << 18; 145*92bbfe1fSAlan Somers off_t start2 = 3 << 17; 146*92bbfe1fSAlan Somers ssize_t len = 65536; 147*92bbfe1fSAlan Somers int fd1, fd2; 148*92bbfe1fSAlan Somers 149*92bbfe1fSAlan Somers expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1); 150*92bbfe1fSAlan Somers expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1); 151*92bbfe1fSAlan Somers expect_open(ino1, 0, 1, fh1); 152*92bbfe1fSAlan Somers expect_open(ino2, 0, 1, fh2); 153*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 154*92bbfe1fSAlan Somers ResultOf([=](auto in) { 155*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_COPY_FILE_RANGE && 156*92bbfe1fSAlan Somers in.header.nodeid == ino1 && 157*92bbfe1fSAlan Somers in.body.copy_file_range.fh_in == fh1 && 158*92bbfe1fSAlan Somers (off_t)in.body.copy_file_range.off_in == start1 && 159*92bbfe1fSAlan Somers in.body.copy_file_range.nodeid_out == ino2 && 160*92bbfe1fSAlan Somers in.body.copy_file_range.fh_out == fh2 && 161*92bbfe1fSAlan Somers (off_t)in.body.copy_file_range.off_out == start2 && 162*92bbfe1fSAlan Somers in.body.copy_file_range.len == (size_t)len && 163*92bbfe1fSAlan Somers in.body.copy_file_range.flags == 0); 164*92bbfe1fSAlan Somers }, Eq(true)), 165*92bbfe1fSAlan Somers _) 166*92bbfe1fSAlan Somers ).WillOnce(Invoke(ReturnErrno(EIO))); 167*92bbfe1fSAlan Somers 168*92bbfe1fSAlan Somers fd1 = open(FULLPATH1, O_RDONLY); 169*92bbfe1fSAlan Somers fd2 = open(FULLPATH2, O_WRONLY); 170*92bbfe1fSAlan Somers ASSERT_EQ(-1, copy_file_range(fd1, &start1, fd2, &start2, len, 0)); 171*92bbfe1fSAlan Somers EXPECT_EQ(EIO, errno); 172*92bbfe1fSAlan Somers } 173*92bbfe1fSAlan Somers 174*92bbfe1fSAlan Somers /* 175*92bbfe1fSAlan Somers * If the server doesn't support FUSE_COPY_FILE_RANGE, the kernel should 176*92bbfe1fSAlan Somers * fallback to a read/write based implementation. 177*92bbfe1fSAlan Somers */ 178*92bbfe1fSAlan Somers TEST_F(CopyFileRange, fallback) 179*92bbfe1fSAlan Somers { 180*92bbfe1fSAlan Somers const char FULLPATH1[] = "mountpoint/src.txt"; 181*92bbfe1fSAlan Somers const char RELPATH1[] = "src.txt"; 182*92bbfe1fSAlan Somers const char FULLPATH2[] = "mountpoint/dst.txt"; 183*92bbfe1fSAlan Somers const char RELPATH2[] = "dst.txt"; 184*92bbfe1fSAlan Somers const uint64_t ino1 = 42; 185*92bbfe1fSAlan Somers const uint64_t ino2 = 43; 186*92bbfe1fSAlan Somers const uint64_t fh1 = 0xdeadbeef1a7ebabe; 187*92bbfe1fSAlan Somers const uint64_t fh2 = 0xdeadc0de88c0ffee; 188*92bbfe1fSAlan Somers off_t fsize2 = 0; 189*92bbfe1fSAlan Somers off_t start1 = 0; 190*92bbfe1fSAlan Somers off_t start2 = 0; 191*92bbfe1fSAlan Somers const char *contents = "Hello, world!"; 192*92bbfe1fSAlan Somers ssize_t len; 193*92bbfe1fSAlan Somers int fd1, fd2; 194*92bbfe1fSAlan Somers 195*92bbfe1fSAlan Somers len = strlen(contents); 196*92bbfe1fSAlan Somers 197*92bbfe1fSAlan Somers /* 198*92bbfe1fSAlan Somers * Ensure that we read to EOF, just so the buffer cache's read size is 199*92bbfe1fSAlan Somers * predictable. 200*92bbfe1fSAlan Somers */ 201*92bbfe1fSAlan Somers expect_lookup(RELPATH1, ino1, S_IFREG | 0644, start1 + len, 1); 202*92bbfe1fSAlan Somers expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1); 203*92bbfe1fSAlan Somers expect_open(ino1, 0, 1, fh1); 204*92bbfe1fSAlan Somers expect_open(ino2, 0, 1, fh2); 205*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 206*92bbfe1fSAlan Somers ResultOf([=](auto in) { 207*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_COPY_FILE_RANGE && 208*92bbfe1fSAlan Somers in.header.nodeid == ino1 && 209*92bbfe1fSAlan Somers in.body.copy_file_range.fh_in == fh1 && 210*92bbfe1fSAlan Somers (off_t)in.body.copy_file_range.off_in == start1 && 211*92bbfe1fSAlan Somers in.body.copy_file_range.nodeid_out == ino2 && 212*92bbfe1fSAlan Somers in.body.copy_file_range.fh_out == fh2 && 213*92bbfe1fSAlan Somers (off_t)in.body.copy_file_range.off_out == start2 && 214*92bbfe1fSAlan Somers in.body.copy_file_range.len == (size_t)len && 215*92bbfe1fSAlan Somers in.body.copy_file_range.flags == 0); 216*92bbfe1fSAlan Somers }, Eq(true)), 217*92bbfe1fSAlan Somers _) 218*92bbfe1fSAlan Somers ).WillOnce(Invoke(ReturnErrno(ENOSYS))); 219*92bbfe1fSAlan Somers expect_maybe_lseek(ino1); 220*92bbfe1fSAlan Somers expect_read(ino1, start1, len, len, contents, 0); 221*92bbfe1fSAlan Somers expect_write(ino2, start2, len, len, contents); 222*92bbfe1fSAlan Somers 223*92bbfe1fSAlan Somers fd1 = open(FULLPATH1, O_RDONLY); 224*92bbfe1fSAlan Somers ASSERT_GE(fd1, 0); 225*92bbfe1fSAlan Somers fd2 = open(FULLPATH2, O_WRONLY); 226*92bbfe1fSAlan Somers ASSERT_GE(fd2, 0); 227*92bbfe1fSAlan Somers ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0)); 228*92bbfe1fSAlan Somers } 229*92bbfe1fSAlan Somers 230*92bbfe1fSAlan Somers /* fusefs should respect RLIMIT_FSIZE */ 231*92bbfe1fSAlan Somers TEST_F(CopyFileRange, rlimit_fsize) 232*92bbfe1fSAlan Somers { 233*92bbfe1fSAlan Somers const char FULLPATH1[] = "mountpoint/src.txt"; 234*92bbfe1fSAlan Somers const char RELPATH1[] = "src.txt"; 235*92bbfe1fSAlan Somers const char FULLPATH2[] = "mountpoint/dst.txt"; 236*92bbfe1fSAlan Somers const char RELPATH2[] = "dst.txt"; 237*92bbfe1fSAlan Somers struct rlimit rl; 238*92bbfe1fSAlan Somers const uint64_t ino1 = 42; 239*92bbfe1fSAlan Somers const uint64_t ino2 = 43; 240*92bbfe1fSAlan Somers const uint64_t fh1 = 0xdeadbeef1a7ebabe; 241*92bbfe1fSAlan Somers const uint64_t fh2 = 0xdeadc0de88c0ffee; 242*92bbfe1fSAlan Somers off_t fsize1 = 1 << 20; /* 1 MiB */ 243*92bbfe1fSAlan Somers off_t fsize2 = 1 << 19; /* 512 KiB */ 244*92bbfe1fSAlan Somers off_t start1 = 1 << 18; 245*92bbfe1fSAlan Somers off_t start2 = fsize2; 246*92bbfe1fSAlan Somers ssize_t len = 65536; 247*92bbfe1fSAlan Somers int fd1, fd2; 248*92bbfe1fSAlan Somers 249*92bbfe1fSAlan Somers expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1); 250*92bbfe1fSAlan Somers expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1); 251*92bbfe1fSAlan Somers expect_open(ino1, 0, 1, fh1); 252*92bbfe1fSAlan Somers expect_open(ino2, 0, 1, fh2); 253*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 254*92bbfe1fSAlan Somers ResultOf([=](auto in) { 255*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_COPY_FILE_RANGE); 256*92bbfe1fSAlan Somers }, Eq(true)), 257*92bbfe1fSAlan Somers _) 258*92bbfe1fSAlan Somers ).Times(0); 259*92bbfe1fSAlan Somers 260*92bbfe1fSAlan Somers rl.rlim_cur = fsize2; 261*92bbfe1fSAlan Somers rl.rlim_max = 10 * fsize2; 262*92bbfe1fSAlan Somers ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rl)) << strerror(errno); 263*92bbfe1fSAlan Somers ASSERT_NE(SIG_ERR, signal(SIGXFSZ, sigxfsz_handler)) << strerror(errno); 264*92bbfe1fSAlan Somers 265*92bbfe1fSAlan Somers fd1 = open(FULLPATH1, O_RDONLY); 266*92bbfe1fSAlan Somers fd2 = open(FULLPATH2, O_WRONLY); 267*92bbfe1fSAlan Somers ASSERT_EQ(-1, copy_file_range(fd1, &start1, fd2, &start2, len, 0)); 268*92bbfe1fSAlan Somers EXPECT_EQ(EFBIG, errno); 269*92bbfe1fSAlan Somers EXPECT_EQ(1, s_sigxfsz); 270*92bbfe1fSAlan Somers } 271*92bbfe1fSAlan Somers 272*92bbfe1fSAlan Somers TEST_F(CopyFileRange, ok) 273*92bbfe1fSAlan Somers { 274*92bbfe1fSAlan Somers const char FULLPATH1[] = "mountpoint/src.txt"; 275*92bbfe1fSAlan Somers const char RELPATH1[] = "src.txt"; 276*92bbfe1fSAlan Somers const char FULLPATH2[] = "mountpoint/dst.txt"; 277*92bbfe1fSAlan Somers const char RELPATH2[] = "dst.txt"; 278*92bbfe1fSAlan Somers const uint64_t ino1 = 42; 279*92bbfe1fSAlan Somers const uint64_t ino2 = 43; 280*92bbfe1fSAlan Somers const uint64_t fh1 = 0xdeadbeef1a7ebabe; 281*92bbfe1fSAlan Somers const uint64_t fh2 = 0xdeadc0de88c0ffee; 282*92bbfe1fSAlan Somers off_t fsize1 = 1 << 20; /* 1 MiB */ 283*92bbfe1fSAlan Somers off_t fsize2 = 1 << 19; /* 512 KiB */ 284*92bbfe1fSAlan Somers off_t start1 = 1 << 18; 285*92bbfe1fSAlan Somers off_t start2 = 3 << 17; 286*92bbfe1fSAlan Somers ssize_t len = 65536; 287*92bbfe1fSAlan Somers int fd1, fd2; 288*92bbfe1fSAlan Somers 289*92bbfe1fSAlan Somers expect_lookup(RELPATH1, ino1, S_IFREG | 0644, fsize1, 1); 290*92bbfe1fSAlan Somers expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1); 291*92bbfe1fSAlan Somers expect_open(ino1, 0, 1, fh1); 292*92bbfe1fSAlan Somers expect_open(ino2, 0, 1, fh2); 293*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 294*92bbfe1fSAlan Somers ResultOf([=](auto in) { 295*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_COPY_FILE_RANGE && 296*92bbfe1fSAlan Somers in.header.nodeid == ino1 && 297*92bbfe1fSAlan Somers in.body.copy_file_range.fh_in == fh1 && 298*92bbfe1fSAlan Somers (off_t)in.body.copy_file_range.off_in == start1 && 299*92bbfe1fSAlan Somers in.body.copy_file_range.nodeid_out == ino2 && 300*92bbfe1fSAlan Somers in.body.copy_file_range.fh_out == fh2 && 301*92bbfe1fSAlan Somers (off_t)in.body.copy_file_range.off_out == start2 && 302*92bbfe1fSAlan Somers in.body.copy_file_range.len == (size_t)len && 303*92bbfe1fSAlan Somers in.body.copy_file_range.flags == 0); 304*92bbfe1fSAlan Somers }, Eq(true)), 305*92bbfe1fSAlan Somers _) 306*92bbfe1fSAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 307*92bbfe1fSAlan Somers SET_OUT_HEADER_LEN(out, write); 308*92bbfe1fSAlan Somers out.body.write.size = len; 309*92bbfe1fSAlan Somers }))); 310*92bbfe1fSAlan Somers 311*92bbfe1fSAlan Somers fd1 = open(FULLPATH1, O_RDONLY); 312*92bbfe1fSAlan Somers fd2 = open(FULLPATH2, O_WRONLY); 313*92bbfe1fSAlan Somers ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0)); 314*92bbfe1fSAlan Somers } 315*92bbfe1fSAlan Somers 316*92bbfe1fSAlan Somers /* 317*92bbfe1fSAlan Somers * copy_file_range can make copies within a single file, as long as the ranges 318*92bbfe1fSAlan Somers * don't overlap. 319*92bbfe1fSAlan Somers * */ 320*92bbfe1fSAlan Somers TEST_F(CopyFileRange, same_file) 321*92bbfe1fSAlan Somers { 322*92bbfe1fSAlan Somers const char FULLPATH[] = "mountpoint/src.txt"; 323*92bbfe1fSAlan Somers const char RELPATH[] = "src.txt"; 324*92bbfe1fSAlan Somers const uint64_t ino = 4; 325*92bbfe1fSAlan Somers const uint64_t fh = 0xdeadbeefa7ebabe; 326*92bbfe1fSAlan Somers off_t fsize = 1 << 20; /* 1 MiB */ 327*92bbfe1fSAlan Somers off_t off_in = 1 << 18; 328*92bbfe1fSAlan Somers off_t off_out = 3 << 17; 329*92bbfe1fSAlan Somers ssize_t len = 65536; 330*92bbfe1fSAlan Somers int fd; 331*92bbfe1fSAlan Somers 332*92bbfe1fSAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, fsize, 1); 333*92bbfe1fSAlan Somers expect_open(ino, 0, 1, fh); 334*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 335*92bbfe1fSAlan Somers ResultOf([=](auto in) { 336*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_COPY_FILE_RANGE && 337*92bbfe1fSAlan Somers in.header.nodeid == ino && 338*92bbfe1fSAlan Somers in.body.copy_file_range.fh_in == fh && 339*92bbfe1fSAlan Somers (off_t)in.body.copy_file_range.off_in == off_in && 340*92bbfe1fSAlan Somers in.body.copy_file_range.nodeid_out == ino && 341*92bbfe1fSAlan Somers in.body.copy_file_range.fh_out == fh && 342*92bbfe1fSAlan Somers (off_t)in.body.copy_file_range.off_out == off_out && 343*92bbfe1fSAlan Somers in.body.copy_file_range.len == (size_t)len && 344*92bbfe1fSAlan Somers in.body.copy_file_range.flags == 0); 345*92bbfe1fSAlan Somers }, Eq(true)), 346*92bbfe1fSAlan Somers _) 347*92bbfe1fSAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 348*92bbfe1fSAlan Somers SET_OUT_HEADER_LEN(out, write); 349*92bbfe1fSAlan Somers out.body.write.size = len; 350*92bbfe1fSAlan Somers }))); 351*92bbfe1fSAlan Somers 352*92bbfe1fSAlan Somers fd = open(FULLPATH, O_RDWR); 353*92bbfe1fSAlan Somers ASSERT_EQ(len, copy_file_range(fd, &off_in, fd, &off_out, len, 0)); 354*92bbfe1fSAlan Somers } 355*92bbfe1fSAlan Somers 356*92bbfe1fSAlan Somers /* With older protocol versions, no FUSE_COPY_FILE_RANGE should be attempted */ 357*92bbfe1fSAlan Somers TEST_F(CopyFileRange_7_27, fallback) 358*92bbfe1fSAlan Somers { 359*92bbfe1fSAlan Somers const char FULLPATH1[] = "mountpoint/src.txt"; 360*92bbfe1fSAlan Somers const char RELPATH1[] = "src.txt"; 361*92bbfe1fSAlan Somers const char FULLPATH2[] = "mountpoint/dst.txt"; 362*92bbfe1fSAlan Somers const char RELPATH2[] = "dst.txt"; 363*92bbfe1fSAlan Somers const uint64_t ino1 = 42; 364*92bbfe1fSAlan Somers const uint64_t ino2 = 43; 365*92bbfe1fSAlan Somers const uint64_t fh1 = 0xdeadbeef1a7ebabe; 366*92bbfe1fSAlan Somers const uint64_t fh2 = 0xdeadc0de88c0ffee; 367*92bbfe1fSAlan Somers off_t fsize2 = 0; 368*92bbfe1fSAlan Somers off_t start1 = 0; 369*92bbfe1fSAlan Somers off_t start2 = 0; 370*92bbfe1fSAlan Somers const char *contents = "Hello, world!"; 371*92bbfe1fSAlan Somers ssize_t len; 372*92bbfe1fSAlan Somers int fd1, fd2; 373*92bbfe1fSAlan Somers 374*92bbfe1fSAlan Somers len = strlen(contents); 375*92bbfe1fSAlan Somers 376*92bbfe1fSAlan Somers /* 377*92bbfe1fSAlan Somers * Ensure that we read to EOF, just so the buffer cache's read size is 378*92bbfe1fSAlan Somers * predictable. 379*92bbfe1fSAlan Somers */ 380*92bbfe1fSAlan Somers expect_lookup(RELPATH1, ino1, S_IFREG | 0644, start1 + len, 1); 381*92bbfe1fSAlan Somers expect_lookup(RELPATH2, ino2, S_IFREG | 0644, fsize2, 1); 382*92bbfe1fSAlan Somers expect_open(ino1, 0, 1, fh1); 383*92bbfe1fSAlan Somers expect_open(ino2, 0, 1, fh2); 384*92bbfe1fSAlan Somers EXPECT_CALL(*m_mock, process( 385*92bbfe1fSAlan Somers ResultOf([=](auto in) { 386*92bbfe1fSAlan Somers return (in.header.opcode == FUSE_COPY_FILE_RANGE); 387*92bbfe1fSAlan Somers }, Eq(true)), 388*92bbfe1fSAlan Somers _) 389*92bbfe1fSAlan Somers ).Times(0); 390*92bbfe1fSAlan Somers expect_maybe_lseek(ino1); 391*92bbfe1fSAlan Somers expect_read(ino1, start1, len, len, contents, 0); 392*92bbfe1fSAlan Somers expect_write(ino2, start2, len, len, contents); 393*92bbfe1fSAlan Somers 394*92bbfe1fSAlan Somers fd1 = open(FULLPATH1, O_RDONLY); 395*92bbfe1fSAlan Somers ASSERT_GE(fd1, 0); 396*92bbfe1fSAlan Somers fd2 = open(FULLPATH2, O_WRONLY); 397*92bbfe1fSAlan Somers ASSERT_GE(fd2, 0); 398*92bbfe1fSAlan Somers ASSERT_EQ(len, copy_file_range(fd1, &start1, fd2, &start2, len, 0)); 399*92bbfe1fSAlan Somers } 400*92bbfe1fSAlan Somers 401*92bbfe1fSAlan Somers 402