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