Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/fs/fusefs/bmap.cc
39537 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
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
class BmapEof: public Bmap, public WithParamInterface<int> {};
77
78
/*
79
* Test FUSE_BMAP
80
*/
81
TEST_F(Bmap, bmap)
82
{
83
struct fiobmap2_arg arg;
84
/*
85
* Pick fsize and lbn large enough that max length runs won't reach
86
* either beginning or end of file
87
*/
88
const off_t filesize = 1 << 30;
89
int64_t lbn = 100;
90
int64_t pbn = 12345;
91
const ino_t ino = 42;
92
int fd;
93
94
expect_lookup(RELPATH, 42, filesize);
95
expect_open(ino, 0, 1);
96
expect_bmap(ino, lbn, m_maxbcachebuf, pbn);
97
98
fd = open(FULLPATH, O_RDWR);
99
ASSERT_LE(0, fd) << strerror(errno);
100
101
arg.bn = lbn;
102
arg.runp = -1;
103
arg.runb = -1;
104
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
105
EXPECT_EQ(arg.bn, pbn);
106
/*
107
* XXX The FUSE protocol does not include the runp and runb variables,
108
* so those must be guessed in-kernel. There's no "right" answer, so
109
* just check that they're within reasonable limits.
110
*/
111
EXPECT_LE(arg.runb, lbn);
112
EXPECT_LE((unsigned long)arg.runb, m_maxreadahead / m_maxbcachebuf);
113
EXPECT_LE((unsigned long)arg.runb, m_maxphys / m_maxbcachebuf);
114
EXPECT_GT(arg.runb, 0);
115
EXPECT_LE(arg.runp, filesize / m_maxbcachebuf - lbn);
116
EXPECT_LE((unsigned long)arg.runp, m_maxreadahead / m_maxbcachebuf);
117
EXPECT_LE((unsigned long)arg.runp, m_maxphys / m_maxbcachebuf);
118
EXPECT_GT(arg.runp, 0);
119
120
leak(fd);
121
}
122
123
/*
124
* If the daemon does not implement VOP_BMAP, fusefs should return sensible
125
* defaults.
126
*/
127
TEST_F(Bmap, default_)
128
{
129
struct fiobmap2_arg arg;
130
const off_t filesize = 1 << 30;
131
const ino_t ino = 42;
132
int64_t lbn;
133
int fd;
134
135
expect_lookup(RELPATH, 42, filesize);
136
expect_open(ino, 0, 1);
137
EXPECT_CALL(*m_mock, process(
138
ResultOf([=](auto in) {
139
return (in.header.opcode == FUSE_BMAP);
140
}, Eq(true)),
141
_)
142
).WillOnce(Invoke(ReturnErrno(ENOSYS)));
143
144
fd = open(FULLPATH, O_RDWR);
145
ASSERT_LE(0, fd) << strerror(errno);
146
147
/* First block */
148
lbn = 0;
149
arg.bn = lbn;
150
arg.runp = -1;
151
arg.runb = -1;
152
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
153
EXPECT_EQ(arg.bn, 0);
154
EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
155
EXPECT_EQ(arg.runb, 0);
156
157
/* In the middle */
158
lbn = filesize / m_maxbcachebuf / 2;
159
arg.bn = lbn;
160
arg.runp = -1;
161
arg.runb = -1;
162
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
163
EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
164
EXPECT_EQ((unsigned long )arg.runp, m_maxphys / m_maxbcachebuf - 1);
165
EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
166
167
/* Last block */
168
lbn = filesize / m_maxbcachebuf - 1;
169
arg.bn = lbn;
170
arg.runp = -1;
171
arg.runb = -1;
172
ASSERT_EQ(0, ioctl(fd, FIOBMAP2, &arg)) << strerror(errno);
173
EXPECT_EQ(arg.bn, lbn * m_maxbcachebuf / DEV_BSIZE);
174
EXPECT_EQ(arg.runp, 0);
175
EXPECT_EQ((unsigned long )arg.runb, m_maxphys / m_maxbcachebuf - 1);
176
177
leak(fd);
178
}
179
180
/*
181
* VOP_BMAP should not query the server for the file's size, even if its cached
182
* attributes have expired.
183
* Regression test for https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=256937
184
*/
185
TEST_P(BmapEof, eof)
186
{
187
/*
188
* Outline:
189
* 1) lookup the file, setting attr_valid=0
190
* 2) Read more than one block, causing the kernel to issue VOP_BMAP to
191
* plan readahead.
192
* 3) Nothing should panic
193
* 4) Repeat the tests, truncating the file after different numbers of
194
* GETATTR operations.
195
*/
196
Sequence seq;
197
const off_t filesize = 2 * m_maxbcachebuf;
198
const ino_t ino = 42;
199
mode_t mode = S_IFREG | 0644;
200
char *buf;
201
int fd;
202
int ngetattrs;
203
204
ngetattrs = GetParam();
205
FuseTest::expect_lookup(RELPATH, ino, mode, filesize, 1, 0);
206
expect_open(ino, 0, 1);
207
// Depending on ngetattrs, FUSE_READ could be called with either
208
// filesize or filesize / 2 .
209
EXPECT_CALL(*m_mock, process(
210
ResultOf([=](auto in) {
211
return (in.header.opcode == FUSE_READ &&
212
in.header.nodeid == ino &&
213
in.body.read.offset == 0 &&
214
( in.body.read.size == filesize ||
215
in.body.read.size == filesize / 2));
216
}, Eq(true)),
217
_)
218
).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) {
219
size_t osize = in.body.read.size;
220
221
assert(osize < sizeof(out.body.bytes));
222
out.header.len = sizeof(struct fuse_out_header) + osize;
223
bzero(out.body.bytes, osize);
224
})));
225
EXPECT_CALL(*m_mock, process(
226
ResultOf([](auto in) {
227
return (in.header.opcode == FUSE_GETATTR &&
228
in.header.nodeid == ino);
229
}, Eq(true)),
230
_)
231
).Times(Between(ngetattrs - 1, ngetattrs))
232
.InSequence(seq)
233
.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
234
SET_OUT_HEADER_LEN(out, attr);
235
out.body.attr.attr_valid = 0;
236
out.body.attr.attr.ino = ino;
237
out.body.attr.attr.mode = S_IFREG | 0644;
238
out.body.attr.attr.size = filesize;
239
})));
240
EXPECT_CALL(*m_mock, process(
241
ResultOf([](auto in) {
242
return (in.header.opcode == FUSE_GETATTR &&
243
in.header.nodeid == ino);
244
}, Eq(true)),
245
_)
246
).InSequence(seq)
247
.WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) {
248
SET_OUT_HEADER_LEN(out, attr);
249
out.body.attr.attr_valid = 0;
250
out.body.attr.attr.ino = ino;
251
out.body.attr.attr.mode = S_IFREG | 0644;
252
out.body.attr.attr.size = filesize / 2;
253
})));
254
255
buf = new char[filesize]();
256
fd = open(FULLPATH, O_RDWR);
257
ASSERT_LE(0, fd) << strerror(errno);
258
read(fd, buf, filesize);
259
260
delete[] buf;
261
leak(fd);
262
}
263
264
INSTANTIATE_TEST_SUITE_P(BE, BmapEof,
265
Values(1, 2, 3)
266
);
267
268