Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/fs/fusefs/ioctl.cc
96309 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2025 CismonX <[email protected]>
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
extern "C" {
29
#include <sys/types.h>
30
#include <sys/ioctl.h>
31
#include <fcntl.h>
32
#include <string.h>
33
}
34
35
#include "mockfs.hh"
36
#include "utils.hh"
37
38
using namespace testing;
39
40
using IoctlTestProcT = std::function<void (int)>;
41
42
static const char INPUT_DATA[] = "input_data";
43
static const char OUTPUT_DATA[] = "output_data";
44
45
class Ioctl: public FuseTest {
46
public:
47
void expect_ioctl(uint64_t ino, ProcessMockerT r)
48
{
49
EXPECT_CALL(*m_mock, process(
50
ResultOf([=](auto in) {
51
return (in.header.opcode == FUSE_IOCTL &&
52
in.header.nodeid == ino);
53
}, Eq(true)), _)
54
).WillOnce(Invoke(r)).RetiresOnSaturation();
55
}
56
57
void expect_ioctl_rw(uint64_t ino)
58
{
59
/*
60
* _IOR(): Compare the input data with INPUT_DATA.
61
* _IOW(): Copy out OUTPUT_DATA.
62
* _IOWR(): Combination of above.
63
* _IOWINT(): Return the integer argument value.
64
*/
65
expect_ioctl(ino, ReturnImmediate([](auto in, auto& out) {
66
uint8_t *in_buf = in.body.bytes + sizeof(in.body.ioctl);
67
uint8_t *out_buf = out.body.bytes + sizeof(out.body.ioctl);
68
uint32_t cmd = in.body.ioctl.cmd;
69
uint32_t arg_len = IOCPARM_LEN(cmd);
70
int result = 0;
71
72
out.header.error = 0;
73
SET_OUT_HEADER_LEN(out, ioctl);
74
if ((cmd & IOC_VOID) != 0 && arg_len > 0) {
75
memcpy(&result, in_buf, sizeof(int));
76
goto out;
77
}
78
if ((cmd & IOC_IN) != 0) {
79
if (0 != strncmp(INPUT_DATA, (char *)in_buf, arg_len)) {
80
result = -EINVAL;
81
goto out;
82
}
83
}
84
if ((cmd & IOC_OUT) != 0) {
85
memcpy(out_buf, OUTPUT_DATA, sizeof(OUTPUT_DATA));
86
out.header.len += sizeof(OUTPUT_DATA);
87
}
88
89
out:
90
out.body.ioctl.result = result;
91
}));
92
}
93
};
94
95
/**
96
* If the server does not implement FUSE_IOCTL handler (returns ENOSYS),
97
* the kernel should return ENOTTY to the user instead.
98
*/
99
TEST_F(Ioctl, enosys)
100
{
101
unsigned long req = _IO(0xff, 0);
102
int fd;
103
104
expect_opendir(FUSE_ROOT_ID);
105
expect_ioctl(FUSE_ROOT_ID, ReturnErrno(ENOSYS));
106
107
fd = open("mountpoint", O_RDONLY | O_DIRECTORY);
108
ASSERT_LE(0, fd) << strerror(errno);
109
110
EXPECT_EQ(-1, ioctl(fd, req));
111
EXPECT_EQ(ENOTTY, errno);
112
113
leak(fd);
114
}
115
116
/*
117
* For _IOR() and _IOWR(), The server is allowed to write fewer bytes
118
* than IOCPARM_LEN(req).
119
*/
120
TEST_F(Ioctl, ior)
121
{
122
char buf[sizeof(OUTPUT_DATA) + 1] = { 0 };
123
unsigned long req = _IOR(0xff, 1, buf);
124
int fd;
125
126
expect_opendir(FUSE_ROOT_ID);
127
expect_ioctl_rw(FUSE_ROOT_ID);
128
129
fd = open("mountpoint", O_RDONLY | O_DIRECTORY);
130
ASSERT_LE(0, fd) << strerror(errno);
131
132
EXPECT_EQ(0, ioctl(fd, req, buf)) << strerror(errno);
133
EXPECT_EQ(0, memcmp(buf, OUTPUT_DATA, sizeof(OUTPUT_DATA)));
134
135
leak(fd);
136
}
137
138
/*
139
* For _IOR() and _IOWR(), if the server attempts to write more bytes
140
* than IOCPARM_LEN(req), the kernel should fail the syscall with EIO.
141
*/
142
TEST_F(Ioctl, ior_overflow)
143
{
144
char buf[sizeof(OUTPUT_DATA) - 1] = { 0 };
145
unsigned long req = _IOR(0xff, 2, buf);
146
int fd;
147
148
expect_opendir(FUSE_ROOT_ID);
149
expect_ioctl_rw(FUSE_ROOT_ID);
150
151
fd = open("mountpoint", O_RDONLY | O_DIRECTORY);
152
ASSERT_LE(0, fd) << strerror(errno);
153
154
EXPECT_EQ(-1, ioctl(fd, req, buf));
155
EXPECT_EQ(EIO, errno);
156
157
leak(fd);
158
}
159
160
TEST_F(Ioctl, iow)
161
{
162
unsigned long req = _IOW(0xff, 3, INPUT_DATA);
163
int fd;
164
165
expect_opendir(FUSE_ROOT_ID);
166
expect_ioctl_rw(FUSE_ROOT_ID);
167
168
fd = open("mountpoint", O_RDONLY | O_DIRECTORY);
169
ASSERT_LE(0, fd) << strerror(errno);
170
171
EXPECT_EQ(0, ioctl(fd, req, INPUT_DATA)) << strerror(errno);
172
173
leak(fd);
174
}
175
176
TEST_F(Ioctl, iowr)
177
{
178
char buf[std::max(sizeof(INPUT_DATA), sizeof(OUTPUT_DATA))] = { 0 };
179
unsigned long req = _IOWR(0xff, 4, buf);
180
int fd;
181
182
expect_opendir(FUSE_ROOT_ID);
183
expect_ioctl_rw(FUSE_ROOT_ID);
184
185
fd = open("mountpoint", O_RDONLY | O_DIRECTORY);
186
ASSERT_LE(0, fd) << strerror(errno);
187
188
memcpy(buf, INPUT_DATA, sizeof(INPUT_DATA));
189
EXPECT_EQ(0, ioctl(fd, req, buf)) << strerror(errno);
190
EXPECT_EQ(0, memcmp(buf, OUTPUT_DATA, sizeof(OUTPUT_DATA)));
191
192
leak(fd);
193
}
194
195
TEST_F(Ioctl, iowint)
196
{
197
unsigned long req = _IOWINT(0xff, 5);
198
int arg = 1337;
199
int fd, r;
200
201
expect_opendir(FUSE_ROOT_ID);
202
expect_ioctl_rw(FUSE_ROOT_ID);
203
204
fd = open("mountpoint", O_RDONLY | O_DIRECTORY);
205
ASSERT_LE(0, fd) << strerror(errno);
206
207
/* The server is allowed to return a positive value on success */
208
r = ioctl(fd, req, arg);
209
EXPECT_LE(0, r) << strerror(errno);
210
EXPECT_EQ(arg, r);
211
212
leak(fd);
213
}
214
215