xref: /freebsd/tests/sys/fs/fusefs/mockfs.cc (revision e17f5b1d)
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 
33 extern "C" {
34 #include <sys/param.h>
35 
36 #include <sys/mount.h>
37 #include <sys/select.h>
38 #include <sys/stat.h>
39 #include <sys/uio.h>
40 #include <sys/user.h>
41 
42 #include <fcntl.h>
43 #include <libutil.h>
44 #include <poll.h>
45 #include <pthread.h>
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 
50 #include "mntopts.h"	// for build_iovec
51 }
52 
53 #include <cinttypes>
54 
55 #include <gtest/gtest.h>
56 
57 #include "mockfs.hh"
58 
59 using namespace testing;
60 
61 int verbosity = 0;
62 
63 const char* opcode2opname(uint32_t opcode)
64 {
65 	const int NUM_OPS = 39;
66 	const char* table[NUM_OPS] = {
67 		"Unknown (opcode 0)",
68 		"LOOKUP",
69 		"FORGET",
70 		"GETATTR",
71 		"SETATTR",
72 		"READLINK",
73 		"SYMLINK",
74 		"Unknown (opcode 7)",
75 		"MKNOD",
76 		"MKDIR",
77 		"UNLINK",
78 		"RMDIR",
79 		"RENAME",
80 		"LINK",
81 		"OPEN",
82 		"READ",
83 		"WRITE",
84 		"STATFS",
85 		"RELEASE",
86 		"Unknown (opcode 19)",
87 		"FSYNC",
88 		"SETXATTR",
89 		"GETXATTR",
90 		"LISTXATTR",
91 		"REMOVEXATTR",
92 		"FLUSH",
93 		"INIT",
94 		"OPENDIR",
95 		"READDIR",
96 		"RELEASEDIR",
97 		"FSYNCDIR",
98 		"GETLK",
99 		"SETLK",
100 		"SETLKW",
101 		"ACCESS",
102 		"CREATE",
103 		"INTERRUPT",
104 		"BMAP",
105 		"DESTROY"
106 	};
107 	if (opcode >= NUM_OPS)
108 		return ("Unknown (opcode > max)");
109 	else
110 		return (table[opcode]);
111 }
112 
113 ProcessMockerT
114 ReturnErrno(int error)
115 {
116 	return([=](auto in, auto &out) {
117 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
118 		out0->header.unique = in.header.unique;
119 		out0->header.error = -error;
120 		out0->header.len = sizeof(out0->header);
121 		out.push_back(std::move(out0));
122 	});
123 }
124 
125 /* Helper function used for returning negative cache entries for LOOKUP */
126 ProcessMockerT
127 ReturnNegativeCache(const struct timespec *entry_valid)
128 {
129 	return([=](auto in, auto &out) {
130 		/* nodeid means ENOENT and cache it */
131 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
132 		out0->body.entry.nodeid = 0;
133 		out0->header.unique = in.header.unique;
134 		out0->header.error = 0;
135 		out0->body.entry.entry_valid = entry_valid->tv_sec;
136 		out0->body.entry.entry_valid_nsec = entry_valid->tv_nsec;
137 		SET_OUT_HEADER_LEN(*out0, entry);
138 		out.push_back(std::move(out0));
139 	});
140 }
141 
142 ProcessMockerT
143 ReturnImmediate(std::function<void(const mockfs_buf_in& in,
144 				   struct mockfs_buf_out &out)> f)
145 {
146 	return([=](auto& in, auto &out) {
147 		std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
148 		out0->header.unique = in.header.unique;
149 		f(in, *out0);
150 		out.push_back(std::move(out0));
151 	});
152 }
153 
154 void sigint_handler(int __unused sig) {
155 	// Don't do anything except interrupt the daemon's read(2) call
156 }
157 
158 void MockFS::debug_request(const mockfs_buf_in &in, ssize_t buflen)
159 {
160 	printf("%-11s ino=%2" PRIu64, opcode2opname(in.header.opcode),
161 		in.header.nodeid);
162 	if (verbosity > 1) {
163 		printf(" uid=%5u gid=%5u pid=%5u unique=%" PRIu64 " len=%u"
164 			" buflen=%zd",
165 			in.header.uid, in.header.gid, in.header.pid,
166 			in.header.unique, in.header.len, buflen);
167 	}
168 	switch (in.header.opcode) {
169 		const char *name, *value;
170 
171 		case FUSE_ACCESS:
172 			printf(" mask=%#x", in.body.access.mask);
173 			break;
174 		case FUSE_BMAP:
175 			printf(" block=%" PRIx64 " blocksize=%#x",
176 				in.body.bmap.block, in.body.bmap.blocksize);
177 			break;
178 		case FUSE_CREATE:
179 			if (m_kernel_minor_version >= 12)
180 				name = (const char*)in.body.bytes +
181 					sizeof(fuse_create_in);
182 			else
183 				name = (const char*)in.body.bytes +
184 					sizeof(fuse_open_in);
185 			printf(" flags=%#x name=%s",
186 				in.body.open.flags, name);
187 			break;
188 		case FUSE_FLUSH:
189 			printf(" fh=%#" PRIx64 " lock_owner=%" PRIu64,
190 				in.body.flush.fh,
191 				in.body.flush.lock_owner);
192 			break;
193 		case FUSE_FORGET:
194 			printf(" nlookup=%" PRIu64, in.body.forget.nlookup);
195 			break;
196 		case FUSE_FSYNC:
197 			printf(" flags=%#x", in.body.fsync.fsync_flags);
198 			break;
199 		case FUSE_FSYNCDIR:
200 			printf(" flags=%#x", in.body.fsyncdir.fsync_flags);
201 			break;
202 		case FUSE_INTERRUPT:
203 			printf(" unique=%" PRIu64, in.body.interrupt.unique);
204 			break;
205 		case FUSE_LINK:
206 			printf(" oldnodeid=%" PRIu64, in.body.link.oldnodeid);
207 			break;
208 		case FUSE_LISTXATTR:
209 			printf(" size=%" PRIu32, in.body.listxattr.size);
210 			break;
211 		case FUSE_LOOKUP:
212 			printf(" %s", in.body.lookup);
213 			break;
214 		case FUSE_MKDIR:
215 			name = (const char*)in.body.bytes +
216 				sizeof(fuse_mkdir_in);
217 			printf(" name=%s mode=%#o umask=%#o", name,
218 				in.body.mkdir.mode, in.body.mkdir.umask);
219 			break;
220 		case FUSE_MKNOD:
221 			if (m_kernel_minor_version >= 12)
222 				name = (const char*)in.body.bytes +
223 					sizeof(fuse_mknod_in);
224 			else
225 				name = (const char*)in.body.bytes +
226 					FUSE_COMPAT_MKNOD_IN_SIZE;
227 			printf(" mode=%#o rdev=%x umask=%#o name=%s",
228 				in.body.mknod.mode, in.body.mknod.rdev,
229 				in.body.mknod.umask, name);
230 			break;
231 		case FUSE_OPEN:
232 			printf(" flags=%#x", in.body.open.flags);
233 			break;
234 		case FUSE_OPENDIR:
235 			printf(" flags=%#x", in.body.opendir.flags);
236 			break;
237 		case FUSE_READ:
238 			printf(" offset=%" PRIu64 " size=%u",
239 				in.body.read.offset,
240 				in.body.read.size);
241 			if (verbosity > 1)
242 				printf(" flags=%#x", in.body.read.flags);
243 			break;
244 		case FUSE_READDIR:
245 			printf(" fh=%#" PRIx64 " offset=%" PRIu64 " size=%u",
246 				in.body.readdir.fh, in.body.readdir.offset,
247 				in.body.readdir.size);
248 			break;
249 		case FUSE_RELEASE:
250 			printf(" fh=%#" PRIx64 " flags=%#x lock_owner=%" PRIu64,
251 				in.body.release.fh,
252 				in.body.release.flags,
253 				in.body.release.lock_owner);
254 			break;
255 		case FUSE_SETATTR:
256 			if (verbosity <= 1) {
257 				printf(" valid=%#x", in.body.setattr.valid);
258 				break;
259 			}
260 			if (in.body.setattr.valid & FATTR_MODE)
261 				printf(" mode=%#o", in.body.setattr.mode);
262 			if (in.body.setattr.valid & FATTR_UID)
263 				printf(" uid=%u", in.body.setattr.uid);
264 			if (in.body.setattr.valid & FATTR_GID)
265 				printf(" gid=%u", in.body.setattr.gid);
266 			if (in.body.setattr.valid & FATTR_SIZE)
267 				printf(" size=%" PRIu64, in.body.setattr.size);
268 			if (in.body.setattr.valid & FATTR_ATIME)
269 				printf(" atime=%" PRIu64 ".%u",
270 					in.body.setattr.atime,
271 					in.body.setattr.atimensec);
272 			if (in.body.setattr.valid & FATTR_MTIME)
273 				printf(" mtime=%" PRIu64 ".%u",
274 					in.body.setattr.mtime,
275 					in.body.setattr.mtimensec);
276 			if (in.body.setattr.valid & FATTR_FH)
277 				printf(" fh=%" PRIu64 "", in.body.setattr.fh);
278 			break;
279 		case FUSE_SETLK:
280 			printf(" fh=%#" PRIx64 " owner=%" PRIu64
281 				" type=%u pid=%u",
282 				in.body.setlk.fh, in.body.setlk.owner,
283 				in.body.setlk.lk.type,
284 				in.body.setlk.lk.pid);
285 			if (verbosity >= 2) {
286 				printf(" range=[%" PRIu64 "-%" PRIu64 "]",
287 					in.body.setlk.lk.start,
288 					in.body.setlk.lk.end);
289 			}
290 			break;
291 		case FUSE_SETXATTR:
292 			/*
293 			 * In theory neither the xattr name and value need be
294 			 * ASCII, but in this test suite they always are.
295 			 */
296 			name = (const char*)in.body.bytes +
297 				sizeof(fuse_setxattr_in);
298 			value = name + strlen(name) + 1;
299 			printf(" %s=%s", name, value);
300 			break;
301 		case FUSE_WRITE:
302 			printf(" fh=%#" PRIx64 " offset=%" PRIu64
303 				" size=%u write_flags=%u",
304 				in.body.write.fh,
305 				in.body.write.offset, in.body.write.size,
306 				in.body.write.write_flags);
307 			if (verbosity > 1)
308 				printf(" flags=%#x", in.body.write.flags);
309 			break;
310 		default:
311 			break;
312 	}
313 	printf("\n");
314 }
315 
316 /*
317  * Debug a FUSE response.
318  *
319  * This is mostly useful for asynchronous notifications, which don't correspond
320  * to any request
321  */
322 void MockFS::debug_response(const mockfs_buf_out &out) {
323 	const char *name;
324 
325 	if (verbosity == 0)
326 		return;
327 
328 	switch (out.header.error) {
329 		case FUSE_NOTIFY_INVAL_ENTRY:
330 			name = (const char*)out.body.bytes +
331 				sizeof(fuse_notify_inval_entry_out);
332 			printf("<- INVAL_ENTRY parent=%" PRIu64 " %s\n",
333 				out.body.inval_entry.parent, name);
334 			break;
335 		case FUSE_NOTIFY_INVAL_INODE:
336 			printf("<- INVAL_INODE ino=%" PRIu64 " off=%" PRIi64
337 				" len=%" PRIi64 "\n",
338 				out.body.inval_inode.ino,
339 				out.body.inval_inode.off,
340 				out.body.inval_inode.len);
341 			break;
342 		case FUSE_NOTIFY_STORE:
343 			printf("<- STORE ino=%" PRIu64 " off=%" PRIu64
344 				" size=%" PRIu32 "\n",
345 				out.body.store.nodeid,
346 				out.body.store.offset,
347 				out.body.store.size);
348 			break;
349 		default:
350 			break;
351 	}
352 }
353 
354 MockFS::MockFS(int max_readahead, bool allow_other, bool default_permissions,
355 	bool push_symlinks_in, bool ro, enum poll_method pm, uint32_t flags,
356 	uint32_t kernel_minor_version, uint32_t max_write, bool async,
357 	bool noclusterr, unsigned time_gran, bool nointr)
358 {
359 	struct sigaction sa;
360 	struct iovec *iov = NULL;
361 	int iovlen = 0;
362 	char fdstr[15];
363 	const bool trueval = true;
364 
365 	m_daemon_id = NULL;
366 	m_kernel_minor_version = kernel_minor_version;
367 	m_maxreadahead = max_readahead;
368 	m_maxwrite = max_write;
369 	m_nready = -1;
370 	m_pm = pm;
371 	m_time_gran = time_gran;
372 	m_quit = false;
373 	if (m_pm == KQ)
374 		m_kq = kqueue();
375 	else
376 		m_kq = -1;
377 
378 	/*
379 	 * Kyua sets pwd to a testcase-unique tempdir; no need to use
380 	 * mkdtemp
381 	 */
382 	/*
383 	 * googletest doesn't allow ASSERT_ in constructors, so we must throw
384 	 * instead.
385 	 */
386 	if (mkdir("mountpoint" , 0755) && errno != EEXIST)
387 		throw(std::system_error(errno, std::system_category(),
388 			"Couldn't make mountpoint directory"));
389 
390 	switch (m_pm) {
391 	case BLOCKING:
392 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR);
393 		break;
394 	default:
395 		m_fuse_fd = open("/dev/fuse", O_CLOEXEC | O_RDWR | O_NONBLOCK);
396 		break;
397 	}
398 	if (m_fuse_fd < 0)
399 		throw(std::system_error(errno, std::system_category(),
400 			"Couldn't open /dev/fuse"));
401 
402 	m_pid = getpid();
403 	m_child_pid = -1;
404 
405 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
406 	build_iovec(&iov, &iovlen, "fspath",
407 		    __DECONST(void *, "mountpoint"), -1);
408 	build_iovec(&iov, &iovlen, "from", __DECONST(void *, "/dev/fuse"), -1);
409 	sprintf(fdstr, "%d", m_fuse_fd);
410 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
411 	if (allow_other) {
412 		build_iovec(&iov, &iovlen, "allow_other",
413 			__DECONST(void*, &trueval), sizeof(bool));
414 	}
415 	if (default_permissions) {
416 		build_iovec(&iov, &iovlen, "default_permissions",
417 			__DECONST(void*, &trueval), sizeof(bool));
418 	}
419 	if (push_symlinks_in) {
420 		build_iovec(&iov, &iovlen, "push_symlinks_in",
421 			__DECONST(void*, &trueval), sizeof(bool));
422 	}
423 	if (ro) {
424 		build_iovec(&iov, &iovlen, "ro",
425 			__DECONST(void*, &trueval), sizeof(bool));
426 	}
427 	if (async) {
428 		build_iovec(&iov, &iovlen, "async", __DECONST(void*, &trueval),
429 			sizeof(bool));
430 	}
431 	if (noclusterr) {
432 		build_iovec(&iov, &iovlen, "noclusterr",
433 			__DECONST(void*, &trueval), sizeof(bool));
434 	}
435 	if (nointr) {
436 		build_iovec(&iov, &iovlen, "nointr",
437 			__DECONST(void*, &trueval), sizeof(bool));
438 	} else {
439 		build_iovec(&iov, &iovlen, "intr",
440 			__DECONST(void*, &trueval), sizeof(bool));
441 	}
442 	if (nmount(iov, iovlen, 0))
443 		throw(std::system_error(errno, std::system_category(),
444 			"Couldn't mount filesystem"));
445 
446 	// Setup default handler
447 	ON_CALL(*this, process(_, _))
448 		.WillByDefault(Invoke(this, &MockFS::process_default));
449 
450 	init(flags);
451 	bzero(&sa, sizeof(sa));
452 	sa.sa_handler = sigint_handler;
453 	sa.sa_flags = 0;	/* Don't set SA_RESTART! */
454 	if (0 != sigaction(SIGUSR1, &sa, NULL))
455 		throw(std::system_error(errno, std::system_category(),
456 			"Couldn't handle SIGUSR1"));
457 	if (pthread_create(&m_daemon_id, NULL, service, (void*)this))
458 		throw(std::system_error(errno, std::system_category(),
459 			"Couldn't Couldn't start fuse thread"));
460 }
461 
462 MockFS::~MockFS() {
463 	kill_daemon();
464 	if (m_daemon_id != NULL) {
465 		pthread_join(m_daemon_id, NULL);
466 		m_daemon_id = NULL;
467 	}
468 	::unmount("mountpoint", MNT_FORCE);
469 	rmdir("mountpoint");
470 	if (m_kq >= 0)
471 		close(m_kq);
472 }
473 
474 void MockFS::audit_request(const mockfs_buf_in &in, ssize_t buflen) {
475 	uint32_t inlen = in.header.len;
476 	size_t fih = sizeof(in.header);
477 	switch (in.header.opcode) {
478 	case FUSE_LOOKUP:
479 	case FUSE_RMDIR:
480 	case FUSE_SYMLINK:
481 	case FUSE_UNLINK:
482 		EXPECT_GT(inlen, fih) << "Missing request filename";
483 		// No redundant information for checking buflen
484 		break;
485 	case FUSE_FORGET:
486 		EXPECT_EQ(inlen, fih + sizeof(in.body.forget));
487 		EXPECT_EQ((size_t)buflen, inlen);
488 		break;
489 	case FUSE_GETATTR:
490 		EXPECT_EQ(inlen, fih + sizeof(in.body.getattr));
491 		EXPECT_EQ((size_t)buflen, inlen);
492 		break;
493 	case FUSE_SETATTR:
494 		EXPECT_EQ(inlen, fih + sizeof(in.body.setattr));
495 		EXPECT_EQ((size_t)buflen, inlen);
496 		break;
497 	case FUSE_READLINK:
498 		EXPECT_EQ(inlen, fih) << "Unexpected request body";
499 		EXPECT_EQ((size_t)buflen, inlen);
500 		break;
501 	case FUSE_MKNOD:
502 		{
503 			size_t s;
504 			if (m_kernel_minor_version >= 12)
505 				s = sizeof(in.body.mknod);
506 			else
507 				s = FUSE_COMPAT_MKNOD_IN_SIZE;
508 			EXPECT_GE(inlen, fih + s) << "Missing request body";
509 			EXPECT_GT(inlen, fih + s) << "Missing request filename";
510 			// No redundant information for checking buflen
511 			break;
512 		}
513 	case FUSE_MKDIR:
514 		EXPECT_GE(inlen, fih + sizeof(in.body.mkdir)) <<
515 			"Missing request body";
516 		EXPECT_GT(inlen, fih + sizeof(in.body.mkdir)) <<
517 			"Missing request filename";
518 		// No redundant information for checking buflen
519 		break;
520 	case FUSE_RENAME:
521 		EXPECT_GE(inlen, fih + sizeof(in.body.rename)) <<
522 			"Missing request body";
523 		EXPECT_GT(inlen, fih + sizeof(in.body.rename)) <<
524 			"Missing request filename";
525 		// No redundant information for checking buflen
526 		break;
527 	case FUSE_LINK:
528 		EXPECT_GE(inlen, fih + sizeof(in.body.link)) <<
529 			"Missing request body";
530 		EXPECT_GT(inlen, fih + sizeof(in.body.link)) <<
531 			"Missing request filename";
532 		// No redundant information for checking buflen
533 		break;
534 	case FUSE_OPEN:
535 		EXPECT_EQ(inlen, fih + sizeof(in.body.open));
536 		EXPECT_EQ((size_t)buflen, inlen);
537 		break;
538 	case FUSE_READ:
539 		EXPECT_EQ(inlen, fih + sizeof(in.body.read));
540 		EXPECT_EQ((size_t)buflen, inlen);
541 		break;
542 	case FUSE_WRITE:
543 		{
544 			size_t s;
545 
546 			if (m_kernel_minor_version >= 9)
547 				s = sizeof(in.body.write);
548 			else
549 				s = FUSE_COMPAT_WRITE_IN_SIZE;
550 			// I suppose a 0-byte write should be allowed
551 			EXPECT_GE(inlen, fih + s) << "Missing request body";
552 			EXPECT_EQ((size_t)buflen, fih + s + in.body.write.size);
553 			break;
554 		}
555 	case FUSE_DESTROY:
556 	case FUSE_STATFS:
557 		EXPECT_EQ(inlen, fih);
558 		EXPECT_EQ((size_t)buflen, inlen);
559 		break;
560 	case FUSE_RELEASE:
561 		EXPECT_EQ(inlen, fih + sizeof(in.body.release));
562 		EXPECT_EQ((size_t)buflen, inlen);
563 		break;
564 	case FUSE_FSYNC:
565 	case FUSE_FSYNCDIR:
566 		EXPECT_EQ(inlen, fih + sizeof(in.body.fsync));
567 		EXPECT_EQ((size_t)buflen, inlen);
568 		break;
569 	case FUSE_SETXATTR:
570 		EXPECT_GE(inlen, fih + sizeof(in.body.setxattr)) <<
571 			"Missing request body";
572 		EXPECT_GT(inlen, fih + sizeof(in.body.setxattr)) <<
573 			"Missing request attribute name";
574 		// No redundant information for checking buflen
575 		break;
576 	case FUSE_GETXATTR:
577 		EXPECT_GE(inlen, fih + sizeof(in.body.getxattr)) <<
578 			"Missing request body";
579 		EXPECT_GT(inlen, fih + sizeof(in.body.getxattr)) <<
580 			"Missing request attribute name";
581 		// No redundant information for checking buflen
582 		break;
583 	case FUSE_LISTXATTR:
584 		EXPECT_EQ(inlen, fih + sizeof(in.body.listxattr));
585 		EXPECT_EQ((size_t)buflen, inlen);
586 		break;
587 	case FUSE_REMOVEXATTR:
588 		EXPECT_GT(inlen, fih) << "Missing request attribute name";
589 		// No redundant information for checking buflen
590 		break;
591 	case FUSE_FLUSH:
592 		EXPECT_EQ(inlen, fih + sizeof(in.body.flush));
593 		EXPECT_EQ((size_t)buflen, inlen);
594 		break;
595 	case FUSE_INIT:
596 		EXPECT_EQ(inlen, fih + sizeof(in.body.init));
597 		EXPECT_EQ((size_t)buflen, inlen);
598 		break;
599 	case FUSE_OPENDIR:
600 		EXPECT_EQ(inlen, fih + sizeof(in.body.opendir));
601 		EXPECT_EQ((size_t)buflen, inlen);
602 		break;
603 	case FUSE_READDIR:
604 		EXPECT_EQ(inlen, fih + sizeof(in.body.readdir));
605 		EXPECT_EQ((size_t)buflen, inlen);
606 		break;
607 	case FUSE_RELEASEDIR:
608 		EXPECT_EQ(inlen, fih + sizeof(in.body.releasedir));
609 		EXPECT_EQ((size_t)buflen, inlen);
610 		break;
611 	case FUSE_GETLK:
612 		EXPECT_EQ(inlen, fih + sizeof(in.body.getlk));
613 		EXPECT_EQ((size_t)buflen, inlen);
614 		break;
615 	case FUSE_SETLK:
616 	case FUSE_SETLKW:
617 		EXPECT_EQ(inlen, fih + sizeof(in.body.setlk));
618 		EXPECT_EQ((size_t)buflen, inlen);
619 		break;
620 	case FUSE_ACCESS:
621 		EXPECT_EQ(inlen, fih + sizeof(in.body.access));
622 		EXPECT_EQ((size_t)buflen, inlen);
623 		break;
624 	case FUSE_CREATE:
625 		EXPECT_GE(inlen, fih + sizeof(in.body.create)) <<
626 			"Missing request body";
627 		EXPECT_GT(inlen, fih + sizeof(in.body.create)) <<
628 			"Missing request filename";
629 		// No redundant information for checking buflen
630 		break;
631 	case FUSE_INTERRUPT:
632 		EXPECT_EQ(inlen, fih + sizeof(in.body.interrupt));
633 		EXPECT_EQ((size_t)buflen, inlen);
634 		break;
635 	case FUSE_BMAP:
636 		EXPECT_EQ(inlen, fih + sizeof(in.body.bmap));
637 		EXPECT_EQ((size_t)buflen, inlen);
638 		break;
639 	case FUSE_NOTIFY_REPLY:
640 	case FUSE_BATCH_FORGET:
641 	case FUSE_FALLOCATE:
642 	case FUSE_IOCTL:
643 	case FUSE_POLL:
644 	case FUSE_READDIRPLUS:
645 		FAIL() << "Unsupported opcode?";
646 	default:
647 		FAIL() << "Unknown opcode " << in.header.opcode;
648 	}
649 }
650 
651 void MockFS::init(uint32_t flags) {
652 	ssize_t buflen;
653 
654 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
655 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
656 
657 	read_request(*in, buflen);
658 	audit_request(*in, buflen);
659 	ASSERT_EQ(FUSE_INIT, in->header.opcode);
660 
661 	out->header.unique = in->header.unique;
662 	out->header.error = 0;
663 	out->body.init.major = FUSE_KERNEL_VERSION;
664 	out->body.init.minor = m_kernel_minor_version;;
665 	out->body.init.flags = in->body.init.flags & flags;
666 	out->body.init.max_write = m_maxwrite;
667 	out->body.init.max_readahead = m_maxreadahead;
668 
669 	if (m_kernel_minor_version < 23) {
670 		SET_OUT_HEADER_LEN(*out, init_7_22);
671 	} else {
672 		out->body.init.time_gran = m_time_gran;
673 		SET_OUT_HEADER_LEN(*out, init);
674 	}
675 
676 	write(m_fuse_fd, out.get(), out->header.len);
677 }
678 
679 void MockFS::kill_daemon() {
680 	m_quit = true;
681 	if (m_daemon_id != NULL)
682 		pthread_kill(m_daemon_id, SIGUSR1);
683 	// Closing the /dev/fuse file descriptor first allows unmount to
684 	// succeed even if the daemon doesn't correctly respond to commands
685 	// during the unmount sequence.
686 	close(m_fuse_fd);
687 	m_fuse_fd = -1;
688 }
689 
690 void MockFS::loop() {
691 	std::vector<std::unique_ptr<mockfs_buf_out>> out;
692 
693 	std::unique_ptr<mockfs_buf_in> in(new mockfs_buf_in);
694 	ASSERT_TRUE(in != NULL);
695 	while (!m_quit) {
696 		ssize_t buflen;
697 
698 		bzero(in.get(), sizeof(*in));
699 		read_request(*in, buflen);
700 		if (m_quit)
701 			break;
702 		if (verbosity > 0)
703 			debug_request(*in, buflen);
704 		audit_request(*in, buflen);
705 		if (pid_ok((pid_t)in->header.pid)) {
706 			process(*in, out);
707 		} else {
708 			/*
709 			 * Reject any requests from unknown processes.  Because
710 			 * we actually do mount a filesystem, plenty of
711 			 * unrelated system daemons may try to access it.
712 			 */
713 			if (verbosity > 1)
714 				printf("\tREJECTED (wrong pid %d)\n",
715 					in->header.pid);
716 			process_default(*in, out);
717 		}
718 		for (auto &it: out)
719 			write_response(*it);
720 		out.clear();
721 	}
722 }
723 
724 int MockFS::notify_inval_entry(ino_t parent, const char *name, size_t namelen)
725 {
726 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
727 
728 	out->header.unique = 0;	/* 0 means asynchronous notification */
729 	out->header.error = FUSE_NOTIFY_INVAL_ENTRY;
730 	out->body.inval_entry.parent = parent;
731 	out->body.inval_entry.namelen = namelen;
732 	strlcpy((char*)&out->body.bytes + sizeof(out->body.inval_entry),
733 		name, sizeof(out->body.bytes) - sizeof(out->body.inval_entry));
734 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_entry) +
735 		namelen;
736 	debug_response(*out);
737 	write_response(*out);
738 	return 0;
739 }
740 
741 int MockFS::notify_inval_inode(ino_t ino, off_t off, ssize_t len)
742 {
743 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
744 
745 	out->header.unique = 0;	/* 0 means asynchronous notification */
746 	out->header.error = FUSE_NOTIFY_INVAL_INODE;
747 	out->body.inval_inode.ino = ino;
748 	out->body.inval_inode.off = off;
749 	out->body.inval_inode.len = len;
750 	out->header.len = sizeof(out->header) + sizeof(out->body.inval_inode);
751 	debug_response(*out);
752 	write_response(*out);
753 	return 0;
754 }
755 
756 int MockFS::notify_store(ino_t ino, off_t off, const void* data, ssize_t size)
757 {
758 	std::unique_ptr<mockfs_buf_out> out(new mockfs_buf_out);
759 
760 	out->header.unique = 0;	/* 0 means asynchronous notification */
761 	out->header.error = FUSE_NOTIFY_STORE;
762 	out->body.store.nodeid = ino;
763 	out->body.store.offset = off;
764 	out->body.store.size = size;
765 	bcopy(data, (char*)&out->body.bytes + sizeof(out->body.store), size);
766 	out->header.len = sizeof(out->header) + sizeof(out->body.store) + size;
767 	debug_response(*out);
768 	write_response(*out);
769 	return 0;
770 }
771 
772 bool MockFS::pid_ok(pid_t pid) {
773 	if (pid == m_pid) {
774 		return (true);
775 	} else if (pid == m_child_pid) {
776 		return (true);
777 	} else {
778 		struct kinfo_proc *ki;
779 		bool ok = false;
780 
781 		ki = kinfo_getproc(pid);
782 		if (ki == NULL)
783 			return (false);
784 		/*
785 		 * Allow access by the aio daemon processes so that our tests
786 		 * can use aio functions
787 		 */
788 		if (0 == strncmp("aiod", ki->ki_comm, 4))
789 			ok = true;
790 		free(ki);
791 		return (ok);
792 	}
793 }
794 
795 void MockFS::process_default(const mockfs_buf_in& in,
796 		std::vector<std::unique_ptr<mockfs_buf_out>> &out)
797 {
798 	std::unique_ptr<mockfs_buf_out> out0(new mockfs_buf_out);
799 	out0->header.unique = in.header.unique;
800 	out0->header.error = -EOPNOTSUPP;
801 	out0->header.len = sizeof(out0->header);
802 	out.push_back(std::move(out0));
803 }
804 
805 void MockFS::read_request(mockfs_buf_in &in, ssize_t &res) {
806 	int nready = 0;
807 	fd_set readfds;
808 	pollfd fds[1];
809 	struct kevent changes[1];
810 	struct kevent events[1];
811 	struct timespec timeout_ts;
812 	struct timeval timeout_tv;
813 	const int timeout_ms = 999;
814 	int timeout_int, nfds;
815 
816 	switch (m_pm) {
817 	case BLOCKING:
818 		break;
819 	case KQ:
820 		timeout_ts.tv_sec = 0;
821 		timeout_ts.tv_nsec = timeout_ms * 1'000'000;
822 		while (nready == 0) {
823 			EV_SET(&changes[0], m_fuse_fd, EVFILT_READ, EV_ADD, 0,
824 				0, 0);
825 			nready = kevent(m_kq, &changes[0], 1, &events[0], 1,
826 				&timeout_ts);
827 			if (m_quit)
828 				return;
829 		}
830 		ASSERT_LE(0, nready) << strerror(errno);
831 		ASSERT_EQ(events[0].ident, (uintptr_t)m_fuse_fd);
832 		if (events[0].flags & EV_ERROR)
833 			FAIL() << strerror(events[0].data);
834 		else if (events[0].flags & EV_EOF)
835 			FAIL() << strerror(events[0].fflags);
836 		m_nready = events[0].data;
837 		break;
838 	case POLL:
839 		timeout_int = timeout_ms;
840 		fds[0].fd = m_fuse_fd;
841 		fds[0].events = POLLIN;
842 		while (nready == 0) {
843 			nready = poll(fds, 1, timeout_int);
844 			if (m_quit)
845 				return;
846 		}
847 		ASSERT_LE(0, nready) << strerror(errno);
848 		ASSERT_TRUE(fds[0].revents & POLLIN);
849 		break;
850 	case SELECT:
851 		timeout_tv.tv_sec = 0;
852 		timeout_tv.tv_usec = timeout_ms * 1'000;
853 		nfds = m_fuse_fd + 1;
854 		while (nready == 0) {
855 			FD_ZERO(&readfds);
856 			FD_SET(m_fuse_fd, &readfds);
857 			nready = select(nfds, &readfds, NULL, NULL,
858 				&timeout_tv);
859 			if (m_quit)
860 				return;
861 		}
862 		ASSERT_LE(0, nready) << strerror(errno);
863 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &readfds));
864 		break;
865 	default:
866 		FAIL() << "not yet implemented";
867 	}
868 	res = read(m_fuse_fd, &in, sizeof(in));
869 
870 	if (res < 0 && !m_quit) {
871 		m_quit = true;
872 		FAIL() << "read: " << strerror(errno);
873 	}
874 	ASSERT_TRUE(res >= static_cast<ssize_t>(sizeof(in.header)) || m_quit);
875 	/*
876 	 * Inconsistently, fuse_in_header.len is the size of the entire
877 	 * request,including header, even though fuse_out_header.len excludes
878 	 * the size of the header.
879 	 */
880 	ASSERT_TRUE(res == static_cast<ssize_t>(in.header.len) || m_quit);
881 }
882 
883 void MockFS::write_response(const mockfs_buf_out &out) {
884 	fd_set writefds;
885 	pollfd fds[1];
886 	int nready, nfds;
887 	ssize_t r;
888 
889 	switch (m_pm) {
890 	case BLOCKING:
891 	case KQ:	/* EVFILT_WRITE is not supported */
892 		break;
893 	case POLL:
894 		fds[0].fd = m_fuse_fd;
895 		fds[0].events = POLLOUT;
896 		nready = poll(fds, 1, INFTIM);
897 		ASSERT_LE(0, nready) << strerror(errno);
898 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
899 		ASSERT_TRUE(fds[0].revents & POLLOUT);
900 		break;
901 	case SELECT:
902 		FD_ZERO(&writefds);
903 		FD_SET(m_fuse_fd, &writefds);
904 		nfds = m_fuse_fd + 1;
905 		nready = select(nfds, NULL, &writefds, NULL, NULL);
906 		ASSERT_LE(0, nready) << strerror(errno);
907 		ASSERT_EQ(1, nready) << "NULL timeout expired?";
908 		ASSERT_TRUE(FD_ISSET(m_fuse_fd, &writefds));
909 		break;
910 	default:
911 		FAIL() << "not yet implemented";
912 	}
913 	r = write(m_fuse_fd, &out, out.header.len);
914 	ASSERT_TRUE(r > 0 || errno == EAGAIN) << strerror(errno);
915 }
916 
917 void* MockFS::service(void *pthr_data) {
918 	MockFS *mock_fs = (MockFS*)pthr_data;
919 
920 	mock_fs->loop();
921 
922 	return (NULL);
923 }
924 
925 void MockFS::unmount() {
926 	::unmount("mountpoint", 0);
927 }
928