1a1c9f4adSAlan Somers /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3a1c9f4adSAlan Somers *
4a1c9f4adSAlan Somers * Copyright (c) 2019 The FreeBSD Foundation
5a1c9f4adSAlan Somers *
6a1c9f4adSAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship
7a1c9f4adSAlan Somers * from the FreeBSD Foundation.
8a1c9f4adSAlan Somers *
9a1c9f4adSAlan Somers * Redistribution and use in source and binary forms, with or without
10a1c9f4adSAlan Somers * modification, are permitted provided that the following conditions
11a1c9f4adSAlan Somers * are met:
12a1c9f4adSAlan Somers * 1. Redistributions of source code must retain the above copyright
13a1c9f4adSAlan Somers * notice, this list of conditions and the following disclaimer.
14a1c9f4adSAlan Somers * 2. Redistributions in binary form must reproduce the above copyright
15a1c9f4adSAlan Somers * notice, this list of conditions and the following disclaimer in the
16a1c9f4adSAlan Somers * documentation and/or other materials provided with the distribution.
17a1c9f4adSAlan Somers *
18a1c9f4adSAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19a1c9f4adSAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20a1c9f4adSAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21a1c9f4adSAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22a1c9f4adSAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23a1c9f4adSAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24a1c9f4adSAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25a1c9f4adSAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26a1c9f4adSAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27a1c9f4adSAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28a1c9f4adSAlan Somers * SUCH DAMAGE.
29a1c9f4adSAlan Somers */
30a1c9f4adSAlan Somers
31a1c9f4adSAlan Somers extern "C" {
32a1c9f4adSAlan Somers #include <sys/param.h>
33a1c9f4adSAlan Somers #include <sys/ioctl.h>
34a1c9f4adSAlan Somers #include <sys/filio.h>
35a1c9f4adSAlan Somers
36a1c9f4adSAlan Somers #include <fcntl.h>
37a1c9f4adSAlan Somers }
38a1c9f4adSAlan Somers
39a1c9f4adSAlan Somers #include "mockfs.hh"
40a1c9f4adSAlan Somers #include "utils.hh"
41a1c9f4adSAlan Somers
42a1c9f4adSAlan Somers using namespace testing;
43a1c9f4adSAlan Somers
44a1c9f4adSAlan Somers const static char FULLPATH[] = "mountpoint/foo";
45a1c9f4adSAlan Somers const static char RELPATH[] = "foo";
46a1c9f4adSAlan Somers
47a1c9f4adSAlan Somers class Bmap: public FuseTest {
48a1c9f4adSAlan Somers public:
SetUp()49a1c9f4adSAlan Somers virtual void SetUp() {
50a1c9f4adSAlan Somers m_maxreadahead = UINT32_MAX;
51a1c9f4adSAlan Somers FuseTest::SetUp();
52a1c9f4adSAlan Somers }
expect_bmap(uint64_t ino,uint64_t lbn,uint32_t blocksize,uint64_t pbn)53a1c9f4adSAlan Somers void expect_bmap(uint64_t ino, uint64_t lbn, uint32_t blocksize, uint64_t pbn)
54a1c9f4adSAlan Somers {
55a1c9f4adSAlan Somers EXPECT_CALL(*m_mock, process(
56a1c9f4adSAlan Somers ResultOf([=](auto in) {
57a1c9f4adSAlan Somers return (in.header.opcode == FUSE_BMAP &&
58a1c9f4adSAlan Somers in.header.nodeid == ino &&
59a1c9f4adSAlan Somers in.body.bmap.block == lbn &&
60a1c9f4adSAlan Somers in.body.bmap.blocksize == blocksize);
61a1c9f4adSAlan Somers }, Eq(true)),
62a1c9f4adSAlan Somers _)
63a1c9f4adSAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
64a1c9f4adSAlan Somers SET_OUT_HEADER_LEN(out, bmap);
65a1c9f4adSAlan Somers out.body.bmap.block = pbn;
66a1c9f4adSAlan Somers })));
67a1c9f4adSAlan Somers }
68a1c9f4adSAlan Somers
expect_lookup(const char * relpath,uint64_t ino,off_t size)69a1c9f4adSAlan Somers void expect_lookup(const char *relpath, uint64_t ino, off_t size)
70a1c9f4adSAlan Somers {
71a1c9f4adSAlan Somers FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1,
72a1c9f4adSAlan Somers UINT64_MAX);
73a1c9f4adSAlan Somers }
74a1c9f4adSAlan Somers };
75a1c9f4adSAlan Somers
767430017bSAlan Somers class BmapEof: public Bmap, public WithParamInterface<int> {};
777430017bSAlan Somers
78a1c9f4adSAlan Somers /*
79a1c9f4adSAlan Somers * Test FUSE_BMAP
80a1c9f4adSAlan Somers */
TEST_F(Bmap,bmap)81a1c9f4adSAlan Somers TEST_F(Bmap, bmap)
82a1c9f4adSAlan Somers {
83a1c9f4adSAlan Somers struct fiobmap2_arg arg;
84f928dbcbSAlan Somers /*
85f928dbcbSAlan Somers * Pick fsize and lbn large enough that max length runs won't reach
86f928dbcbSAlan Somers * either beginning or end of file
87f928dbcbSAlan Somers */
88f928dbcbSAlan Somers const off_t filesize = 1 << 30;
89f928dbcbSAlan Somers int64_t lbn = 100;
90a1c9f4adSAlan Somers int64_t pbn = 12345;
91f928dbcbSAlan Somers const ino_t ino = 42;
92a1c9f4adSAlan Somers int fd;
93a1c9f4adSAlan Somers
94a1c9f4adSAlan Somers expect_lookup(RELPATH, 42, filesize);
95a1c9f4adSAlan Somers expect_open(ino, 0, 1);
96a1c9f4adSAlan Somers expect_bmap(ino, lbn, m_maxbcachebuf, pbn);
97a1c9f4adSAlan Somers
98a1c9f4adSAlan Somers fd = open(FULLPATH, O_RDWR);
99a1c9f4adSAlan Somers ASSERT_LE(0, fd) << strerror(errno);
100a1c9f4adSAlan Somers
101a1c9f4adSAlan Somers arg.bn = lbn;
102a1c9f4adSAlan Somers arg.runp = -1;
103a1c9f4adSAlan Somers arg.runb = -1;
104a1c9f4adSAlan Somers ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
105a1c9f4adSAlan Somers EXPECT_EQ(arg.bn, pbn);
106*b2792a30SAlan Somers /*
107*b2792a30SAlan Somers * XXX The FUSE protocol does not include the runp and runb variables,
108*b2792a30SAlan Somers * so those must be guessed in-kernel. There's no "right" answer, so
109*b2792a30SAlan Somers * just check that they're within reasonable limits.
110*b2792a30SAlan Somers */
111*b2792a30SAlan Somers EXPECT_LE(arg.runb, lbn);
112*b2792a30SAlan Somers EXPECT_LE((unsigned long)arg.runb, m_maxreadahead / m_maxbcachebuf);
113*b2792a30SAlan Somers EXPECT_LE((unsigned long)arg.runb, m_maxphys / m_maxbcachebuf);
114*b2792a30SAlan Somers EXPECT_GT(arg.runb, 0);
115*b2792a30SAlan Somers EXPECT_LE(arg.runp, filesize / m_maxbcachebuf - lbn);
116*b2792a30SAlan Somers EXPECT_LE((unsigned long)arg.runp, m_maxreadahead / m_maxbcachebuf);
117*b2792a30SAlan Somers EXPECT_LE((unsigned long)arg.runp, m_maxphys / m_maxbcachebuf);
118*b2792a30SAlan Somers EXPECT_GT(arg.runp, 0);
1194ac4b126SAlan Somers
1204ac4b126SAlan Somers leak(fd);
121a1c9f4adSAlan Somers }
122a1c9f4adSAlan Somers
123a1c9f4adSAlan Somers /*
124a1c9f4adSAlan Somers * If the daemon does not implement VOP_BMAP, fusefs should return sensible
125a1c9f4adSAlan Somers * defaults.
126a1c9f4adSAlan Somers */
TEST_F(Bmap,default_)127a1c9f4adSAlan Somers TEST_F(Bmap, default_)
128a1c9f4adSAlan Somers {
129a1c9f4adSAlan Somers struct fiobmap2_arg arg;
130f928dbcbSAlan Somers const off_t filesize = 1 << 30;
131a1c9f4adSAlan Somers const ino_t ino = 42;
132a1c9f4adSAlan Somers int64_t lbn;
133a1c9f4adSAlan Somers int fd;
134a1c9f4adSAlan Somers
135a1c9f4adSAlan Somers expect_lookup(RELPATH, 42, filesize);
136a1c9f4adSAlan Somers expect_open(ino, 0, 1);
137a1c9f4adSAlan Somers EXPECT_CALL(*m_mock, process(
138a1c9f4adSAlan Somers ResultOf([=](auto in) {
139a1c9f4adSAlan Somers return (in.header.opcode == FUSE_BMAP);
140a1c9f4adSAlan Somers }, Eq(true)),
141a1c9f4adSAlan Somers _)
142a1c9f4adSAlan Somers ).WillOnce(Invoke(ReturnErrno(ENOSYS)));
143a1c9f4adSAlan Somers
144a1c9f4adSAlan Somers fd = open(FULLPATH, O_RDWR);
145a1c9f4adSAlan Somers ASSERT_LE(0, fd) << strerror(errno);
146a1c9f4adSAlan Somers
147a1c9f4adSAlan Somers /* First block */
148a1c9f4adSAlan Somers lbn = 0;
149a1c9f4adSAlan Somers arg.bn = lbn;
150a1c9f4adSAlan Somers arg.runp = -1;
151a1c9f4adSAlan Somers arg.runb = -1;
152a1c9f4adSAlan Somers ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
153a1c9f4adSAlan Somers EXPECT_EQ(arg.bn, 0);
154e9b411d2SGleb Smirnoff EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
155a1c9f4adSAlan Somers EXPECT_EQ(arg.runb, 0);
156a1c9f4adSAlan Somers
157a1c9f4adSAlan Somers /* In the middle */
158a1c9f4adSAlan Somers lbn = filesize / m_maxbcachebuf / 2;
159a1c9f4adSAlan Somers arg.bn = lbn;
160a1c9f4adSAlan Somers arg.runp = -1;
161a1c9f4adSAlan Somers arg.runb = -1;
162a1c9f4adSAlan Somers ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
163a1c9f4adSAlan Somers EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
164e9b411d2SGleb Smirnoff EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
165e9b411d2SGleb Smirnoff EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
166a1c9f4adSAlan Somers
167a1c9f4adSAlan Somers /* Last block */
168a1c9f4adSAlan Somers lbn = filesize / m_maxbcachebuf - 1;
169a1c9f4adSAlan Somers arg.bn = lbn;
170a1c9f4adSAlan Somers arg.runp = -1;
171a1c9f4adSAlan Somers arg.runb = -1;
172a1c9f4adSAlan Somers ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
173a1c9f4adSAlan Somers EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
174a1c9f4adSAlan Somers EXPECT_EQ(arg.runp, 0);
175e9b411d2SGleb Smirnoff EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
1768e765737SAlan Somers
1778e765737SAlan Somers leak(fd);
178a1c9f4adSAlan Somers }
1797430017bSAlan Somers
1807430017bSAlan Somers /*
1817430017bSAlan Somers * VOP_BMAP should not query the server for the file's size, even if its cached
1827430017bSAlan Somers * attributes have expired.
1837430017bSAlan Somers * Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
1847430017bSAlan Somers */
TEST_P(BmapEof,eof)1857430017bSAlan Somers TEST_P(BmapEof, eof)
1867430017bSAlan Somers {
1877430017bSAlan Somers /*
1887430017bSAlan Somers * Outline:
1897430017bSAlan Somers * 1) lookup the file, setting attr_valid=0
1907430017bSAlan Somers * 2) Read more than one block, causing the kernel to issue VOP_BMAP to
1917430017bSAlan Somers * plan readahead.
1927430017bSAlan Somers * 3) Nothing should panic
1937430017bSAlan Somers * 4) Repeat the tests, truncating the file after different numbers of
1947430017bSAlan Somers * GETATTR operations.
1957430017bSAlan Somers */
1967430017bSAlan Somers Sequence seq;
1977430017bSAlan Somers const off_t filesize = 2 * m_maxbcachebuf;
1987430017bSAlan Somers const ino_t ino = 42;
1997430017bSAlan Somers mode_t mode = S_IFREG | 0644;
2008bae22bbSAlan Somers char *buf;
2017430017bSAlan Somers int fd;
2027430017bSAlan Somers int ngetattrs;
2037430017bSAlan Somers
2047430017bSAlan Somers ngetattrs = GetParam();
2057430017bSAlan Somers FuseTest::expect_lookup(RELPATH, ino, mode, filesize, 1, 0);
2067430017bSAlan Somers expect_open(ino, 0, 1);
2077430017bSAlan Somers // Depending on ngetattrs, FUSE_READ could be called with either
2087430017bSAlan Somers // filesize or filesize / 2 .
2097430017bSAlan Somers EXPECT_CALL(*m_mock, process(
2107430017bSAlan Somers ResultOf([=](auto in) {
2117430017bSAlan Somers return (in.header.opcode == FUSE_READ &&
2127430017bSAlan Somers in.header.nodeid == ino &&
2137430017bSAlan Somers in.body.read.offset == 0 &&
2147430017bSAlan Somers ( in.body.read.size == filesize ||
2157430017bSAlan Somers in.body.read.size == filesize / 2));
2167430017bSAlan Somers }, Eq(true)),
2177430017bSAlan Somers _)
2187430017bSAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
2197430017bSAlan Somers size_t osize = in.body.read.size;
2200c9df4afSAlan Somers
2210c9df4afSAlan Somers assert(osize < sizeof(out.body.bytes));
2227430017bSAlan Somers out.header.len = sizeof(struct fuse_out_header) + osize;
2237430017bSAlan Somers bzero(out.body.bytes, osize);
2247430017bSAlan Somers })));
2257430017bSAlan Somers EXPECT_CALL(*m_mock, process(
2267430017bSAlan Somers ResultOf([](auto in) {
2277430017bSAlan Somers return (in.header.opcode == FUSE_GETATTR &&
2287430017bSAlan Somers in.header.nodeid == ino);
2297430017bSAlan Somers }, Eq(true)),
2307430017bSAlan Somers _)
2317430017bSAlan Somers ).Times(Between(ngetattrs - 1, ngetattrs))
2327430017bSAlan Somers .InSequence(seq)
2337430017bSAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
2347430017bSAlan Somers SET_OUT_HEADER_LEN(out, attr);
2357430017bSAlan Somers out.body.attr.attr_valid = 0;
2367430017bSAlan Somers out.body.attr.attr.ino = ino;
2377430017bSAlan Somers out.body.attr.attr.mode = S_IFREG | 0644;
2387430017bSAlan Somers out.body.attr.attr.size = filesize;
2397430017bSAlan Somers })));
2407430017bSAlan Somers EXPECT_CALL(*m_mock, process(
2417430017bSAlan Somers ResultOf([](auto in) {
2427430017bSAlan Somers return (in.header.opcode == FUSE_GETATTR &&
2437430017bSAlan Somers in.header.nodeid == ino);
2447430017bSAlan Somers }, Eq(true)),
2457430017bSAlan Somers _)
2467430017bSAlan Somers ).InSequence(seq)
2477430017bSAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
2487430017bSAlan Somers SET_OUT_HEADER_LEN(out, attr);
2497430017bSAlan Somers out.body.attr.attr_valid = 0;
2507430017bSAlan Somers out.body.attr.attr.ino = ino;
2517430017bSAlan Somers out.body.attr.attr.mode = S_IFREG | 0644;
2527430017bSAlan Somers out.body.attr.attr.size = filesize / 2;
2537430017bSAlan Somers })));
2547430017bSAlan Somers
2558bae22bbSAlan Somers buf = new char[filesize]();
2567430017bSAlan Somers fd = open(FULLPATH, O_RDWR);
2577430017bSAlan Somers ASSERT_LE(0, fd) << strerror(errno);
2587430017bSAlan Somers read(fd, buf, filesize);
2594ac4b126SAlan Somers
2608bae22bbSAlan Somers delete[] buf;
2614ac4b126SAlan Somers leak(fd);
2627430017bSAlan Somers }
2637430017bSAlan Somers
264811e0a31SEnji Cooper INSTANTIATE_TEST_SUITE_P(BE, BmapEof,
2657430017bSAlan Somers Values(1, 2, 3)
2667430017bSAlan Somers );
267