xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision 9821f1d3231d4dea3e1319358f416425be2266a6)
1*9821f1d3SAlan Somers /*-
2*9821f1d3SAlan Somers  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*9821f1d3SAlan Somers  *
4*9821f1d3SAlan Somers  * Copyright (c) 2019 The FreeBSD Foundation
5*9821f1d3SAlan Somers  *
6*9821f1d3SAlan Somers  * This software was developed by BFF Storage Systems, LLC under sponsorship
7*9821f1d3SAlan Somers  * from the FreeBSD Foundation.
8*9821f1d3SAlan Somers  *
9*9821f1d3SAlan Somers  * Redistribution and use in source and binary forms, with or without
10*9821f1d3SAlan Somers  * modification, are permitted provided that the following conditions
11*9821f1d3SAlan Somers  * are met:
12*9821f1d3SAlan Somers  * 1. Redistributions of source code must retain the above copyright
13*9821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer.
14*9821f1d3SAlan Somers  * 2. Redistributions in binary form must reproduce the above copyright
15*9821f1d3SAlan Somers  *    notice, this list of conditions and the following disclaimer in the
16*9821f1d3SAlan Somers  *    documentation and/or other materials provided with the distribution.
17*9821f1d3SAlan Somers  *
18*9821f1d3SAlan Somers  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*9821f1d3SAlan Somers  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*9821f1d3SAlan Somers  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*9821f1d3SAlan Somers  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*9821f1d3SAlan Somers  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*9821f1d3SAlan Somers  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*9821f1d3SAlan Somers  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*9821f1d3SAlan Somers  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*9821f1d3SAlan Somers  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*9821f1d3SAlan Somers  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*9821f1d3SAlan Somers  * SUCH DAMAGE.
29*9821f1d3SAlan Somers  */
30*9821f1d3SAlan Somers 
31*9821f1d3SAlan Somers extern "C" {
32*9821f1d3SAlan Somers #include <sys/param.h>
33*9821f1d3SAlan Somers 
34*9821f1d3SAlan Somers #include <sys/mount.h>
35*9821f1d3SAlan Somers #include <sys/stat.h>
36*9821f1d3SAlan Somers #include <sys/uio.h>
37*9821f1d3SAlan Somers #include <sys/user.h>
38*9821f1d3SAlan Somers 
39*9821f1d3SAlan Somers #include <fcntl.h>
40*9821f1d3SAlan Somers #include <libutil.h>
41*9821f1d3SAlan Somers #include <pthread.h>
42*9821f1d3SAlan Somers #include <signal.h>
43*9821f1d3SAlan Somers #include <stdlib.h>
44*9821f1d3SAlan Somers #include <unistd.h>
45*9821f1d3SAlan Somers 
46*9821f1d3SAlan Somers #include "mntopts.h"	// for build_iovec
47*9821f1d3SAlan Somers }
48*9821f1d3SAlan Somers 
49*9821f1d3SAlan Somers #include <gtest/gtest.h>
50*9821f1d3SAlan Somers 
51*9821f1d3SAlan Somers #include "mockfs.hh"
52*9821f1d3SAlan Somers 
53*9821f1d3SAlan Somers using namespace testing;
54*9821f1d3SAlan Somers 
55*9821f1d3SAlan Somers int verbosity = 0;
56*9821f1d3SAlan Somers static sig_atomic_t quit = 0;
57*9821f1d3SAlan Somers 
58*9821f1d3SAlan Somers const char* opcode2opname(uint32_t opcode)
59*9821f1d3SAlan Somers {
60*9821f1d3SAlan Somers 	const int NUM_OPS = 39;
61*9821f1d3SAlan Somers 	const char* table[NUM_OPS] = {
62*9821f1d3SAlan Somers 		"Unknown (opcode 0)",
63*9821f1d3SAlan Somers 		"LOOKUP",
64*9821f1d3SAlan Somers 		"FORGET",
65*9821f1d3SAlan Somers 		"GETATTR",
66*9821f1d3SAlan Somers 		"SETATTR",
67*9821f1d3SAlan Somers 		"READLINK",
68*9821f1d3SAlan Somers 		"SYMLINK",
69*9821f1d3SAlan Somers 		"Unknown (opcode 7)",
70*9821f1d3SAlan Somers 		"MKNOD",
71*9821f1d3SAlan Somers 		"MKDIR",
72*9821f1d3SAlan Somers 		"UNLINK",
73*9821f1d3SAlan Somers 		"RMDIR",
74*9821f1d3SAlan Somers 		"RENAME",
75*9821f1d3SAlan Somers 		"LINK",
76*9821f1d3SAlan Somers 		"OPEN",
77*9821f1d3SAlan Somers 		"READ",
78*9821f1d3SAlan Somers 		"WRITE",
79*9821f1d3SAlan Somers 		"STATFS",
80*9821f1d3SAlan Somers 		"RELEASE",
81*9821f1d3SAlan Somers 		"Unknown (opcode 19)",
82*9821f1d3SAlan Somers 		"FSYNC",
83*9821f1d3SAlan Somers 		"SETXATTR",
84*9821f1d3SAlan Somers 		"GETXATTR",
85*9821f1d3SAlan Somers 		"LISTXATTR",
86*9821f1d3SAlan Somers 		"REMOVEXATTR",
87*9821f1d3SAlan Somers 		"FLUSH",
88*9821f1d3SAlan Somers 		"INIT",
89*9821f1d3SAlan Somers 		"OPENDIR",
90*9821f1d3SAlan Somers 		"READDIR",
91*9821f1d3SAlan Somers 		"RELEASEDIR",
92*9821f1d3SAlan Somers 		"FSYNCDIR",
93*9821f1d3SAlan Somers 		"GETLK",
94*9821f1d3SAlan Somers 		"SETLK",
95*9821f1d3SAlan Somers 		"SETLKW",
96*9821f1d3SAlan Somers 		"ACCESS",
97*9821f1d3SAlan Somers 		"CREATE",
98*9821f1d3SAlan Somers 		"INTERRUPT",
99*9821f1d3SAlan Somers 		"BMAP",
100*9821f1d3SAlan Somers 		"DESTROY"
101*9821f1d3SAlan Somers 	};
102*9821f1d3SAlan Somers 	if (opcode >= NUM_OPS)
103*9821f1d3SAlan Somers 		return ("Unknown (opcode > max)");
104*9821f1d3SAlan Somers 	else
105*9821f1d3SAlan Somers 		return (table[opcode]);
106*9821f1d3SAlan Somers }
107*9821f1d3SAlan Somers 
108*9821f1d3SAlan Somers ProcessMockerT
109*9821f1d3SAlan Somers ReturnErrno(int error)
110*9821f1d3SAlan Somers {
111*9821f1d3SAlan Somers 	return([=](auto in, auto &out) {
112*9821f1d3SAlan Somers 		auto out0 = new mockfs_buf_out;
113*9821f1d3SAlan Somers 		out0->header.unique = in->header.unique;
114*9821f1d3SAlan Somers 		out0->header.error = -error;
115*9821f1d3SAlan Somers 		out0->header.len = sizeof(out0->header);
116*9821f1d3SAlan Somers 		out.push_back(out0);
117*9821f1d3SAlan Somers 	});
118*9821f1d3SAlan Somers }
119*9821f1d3SAlan Somers 
120*9821f1d3SAlan Somers /* Helper function used for returning negative cache entries for LOOKUP */
121*9821f1d3SAlan Somers ProcessMockerT
122*9821f1d3SAlan Somers ReturnNegativeCache(const struct timespec *entry_valid)
123*9821f1d3SAlan Somers {
124*9821f1d3SAlan Somers 	return([=](auto in, auto &out) {
125*9821f1d3SAlan Somers 		/* nodeid means ENOENT and cache it */
126*9821f1d3SAlan Somers 		auto out0 = new mockfs_buf_out;
127*9821f1d3SAlan Somers 		out0->body.entry.nodeid = 0;
128*9821f1d3SAlan Somers 		out0->header.unique = in->header.unique;
129*9821f1d3SAlan Somers 		out0->header.error = 0;
130*9821f1d3SAlan Somers 		out0->body.entry.entry_valid = entry_valid->tv_sec;
131*9821f1d3SAlan Somers 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
132*9821f1d3SAlan Somers 		SET_OUT_HEADER_LEN(out0, entry);
133*9821f1d3SAlan Somers 		out.push_back(out0);
134*9821f1d3SAlan Somers 	});
135*9821f1d3SAlan Somers }
136*9821f1d3SAlan Somers 
137*9821f1d3SAlan Somers ProcessMockerT
138*9821f1d3SAlan Somers ReturnImmediate(std::function<void(const struct mockfs_buf_in *in,
139*9821f1d3SAlan Somers 				   struct mockfs_buf_out *out)> f)
140*9821f1d3SAlan Somers {
141*9821f1d3SAlan Somers 	return([=](auto in, auto &out) {
142*9821f1d3SAlan Somers 		auto out0 = new mockfs_buf_out;
143*9821f1d3SAlan Somers 		out0->header.unique = in->header.unique;
144*9821f1d3SAlan Somers 		f(in, out0);
145*9821f1d3SAlan Somers 		out.push_back(out0);
146*9821f1d3SAlan Somers 	});
147*9821f1d3SAlan Somers }
148*9821f1d3SAlan Somers 
149*9821f1d3SAlan Somers void sigint_handler(int __unused sig) {
150*9821f1d3SAlan Somers 	quit = 1;
151*9821f1d3SAlan Somers }
152*9821f1d3SAlan Somers 
153*9821f1d3SAlan Somers void debug_fuseop(const mockfs_buf_in *in)
154*9821f1d3SAlan Somers {
155*9821f1d3SAlan Somers 	printf("%-11s ino=%2lu", opcode2opname(in->header.opcode),
156*9821f1d3SAlan Somers 		in->header.nodeid);
157*9821f1d3SAlan Somers 	if (verbosity > 1) {
158*9821f1d3SAlan Somers 		printf(" uid=%5u gid=%5u pid=%5u unique=%lu len=%u",
159*9821f1d3SAlan Somers 			in->header.uid, in->header.gid, in->header.pid,
160*9821f1d3SAlan Somers 			in->header.unique, in->header.len);
161*9821f1d3SAlan Somers 	}
162*9821f1d3SAlan Somers 	switch (in->header.opcode) {
163*9821f1d3SAlan Somers 		case FUSE_FLUSH:
164*9821f1d3SAlan Somers 			printf(" lock_owner=%lu", in->body.flush.lock_owner);
165*9821f1d3SAlan Somers 			break;
166*9821f1d3SAlan Somers 		case FUSE_FORGET:
167*9821f1d3SAlan Somers 			printf(" nlookup=%lu", in->body.forget.nlookup);
168*9821f1d3SAlan Somers 			break;
169*9821f1d3SAlan Somers 		case FUSE_FSYNC:
170*9821f1d3SAlan Somers 			printf(" flags=%#x", in->body.fsync.fsync_flags);
171*9821f1d3SAlan Somers 			break;
172*9821f1d3SAlan Somers 		case FUSE_FSYNCDIR:
173*9821f1d3SAlan Somers 			printf(" flags=%#x", in->body.fsyncdir.fsync_flags);
174*9821f1d3SAlan Somers 			break;
175*9821f1d3SAlan Somers 		case FUSE_LOOKUP:
176*9821f1d3SAlan Somers 			printf(" %s", in->body.lookup);
177*9821f1d3SAlan Somers 			break;
178*9821f1d3SAlan Somers 		case FUSE_OPEN:
179*9821f1d3SAlan Somers 			printf(" flags=%#x mode=%#o",
180*9821f1d3SAlan Somers 				in->body.open.flags, in->body.open.mode);
181*9821f1d3SAlan Somers 			break;
182*9821f1d3SAlan Somers 		case FUSE_OPENDIR:
183*9821f1d3SAlan Somers 			printf(" flags=%#x mode=%#o",
184*9821f1d3SAlan Somers 				in->body.opendir.flags, in->body.opendir.mode);
185*9821f1d3SAlan Somers 			break;
186*9821f1d3SAlan Somers 		case FUSE_READ:
187*9821f1d3SAlan Somers 			printf(" offset=%lu size=%u", in->body.read.offset,
188*9821f1d3SAlan Somers 				in->body.read.size);
189*9821f1d3SAlan Somers 			break;
190*9821f1d3SAlan Somers 		case FUSE_READDIR:
191*9821f1d3SAlan Somers 			printf(" offset=%lu size=%u", in->body.readdir.offset,
192*9821f1d3SAlan Somers 				in->body.readdir.size);
193*9821f1d3SAlan Somers 			break;
194*9821f1d3SAlan Somers 		case FUSE_RELEASE:
195*9821f1d3SAlan Somers 			printf(" flags=%#x lock_owner=%lu",
196*9821f1d3SAlan Somers 				in->body.release.flags,
197*9821f1d3SAlan Somers 				in->body.release.lock_owner);
198*9821f1d3SAlan Somers 			break;
199*9821f1d3SAlan Somers 		case FUSE_SETATTR:
200*9821f1d3SAlan Somers 			if (verbosity <= 1) {
201*9821f1d3SAlan Somers 				printf(" valid=%#x", in->body.setattr.valid);
202*9821f1d3SAlan Somers 				break;
203*9821f1d3SAlan Somers 			}
204*9821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_MODE)
205*9821f1d3SAlan Somers 				printf(" mode=%#o", in->body.setattr.mode);
206*9821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_UID)
207*9821f1d3SAlan Somers 				printf(" uid=%u", in->body.setattr.uid);
208*9821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_GID)
209*9821f1d3SAlan Somers 				printf(" gid=%u", in->body.setattr.gid);
210*9821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_SIZE)
211*9821f1d3SAlan Somers 				printf(" size=%zu", in->body.setattr.size);
212*9821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_ATIME)
213*9821f1d3SAlan Somers 				printf(" atime=%zu.%u",
214*9821f1d3SAlan Somers 					in->body.setattr.atime,
215*9821f1d3SAlan Somers 					in->body.setattr.atimensec);
216*9821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_MTIME)
217*9821f1d3SAlan Somers 				printf(" mtime=%zu.%u",
218*9821f1d3SAlan Somers 					in->body.setattr.mtime,
219*9821f1d3SAlan Somers 					in->body.setattr.mtimensec);
220*9821f1d3SAlan Somers 			if (in->body.setattr.valid & FATTR_FH)
221*9821f1d3SAlan Somers 				printf(" fh=%zu", in->body.setattr.fh);
222*9821f1d3SAlan Somers 			break;
223*9821f1d3SAlan Somers 		case FUSE_SETXATTR:
224*9821f1d3SAlan Somers 			/*
225*9821f1d3SAlan Somers 			 * In theory neither the xattr name and value need be
226*9821f1d3SAlan Somers 			 * ASCII, but in this test suite they always are.
227*9821f1d3SAlan Somers 			 */
228*9821f1d3SAlan Somers 			{
229*9821f1d3SAlan Somers 				const char *attr = (const char*)in->body.bytes +
230*9821f1d3SAlan Somers 					sizeof(fuse_setxattr_in);
231*9821f1d3SAlan Somers 				const char *v = attr + strlen(attr) + 1;
232*9821f1d3SAlan Somers 				printf(" %s=%s", attr, v);
233*9821f1d3SAlan Somers 			}
234*9821f1d3SAlan Somers 			break;
235*9821f1d3SAlan Somers 		case FUSE_WRITE:
236*9821f1d3SAlan Somers 			printf(" offset=%lu size=%u flags=%u",
237*9821f1d3SAlan Somers 				in->body.write.offset, in->body.write.size,
238*9821f1d3SAlan Somers 				in->body.write.write_flags);
239*9821f1d3SAlan Somers 			break;
240*9821f1d3SAlan Somers 		default:
241*9821f1d3SAlan Somers 			break;
242*9821f1d3SAlan Somers 	}
243*9821f1d3SAlan Somers 	printf("\n");
244*9821f1d3SAlan Somers }
245*9821f1d3SAlan Somers 
246*9821f1d3SAlan Somers MockFS::MockFS(int max_readahead, bool push_symlinks_in,
247*9821f1d3SAlan Somers 	bool default_permissions, uint32_t flags)
248*9821f1d3SAlan Somers {
249*9821f1d3SAlan Somers 	struct iovec *iov = NULL;
250*9821f1d3SAlan Somers 	int iovlen = 0;
251*9821f1d3SAlan Somers 	char fdstr[15];
252*9821f1d3SAlan Somers 
253*9821f1d3SAlan Somers 	m_daemon_id = NULL;
254*9821f1d3SAlan Somers 	m_maxreadahead = max_readahead;
255*9821f1d3SAlan Somers 	quit = 0;
256*9821f1d3SAlan Somers 
257*9821f1d3SAlan Somers 	/*
258*9821f1d3SAlan Somers 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
259*9821f1d3SAlan Somers 	 * mkdtemp
260*9821f1d3SAlan Somers 	 */
261*9821f1d3SAlan Somers 	/*
262*9821f1d3SAlan Somers 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
263*9821f1d3SAlan Somers 	 * instead.
264*9821f1d3SAlan Somers 	 */
265*9821f1d3SAlan Somers 	if (mkdir("mountpoint" , 0644) && errno != EEXIST)
266*9821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
267*9821f1d3SAlan Somers 			"Couldn't make mountpoint directory"));
268*9821f1d3SAlan Somers 
269*9821f1d3SAlan Somers 	m_fuse_fd = open("/dev/fuse", O_RDWR);
270*9821f1d3SAlan Somers 	if (m_fuse_fd < 0)
271*9821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
272*9821f1d3SAlan Somers 			"Couldn't open /dev/fuse"));
273*9821f1d3SAlan Somers 	sprintf(fdstr, "%d", m_fuse_fd);
274*9821f1d3SAlan Somers 
275*9821f1d3SAlan Somers 	m_pid = getpid();
276*9821f1d3SAlan Somers 
277*9821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
278*9821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fspath",
279*9821f1d3SAlan Somers 		    __DECONST(void *, "mountpoint"), -1);
280*9821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
281*9821f1d3SAlan Somers 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
282*9821f1d3SAlan Somers 	if (push_symlinks_in) {
283*9821f1d3SAlan Somers 		const bool trueval = true;
284*9821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "push_symlinks_in",
285*9821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
286*9821f1d3SAlan Somers 	}
287*9821f1d3SAlan Somers 	if (default_permissions) {
288*9821f1d3SAlan Somers 		const bool trueval = true;
289*9821f1d3SAlan Somers 		build_iovec(&iov, &iovlen, "default_permissions",
290*9821f1d3SAlan Somers 			__DECONST(void*, &trueval), sizeof(bool));
291*9821f1d3SAlan Somers 	}
292*9821f1d3SAlan Somers 	if (nmount(iov, iovlen, 0))
293*9821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
294*9821f1d3SAlan Somers 			"Couldn't mount filesystem"));
295*9821f1d3SAlan Somers 
296*9821f1d3SAlan Somers 	// Setup default handler
297*9821f1d3SAlan Somers 	ON_CALL(*this, process(_, _))
298*9821f1d3SAlan Somers 		.WillByDefault(Invoke(this, &MockFS::process_default));
299*9821f1d3SAlan Somers 
300*9821f1d3SAlan Somers 	init(flags);
301*9821f1d3SAlan Somers 	signal(SIGUSR1, sigint_handler);
302*9821f1d3SAlan Somers 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
303*9821f1d3SAlan Somers 		throw(std::system_error(errno, std::system_category(),
304*9821f1d3SAlan Somers 			"Couldn't Couldn't start fuse thread"));
305*9821f1d3SAlan Somers }
306*9821f1d3SAlan Somers 
307*9821f1d3SAlan Somers MockFS::~MockFS() {
308*9821f1d3SAlan Somers 	kill_daemon();
309*9821f1d3SAlan Somers 	::unmount("mountpoint", MNT_FORCE);
310*9821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
311*9821f1d3SAlan Somers 		pthread_join(m_daemon_id, NULL);
312*9821f1d3SAlan Somers 		m_daemon_id = NULL;
313*9821f1d3SAlan Somers 	}
314*9821f1d3SAlan Somers 	rmdir("mountpoint");
315*9821f1d3SAlan Somers }
316*9821f1d3SAlan Somers 
317*9821f1d3SAlan Somers void MockFS::init(uint32_t flags) {
318*9821f1d3SAlan Somers 	mockfs_buf_in *in;
319*9821f1d3SAlan Somers 	mockfs_buf_out *out;
320*9821f1d3SAlan Somers 
321*9821f1d3SAlan Somers 	in = (mockfs_buf_in*) malloc(sizeof(*in));
322*9821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
323*9821f1d3SAlan Somers 	out = (mockfs_buf_out*) malloc(sizeof(*out));
324*9821f1d3SAlan Somers 	ASSERT_TRUE(out != NULL);
325*9821f1d3SAlan Somers 
326*9821f1d3SAlan Somers 	read_request(in);
327*9821f1d3SAlan Somers 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
328*9821f1d3SAlan Somers 
329*9821f1d3SAlan Somers 	memset(out, 0, sizeof(*out));
330*9821f1d3SAlan Somers 	out->header.unique = in->header.unique;
331*9821f1d3SAlan Somers 	out->header.error = 0;
332*9821f1d3SAlan Somers 	out->body.init.major = FUSE_KERNEL_VERSION;
333*9821f1d3SAlan Somers 	out->body.init.minor = FUSE_KERNEL_MINOR_VERSION;
334*9821f1d3SAlan Somers 	out->body.init.flags = in->body.init.flags & flags;
335*9821f1d3SAlan Somers 
336*9821f1d3SAlan Somers 	/*
337*9821f1d3SAlan Somers 	 * The default max_write is set to this formula in libfuse, though
338*9821f1d3SAlan Somers 	 * individual filesystems can lower it.  The "- 4096" was added in
339*9821f1d3SAlan Somers 	 * commit 154ffe2, with the commit message "fix".
340*9821f1d3SAlan Somers 	 */
341*9821f1d3SAlan Somers 	uint32_t default_max_write = 32 * getpagesize() + 0x1000 - 4096;
342*9821f1d3SAlan Somers 	/* For testing purposes, it should be distinct from MAXPHYS */
343*9821f1d3SAlan Somers 	m_max_write = MIN(default_max_write, MAXPHYS / 2);
344*9821f1d3SAlan Somers 	out->body.init.max_write = m_max_write;
345*9821f1d3SAlan Somers 
346*9821f1d3SAlan Somers 	out->body.init.max_readahead = m_maxreadahead;
347*9821f1d3SAlan Somers 	SET_OUT_HEADER_LEN(out, init);
348*9821f1d3SAlan Somers 	write(m_fuse_fd, out, out->header.len);
349*9821f1d3SAlan Somers 
350*9821f1d3SAlan Somers 	free(in);
351*9821f1d3SAlan Somers }
352*9821f1d3SAlan Somers 
353*9821f1d3SAlan Somers void MockFS::kill_daemon() {
354*9821f1d3SAlan Somers 	if (m_daemon_id != NULL) {
355*9821f1d3SAlan Somers 		pthread_kill(m_daemon_id, SIGUSR1);
356*9821f1d3SAlan Somers 		// Closing the /dev/fuse file descriptor first allows unmount
357*9821f1d3SAlan Somers 		// to succeed even if the daemon doesn't correctly respond to
358*9821f1d3SAlan Somers 		// commands during the unmount sequence.
359*9821f1d3SAlan Somers 		close(m_fuse_fd);
360*9821f1d3SAlan Somers 	}
361*9821f1d3SAlan Somers }
362*9821f1d3SAlan Somers 
363*9821f1d3SAlan Somers void MockFS::loop() {
364*9821f1d3SAlan Somers 	mockfs_buf_in *in;
365*9821f1d3SAlan Somers 	std::vector<mockfs_buf_out*> out;
366*9821f1d3SAlan Somers 
367*9821f1d3SAlan Somers 	in = (mockfs_buf_in*) malloc(sizeof(*in));
368*9821f1d3SAlan Somers 	ASSERT_TRUE(in != NULL);
369*9821f1d3SAlan Somers 	while (!quit) {
370*9821f1d3SAlan Somers 		bzero(in, sizeof(*in));
371*9821f1d3SAlan Somers 		read_request(in);
372*9821f1d3SAlan Somers 		if (quit)
373*9821f1d3SAlan Somers 			break;
374*9821f1d3SAlan Somers 		if (verbosity > 0)
375*9821f1d3SAlan Somers 			debug_fuseop(in);
376*9821f1d3SAlan Somers 		if (pid_ok((pid_t)in->header.pid)) {
377*9821f1d3SAlan Somers 			process(in, out);
378*9821f1d3SAlan Somers 		} else {
379*9821f1d3SAlan Somers 			/*
380*9821f1d3SAlan Somers 			 * Reject any requests from unknown processes.  Because
381*9821f1d3SAlan Somers 			 * we actually do mount a filesystem, plenty of
382*9821f1d3SAlan Somers 			 * unrelated system daemons may try to access it.
383*9821f1d3SAlan Somers 			 */
384*9821f1d3SAlan Somers 			process_default(in, out);
385*9821f1d3SAlan Somers 		}
386*9821f1d3SAlan Somers 		for (auto &it: out) {
387*9821f1d3SAlan Somers 			ASSERT_TRUE(write(m_fuse_fd, it, it->header.len) > 0 ||
388*9821f1d3SAlan Somers 				    errno == EAGAIN)
389*9821f1d3SAlan Somers 				<< strerror(errno);
390*9821f1d3SAlan Somers 			delete it;
391*9821f1d3SAlan Somers 		}
392*9821f1d3SAlan Somers 		out.clear();
393*9821f1d3SAlan Somers 	}
394*9821f1d3SAlan Somers 	free(in);
395*9821f1d3SAlan Somers }
396*9821f1d3SAlan Somers 
397*9821f1d3SAlan Somers bool MockFS::pid_ok(pid_t pid) {
398*9821f1d3SAlan Somers 	if (pid == m_pid) {
399*9821f1d3SAlan Somers 		return (true);
400*9821f1d3SAlan Somers 	} else {
401*9821f1d3SAlan Somers 		struct kinfo_proc *ki;
402*9821f1d3SAlan Somers 		bool ok = false;
403*9821f1d3SAlan Somers 
404*9821f1d3SAlan Somers 		ki = kinfo_getproc(pid);
405*9821f1d3SAlan Somers 		if (ki == NULL)
406*9821f1d3SAlan Somers 			return (false);
407*9821f1d3SAlan Somers 		/*
408*9821f1d3SAlan Somers 		 * Allow access by the aio daemon processes so that our tests
409*9821f1d3SAlan Somers 		 * can use aio functions
410*9821f1d3SAlan Somers 		 */
411*9821f1d3SAlan Somers 		if (0 == strncmp("aiod", ki->ki_comm, 4))
412*9821f1d3SAlan Somers 			ok = true;
413*9821f1d3SAlan Somers 		free(ki);
414*9821f1d3SAlan Somers 		return (ok);
415*9821f1d3SAlan Somers 	}
416*9821f1d3SAlan Somers }
417*9821f1d3SAlan Somers 
418*9821f1d3SAlan Somers void MockFS::process_default(const mockfs_buf_in *in,
419*9821f1d3SAlan Somers 		std::vector<mockfs_buf_out*> &out)
420*9821f1d3SAlan Somers {
421*9821f1d3SAlan Somers 	auto out0 = new mockfs_buf_out;
422*9821f1d3SAlan Somers 	out0->header.unique = in->header.unique;
423*9821f1d3SAlan Somers 	out0->header.error = -EOPNOTSUPP;
424*9821f1d3SAlan Somers 	out0->header.len = sizeof(out0->header);
425*9821f1d3SAlan Somers 	out.push_back(out0);
426*9821f1d3SAlan Somers }
427*9821f1d3SAlan Somers 
428*9821f1d3SAlan Somers void MockFS::read_request(mockfs_buf_in *in) {
429*9821f1d3SAlan Somers 	ssize_t res;
430*9821f1d3SAlan Somers 
431*9821f1d3SAlan Somers 	res = read(m_fuse_fd, in, sizeof(*in));
432*9821f1d3SAlan Somers 	if (res < 0 && !quit)
433*9821f1d3SAlan Somers 		perror("read");
434*9821f1d3SAlan Somers 	ASSERT_TRUE(res >= (ssize_t)sizeof(in->header) || quit);
435*9821f1d3SAlan Somers }
436*9821f1d3SAlan Somers 
437*9821f1d3SAlan Somers void* MockFS::service(void *pthr_data) {
438*9821f1d3SAlan Somers 	MockFS *mock_fs = (MockFS*)pthr_data;
439*9821f1d3SAlan Somers 
440*9821f1d3SAlan Somers 	mock_fs->loop();
441*9821f1d3SAlan Somers 
442*9821f1d3SAlan Somers 	return (NULL);
443*9821f1d3SAlan Somers }
444*9821f1d3SAlan Somers 
445*9821f1d3SAlan Somers void MockFS::unmount() {
446*9821f1d3SAlan Somers 	::unmount("mountpoint", 0);
447*9821f1d3SAlan Somers }
448