1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 The FreeBSD Foundation 5 * 6 * This software was developed by BFF Storage Systems, LLC under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 extern "C" { 32 #include <sys/param.h> 33 #include <sys/ioctl.h> 34 #include <sys/filio.h> 35 36 #include <fcntl.h> 37 } 38 39 #include "mockfs.hh" 40 #include "utils.hh" 41 42 using namespace testing; 43 44 const static char FULLPATH[] = "mountpoint/foo"; 45 const static char RELPATH[] = "foo"; 46 47 class Bmap: public FuseTest { 48 public: 49 virtual void SetUp() { 50 m_maxreadahead = UINT32_MAX; 51 FuseTest::SetUp(); 52 } 53 void expect_bmap(uint64_t ino, uint64_t lbn, uint32_t blocksize, uint64_t pbn) 54 { 55 EXPECT_CALL(*m_mock, process( 56 ResultOf([=](auto in) { 57 return (in.header.opcode == FUSE_BMAP && 58 in.header.nodeid == ino && 59 in.body.bmap.block == lbn && 60 in.body.bmap.blocksize == blocksize); 61 }, Eq(true)), 62 _) 63 ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 64 SET_OUT_HEADER_LEN(out, bmap); 65 out.body.bmap.block = pbn; 66 }))); 67 } 68 69 void expect_lookup(const char *relpath, uint64_t ino, off_t size) 70 { 71 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1, 72 UINT64_MAX); 73 } 74 }; 75 76 /* 77 * Test FUSE_BMAP 78 * XXX The FUSE protocol does not include the runp and runb variables, so those 79 * must be guessed in-kernel. 80 */ 81 TEST_F(Bmap, bmap) 82 { 83 struct fiobmap2_arg arg; 84 const off_t filesize = 1 << 20; 85 const ino_t ino = 42; 86 int64_t lbn = 10; 87 int64_t pbn = 12345; 88 int fd; 89 90 expect_lookup(RELPATH, 42, filesize); 91 expect_open(ino, 0, 1); 92 expect_bmap(ino, lbn, m_maxbcachebuf, pbn); 93 94 fd = open(FULLPATH, O_RDWR); 95 ASSERT_LE(0, fd) << strerror(errno); 96 97 arg.bn = lbn; 98 arg.runp = -1; 99 arg.runb = -1; 100 ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno); 101 EXPECT_EQ(arg.bn, pbn); 102 EXPECT_EQ(arg.runp, m_maxphys / m_maxbcachebuf - 1); 103 EXPECT_EQ(arg.runb, m_maxphys / m_maxbcachebuf - 1); 104 } 105 106 /* 107 * If the daemon does not implement VOP_BMAP, fusefs should return sensible 108 * defaults. 109 */ 110 TEST_F(Bmap, default_) 111 { 112 struct fiobmap2_arg arg; 113 const off_t filesize = 1 << 20; 114 const ino_t ino = 42; 115 int64_t lbn; 116 int fd; 117 118 expect_lookup(RELPATH, 42, filesize); 119 expect_open(ino, 0, 1); 120 EXPECT_CALL(*m_mock, process( 121 ResultOf([=](auto in) { 122 return (in.header.opcode == FUSE_BMAP); 123 }, Eq(true)), 124 _) 125 ).WillOnce(Invoke(ReturnErrno(ENOSYS))); 126 127 fd = open(FULLPATH, O_RDWR); 128 ASSERT_LE(0, fd) << strerror(errno); 129 130 /* First block */ 131 lbn = 0; 132 arg.bn = lbn; 133 arg.runp = -1; 134 arg.runb = -1; 135 ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno); 136 EXPECT_EQ(arg.bn, 0); 137 EXPECT_EQ(arg.runp, m_maxphys / m_maxbcachebuf - 1); 138 EXPECT_EQ(arg.runb, 0); 139 140 /* In the middle */ 141 lbn = filesize / m_maxbcachebuf / 2; 142 arg.bn = lbn; 143 arg.runp = -1; 144 arg.runb = -1; 145 ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno); 146 EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE); 147 EXPECT_EQ(arg.runp, m_maxphys / m_maxbcachebuf - 1); 148 EXPECT_EQ(arg.runb, m_maxphys / m_maxbcachebuf - 1); 149 150 /* Last block */ 151 lbn = filesize / m_maxbcachebuf - 1; 152 arg.bn = lbn; 153 arg.runp = -1; 154 arg.runb = -1; 155 ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno); 156 EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE); 157 EXPECT_EQ(arg.runp, 0); 158 EXPECT_EQ(arg.runb, m_maxphys / m_maxbcachebuf - 1); 159 } 160