1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 * $FreeBSD$
31 */
32
33extern "C" {
34#include <sys/types.h>
35
36#include <pthread.h>
37
38#include "fuse_kernel.h"
39}
40
41#include <gmock/gmock.h>
42
43#define TIME_T_MAX (std::numeric_limits<time_t>::max())
44
45/*
46 * A pseudo-fuse errno used indicate that a fuse operation should have no
47 * response, at least not immediately
48 */
49#define FUSE_NORESPONSE 9999
50
51#define SET_OUT_HEADER_LEN(out, variant) { \
52	(out).header.len = (sizeof((out).header) + \
53			    sizeof((out).body.variant)); \
54}
55
56/*
57 * Create an expectation on FUSE_LOOKUP and return it so the caller can set
58 * actions.
59 *
60 * This must be a macro instead of a method because EXPECT_CALL returns a type
61 * with a deleted constructor.
62 */
63#define EXPECT_LOOKUP(parent, path)					\
64	EXPECT_CALL(*m_mock, process(					\
65		ResultOf([=](auto in) {					\
66			return (in.header.opcode == FUSE_LOOKUP &&	\
67				in.header.nodeid == (parent) &&	\
68				strcmp(in.body.lookup, (path)) == 0);	\
69		}, Eq(true)),						\
70		_)							\
71	)
72
73extern int verbosity;
74
75/* This struct isn't defined by fuse_kernel.h or libfuse, but it should be */
76struct fuse_create_out {
77	struct fuse_entry_out	entry;
78	struct fuse_open_out	open;
79};
80
81/* Protocol 7.8 version of struct fuse_attr */
82struct fuse_attr_7_8
83{
84	uint64_t	ino;
85	uint64_t	size;
86	uint64_t	blocks;
87	uint64_t	atime;
88	uint64_t	mtime;
89	uint64_t	ctime;
90	uint32_t	atimensec;
91	uint32_t	mtimensec;
92	uint32_t	ctimensec;
93	uint32_t	mode;
94	uint32_t	nlink;
95	uint32_t	uid;
96	uint32_t	gid;
97	uint32_t	rdev;
98};
99
100/* Protocol 7.8 version of struct fuse_attr_out */
101struct fuse_attr_out_7_8
102{
103	uint64_t	attr_valid;
104	uint32_t	attr_valid_nsec;
105	uint32_t	dummy;
106	struct fuse_attr_7_8 attr;
107};
108
109/* Protocol 7.8 version of struct fuse_entry_out */
110struct fuse_entry_out_7_8 {
111	uint64_t	nodeid;		/* Inode ID */
112	uint64_t	generation;	/* Inode generation: nodeid:gen must
113				   be unique for the fs's lifetime */
114	uint64_t	entry_valid;	/* Cache timeout for the name */
115	uint64_t	attr_valid;	/* Cache timeout for the attributes */
116	uint32_t	entry_valid_nsec;
117	uint32_t	attr_valid_nsec;
118	struct fuse_attr_7_8 attr;
119};
120
121/* Output struct for FUSE_CREATE for protocol 7.8 servers */
122struct fuse_create_out_7_8 {
123	struct fuse_entry_out_7_8	entry;
124	struct fuse_open_out	open;
125};
126
127/* Output struct for FUSE_INIT for protocol 7.22 and earlier servers */
128struct fuse_init_out_7_22 {
129	uint32_t	major;
130	uint32_t	minor;
131	uint32_t	max_readahead;
132	uint32_t	flags;
133	uint16_t	max_background;
134	uint16_t	congestion_threshold;
135	uint32_t	max_write;
136};
137
138union fuse_payloads_in {
139	fuse_access_in	access;
140	fuse_bmap_in	bmap;
141	/* value is from fuse_kern_chan.c in fusefs-libs */
142	uint8_t		bytes[0x21000 - sizeof(struct fuse_in_header)];
143	fuse_create_in	create;
144	fuse_flush_in	flush;
145	fuse_fsync_in	fsync;
146	fuse_fsync_in	fsyncdir;
147	fuse_forget_in	forget;
148	fuse_getattr_in	getattr;
149	fuse_interrupt_in interrupt;
150	fuse_lk_in	getlk;
151	fuse_getxattr_in getxattr;
152	fuse_init_in	init;
153	fuse_link_in	link;
154	fuse_listxattr_in listxattr;
155	char		lookup[0];
156	fuse_mkdir_in	mkdir;
157	fuse_mknod_in	mknod;
158	fuse_open_in	open;
159	fuse_open_in	opendir;
160	fuse_read_in	read;
161	fuse_read_in	readdir;
162	fuse_release_in	release;
163	fuse_release_in	releasedir;
164	fuse_rename_in	rename;
165	char		rmdir[0];
166	fuse_setattr_in	setattr;
167	fuse_setxattr_in setxattr;
168	fuse_lk_in	setlk;
169	fuse_lk_in	setlkw;
170	char		unlink[0];
171	fuse_write_in	write;
172};
173
174struct mockfs_buf_in {
175	fuse_in_header		header;
176	union fuse_payloads_in	body;
177};
178
179union fuse_payloads_out {
180	fuse_attr_out		attr;
181	fuse_attr_out_7_8	attr_7_8;
182	fuse_bmap_out		bmap;
183	fuse_create_out		create;
184	fuse_create_out_7_8	create_7_8;
185	/*
186	 * The protocol places no limits on the size of bytes.  Choose
187	 * a size big enough for anything we'll test.
188	 */
189	uint8_t			bytes[0x20000];
190	fuse_entry_out		entry;
191	fuse_entry_out_7_8	entry_7_8;
192	fuse_lk_out		getlk;
193	fuse_getxattr_out	getxattr;
194	fuse_init_out		init;
195	fuse_init_out_7_22	init_7_22;
196	/* The inval_entry structure should be followed by the entry's name */
197	fuse_notify_inval_entry_out	inval_entry;
198	fuse_notify_inval_inode_out	inval_inode;
199	/* The store structure should be followed by the data to store */
200	fuse_notify_store_out		store;
201	fuse_listxattr_out	listxattr;
202	fuse_open_out		open;
203	fuse_statfs_out		statfs;
204	/*
205	 * The protocol places no limits on the length of the string.  This is
206	 * merely convenient for testing.
207	 */
208	char			str[80];
209	fuse_write_out		write;
210};
211
212struct mockfs_buf_out {
213	fuse_out_header		header;
214	union fuse_payloads_out	body;
215
216	/* Default constructor: zero everything */
217	mockfs_buf_out() {
218		memset(this, 0, sizeof(*this));
219	}
220};
221
222/* A function that can be invoked in place of MockFS::process */
223typedef std::function<void (const mockfs_buf_in& in,
224			    std::vector<std::unique_ptr<mockfs_buf_out>> &out)>
225ProcessMockerT;
226
227/*
228 * Helper function used for setting an error expectation for any fuse operation.
229 * The operation will return the supplied error
230 */
231ProcessMockerT ReturnErrno(int error);
232
233/* Helper function used for returning negative cache entries for LOOKUP */
234ProcessMockerT ReturnNegativeCache(const struct timespec *entry_valid);
235
236/* Helper function used for returning a single immediate response */
237ProcessMockerT ReturnImmediate(
238	std::function<void(const mockfs_buf_in& in,
239			   struct mockfs_buf_out &out)> f);
240
241/* How the daemon should check /dev/fuse for readiness */
242enum poll_method {
243	BLOCKING,
244	SELECT,
245	POLL,
246	KQ
247};
248
249/*
250 * Fake FUSE filesystem
251 *
252 * "Mounts" a filesystem to a temporary directory and services requests
253 * according to the programmed expectations.
254 *
255 * Operates directly on the fusefs(4) kernel API, not the libfuse(3) user api.
256 */
257class MockFS {
258	/*
259	 * thread id of the fuse daemon thread
260	 *
261	 * It must run in a separate thread so it doesn't deadlock with the
262	 * client test code.
263	 */
264	pthread_t m_daemon_id;
265
266	/* file descriptor of /dev/fuse control device */
267	int m_fuse_fd;
268
269	/* The minor version of the kernel API that this mock daemon targets */
270	uint32_t m_kernel_minor_version;
271
272	int m_kq;
273
274	/* The max_readahead file system option */
275	uint32_t m_maxreadahead;
276
277	/* pid of the test process */
278	pid_t m_pid;
279
280	/* Method the daemon should use for I/O to and from /dev/fuse */
281	enum poll_method m_pm;
282
283	/* Timestamp granularity in nanoseconds */
284	unsigned m_time_gran;
285
286	void audit_request(const mockfs_buf_in &in, ssize_t buflen);
287	void debug_request(const mockfs_buf_in&, ssize_t buflen);
288	void debug_response(const mockfs_buf_out&);
289
290	/* Initialize a session after mounting */
291	void init(uint32_t flags);
292
293	/* Is pid from a process that might be involved in the test? */
294	bool pid_ok(pid_t pid);
295
296	/* Default request handler */
297	void process_default(const mockfs_buf_in&,
298		std::vector<std::unique_ptr<mockfs_buf_out>>&);
299
300	/* Entry point for the daemon thread */
301	static void* service(void*);
302
303	/*
304	 * Read, but do not process, a single request from the kernel
305	 *
306	 * @param in	Return storage for the FUSE request
307	 * @param res	Return value of read(2).  If positive, the amount of
308	 *		data read from the fuse device.
309	 */
310	void read_request(mockfs_buf_in& in, ssize_t& res);
311
312	/* Write a single response back to the kernel */
313	void write_response(const mockfs_buf_out &out);
314
315	public:
316	/* pid of child process, for two-process test cases */
317	pid_t m_child_pid;
318
319	/* Maximum size of a FUSE_WRITE write */
320	uint32_t m_maxwrite;
321
322	/*
323	 * Number of events that were available from /dev/fuse after the last
324	 * kevent call.  Only valid when m_pm = KQ.
325	 */
326	int m_nready;
327
328	/* Tell the daemon to shut down ASAP */
329	bool m_quit;
330
331	/* Create a new mockfs and mount it to a tempdir */
332	MockFS(int max_readahead, bool allow_other,
333		bool default_permissions, bool push_symlinks_in, bool ro,
334		enum poll_method pm, uint32_t flags,
335		uint32_t kernel_minor_version, uint32_t max_write, bool async,
336		bool no_clusterr, unsigned time_gran, bool nointr);
337
338	virtual ~MockFS();
339
340	/* Kill the filesystem daemon without unmounting the filesystem */
341	void kill_daemon();
342
343	/* Process FUSE requests endlessly */
344	void loop();
345
346	/*
347	 * Send an asynchronous notification to invalidate a directory entry.
348	 * Similar to libfuse's fuse_lowlevel_notify_inval_entry
349	 *
350	 * This method will block until the client has responded, so it should
351	 * generally be run in a separate thread from request processing.
352	 *
353	 * @param	parent	Parent directory's inode number
354	 * @param	name	name of dirent to invalidate
355	 * @param	namelen	size of name, including the NUL
356	 */
357	int notify_inval_entry(ino_t parent, const char *name, size_t namelen);
358
359	/*
360	 * Send an asynchronous notification to invalidate an inode's cached
361	 * data and/or attributes.  Similar to libfuse's
362	 * fuse_lowlevel_notify_inval_inode.
363	 *
364	 * This method will block until the client has responded, so it should
365	 * generally be run in a separate thread from request processing.
366	 *
367	 * @param	ino	File's inode number
368	 * @param	off	offset at which to begin invalidation.  A
369	 * 			negative offset means to invalidate attributes
370	 * 			only.
371	 * @param	len	Size of region of data to invalidate.  0 means
372	 * 			to invalidate all cached data.
373	 */
374	int notify_inval_inode(ino_t ino, off_t off, ssize_t len);
375
376	/*
377	 * Send an asynchronous notification to store data directly into an
378	 * inode's cache.  Similar to libfuse's fuse_lowlevel_notify_store.
379	 *
380	 * This method will block until the client has responded, so it should
381	 * generally be run in a separate thread from request processing.
382	 *
383	 * @param	ino	File's inode number
384	 * @param	off	Offset at which to store data
385	 * @param	data	Pointer to the data to cache
386	 * @param	len	Size of data
387	 */
388	int notify_store(ino_t ino, off_t off, const void* data, ssize_t size);
389
390	/*
391	 * Request handler
392	 *
393	 * This method is expected to provide the responses to each FUSE
394	 * operation.  For an immediate response, push one buffer into out.
395	 * For a delayed response, push nothing.  For an immediate response
396	 * plus a delayed response to an earlier operation, push two bufs.
397	 * Test cases must define each response using Googlemock expectations
398	 */
399	MOCK_METHOD2(process, void(const mockfs_buf_in&,
400				std::vector<std::unique_ptr<mockfs_buf_out>>&));
401
402	/* Gracefully unmount */
403	void unmount();
404};
405