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