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